diff --git a/acceptance/repository-cloudant/docker.setup.js b/acceptance/repository-cloudant/docker.setup.js index 9fb1453a331a..c985c0149d2f 100644 --- a/acceptance/repository-cloudant/docker.setup.js +++ b/acceptance/repository-cloudant/docker.setup.js @@ -43,7 +43,7 @@ async function createDB() { // retry if socket hangs up too early try { dbRequest(opts); - } catch (error) { + } catch { dbRequest(opts); } return new Promise(() => { diff --git a/acceptance/repository-cloudant/package.json b/acceptance/repository-cloudant/package.json index 8a570c854573..b16b767d744b 100644 --- a/acceptance/repository-cloudant/package.json +++ b/acceptance/repository-cloudant/package.json @@ -45,6 +45,6 @@ "loopback-connector-cloudant": "2.5.0", "ms": "2.1.3", "tslib": "^2.8.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/benchmark/src/__tests__/benchmark.integration.ts b/benchmark/src/__tests__/benchmark.integration.ts index aa057bec4ec5..8915042b1c45 100644 --- a/benchmark/src/__tests__/benchmark.integration.ts +++ b/benchmark/src/__tests__/benchmark.integration.ts @@ -39,7 +39,7 @@ describe('Benchmark (SLOW)', function (this: Mocha.Suite) { urlPath: string, options?: Omit, ): Promise { - if (!options) options = {}; + options ??= {}; const requestOptions: AxiosRequestConfig = { url: this.buildUrl(urlPath), diff --git a/benchmark/src/rest-routing/routing-table.ts b/benchmark/src/rest-routing/routing-table.ts index d5f46b3edf41..631b5da0177a 100644 --- a/benchmark/src/rest-routing/routing-table.ts +++ b/benchmark/src/rest-routing/routing-table.ts @@ -99,7 +99,7 @@ function givenRouter(router: RestRouter, spec: OpenApiSpec, count: number) { try { table.find(request); found++; - } catch (e) { + } catch { missed++; } } diff --git a/bin/sync-dev-deps.js b/bin/sync-dev-deps.js index 7632ec95ce17..38821565cc8c 100755 --- a/bin/sync-dev-deps.js +++ b/bin/sync-dev-deps.js @@ -12,18 +12,20 @@ const path = require('node:path'); const fse = require('fs-extra'); -const { - loadLernaRepo, - writeJsonSync, - isDryRun, - printJson, - runMain, -} = require('./script-util'); +const pkgJson = require('@npmcli/package-json'); +const mapWorkspaces = require('@npmcli/map-workspaces'); +const {writeJsonSync, isDryRun, printJson, runMain} = require('./script-util'); async function syncDevDeps(options) { - const {project, packages} = await loadLernaRepo(); + const rootPath = process.cwd(); - const rootPath = project.rootPath; + const {content: rootPkg} = await pkgJson.load(rootPath); + const workspaces = await mapWorkspaces({cwd: rootPath, pkg: rootPkg}); + + const packages = Array.from(workspaces, ([name, location]) => ({ + name, + manifestLocation: path.join(location, 'package.json'), + })); // Load dependencies from `packages/eslint-config/package.json` const eslintDeps = require( diff --git a/bodyparsers/rest-msgpack/package.json b/bodyparsers/rest-msgpack/package.json index cf0403751e94..7de9d875e449 100644 --- a/bodyparsers/rest-msgpack/package.json +++ b/bodyparsers/rest-msgpack/package.json @@ -48,6 +48,6 @@ "@types/msgpack5": "^3.4.7", "@types/node": "^20.19.43", "@types/type-is": "^1.6.7", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/access-control-migration/package.json b/examples/access-control-migration/package.json index 5df8db41f5da..eefda068d159 100644 --- a/examples/access-control-migration/package.json +++ b/examples/access-control-migration/package.json @@ -75,6 +75,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/binding-resolution/package.json b/examples/binding-resolution/package.json index 04b5cc517f66..5be9d3b2b481 100644 --- a/examples/binding-resolution/package.json +++ b/examples/binding-resolution/package.json @@ -58,6 +58,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/context/package.json b/examples/context/package.json index 2897c225412b..1f447c95d429 100644 --- a/examples/context/package.json +++ b/examples/context/package.json @@ -55,6 +55,6 @@ "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/context/src/custom-configuration-resolver.ts b/examples/context/src/custom-configuration-resolver.ts index 4d7d536f122c..19f15f124ccd 100644 --- a/examples/context/src/custom-configuration-resolver.ts +++ b/examples/context/src/custom-configuration-resolver.ts @@ -47,7 +47,7 @@ class EnvConfigResolver ); try { return JSON.parse(val); - } catch (err) { + } catch { return val; } } diff --git a/examples/express-composition/package.json b/examples/express-composition/package.json index 329851777c4a..ebd4ebe2fa00 100644 --- a/examples/express-composition/package.json +++ b/examples/express-composition/package.json @@ -63,6 +63,6 @@ "@types/express": "^4.17.25", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/file-transfer/package.json b/examples/file-transfer/package.json index 5167fd6e0007..697d335175f4 100644 --- a/examples/file-transfer/package.json +++ b/examples/file-transfer/package.json @@ -61,6 +61,6 @@ "@types/multer": "^2.2.0", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/file-transfer/src/services/file-upload.service.ts b/examples/file-transfer/src/services/file-upload.service.ts index 897a5c3d3586..facedb8171df 100644 --- a/examples/file-transfer/src/services/file-upload.service.ts +++ b/examples/file-transfer/src/services/file-upload.service.ts @@ -23,10 +23,8 @@ import {FileUploadHandler} from '../types'; }) export class FileUploadProvider implements Provider { constructor(@config() private options: multer.Options = {}) { - if (!this.options.storage) { - // Default to in-memory storage - this.options.storage = multer.memoryStorage(); - } + // Default to in-memory storage + this.options.storage ??= multer.memoryStorage(); } value(): FileUploadHandler { diff --git a/examples/graphql/package.json b/examples/graphql/package.json index 20fa2edb50f2..9cb1d63ce813 100644 --- a/examples/graphql/package.json +++ b/examples/graphql/package.json @@ -68,6 +68,6 @@ "graphql-ws": "^5.16.2", "rimraf": "^5.0.10", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/greeter-extension/package.json b/examples/greeter-extension/package.json index 9f97ca20362f..681708c0749e 100644 --- a/examples/greeter-extension/package.json +++ b/examples/greeter-extension/package.json @@ -59,6 +59,6 @@ "@types/debug": "4.1.13", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/greeting-app/package.json b/examples/greeting-app/package.json index 5de422b724cc..c3b1f71cc429 100644 --- a/examples/greeting-app/package.json +++ b/examples/greeting-app/package.json @@ -63,6 +63,6 @@ "@types/debug": "4.1.13", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index fd3c480e4b73..33072c0b350d 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -56,6 +56,6 @@ "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/lb3-application/package.json b/examples/lb3-application/package.json index 1cc3e52cacc8..c2f7320fe80d 100644 --- a/examples/lb3-application/package.json +++ b/examples/lb3-application/package.json @@ -71,6 +71,6 @@ "eslint": "^8.57.1", "lodash": "^4.18.1", "should": "^13.2.3", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/log-extension/package.json b/examples/log-extension/package.json index 42d3495107e8..32c6841aab2f 100644 --- a/examples/log-extension/package.json +++ b/examples/log-extension/package.json @@ -58,6 +58,6 @@ "@types/debug": "^4.1.13", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/log-extension/src/decorators/log.decorator.ts b/examples/log-extension/src/decorators/log.decorator.ts index 615f4f92900e..aeb29f37bcc5 100644 --- a/examples/log-extension/src/decorators/log.decorator.ts +++ b/examples/log-extension/src/decorators/log.decorator.ts @@ -19,7 +19,7 @@ import {LevelMetadata} from '../types'; * @param level - The Log Level at or above it should log */ export function log(level?: number) { - if (level === undefined) level = LOG_LEVEL.WARN; + level ??= LOG_LEVEL.WARN; return MethodDecoratorFactory.createDecorator( EXAMPLE_LOG_METADATA_KEY, { diff --git a/examples/metrics-prometheus/package.json b/examples/metrics-prometheus/package.json index f1b7b17f8b36..513c600e7f81 100644 --- a/examples/metrics-prometheus/package.json +++ b/examples/metrics-prometheus/package.json @@ -59,6 +59,6 @@ "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/multi-tenancy/package.json b/examples/multi-tenancy/package.json index c822b56bde0b..1007daa7af2a 100644 --- a/examples/multi-tenancy/package.json +++ b/examples/multi-tenancy/package.json @@ -73,6 +73,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/passport-login/package.json b/examples/passport-login/package.json index 87b4b26357d7..4f4f45da2cf8 100644 --- a/examples/passport-login/package.json +++ b/examples/passport-login/package.json @@ -97,6 +97,6 @@ "axios": "^1.16.0", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/passport-login/src/authentication-strategies/basic.ts b/examples/passport-login/src/authentication-strategies/basic.ts index bf4fd64efbae..377adcb74660 100644 --- a/examples/passport-login/src/authentication-strategies/basic.ts +++ b/examples/passport-login/src/authentication-strategies/basic.ts @@ -91,7 +91,7 @@ export class BasicStrategy implements AuthenticationStrategy { return done(null, false); } const user = users[0]; - if (!user.credentials || user.credentials.password !== password) { + if (user.credentials?.password !== password) { return done(null, false); } // Authentication passed, return user profile diff --git a/examples/passport-login/src/authentication-strategies/local.ts b/examples/passport-login/src/authentication-strategies/local.ts index 8b907e72ca7d..335a8c4cd009 100644 --- a/examples/passport-login/src/authentication-strategies/local.ts +++ b/examples/passport-login/src/authentication-strategies/local.ts @@ -87,7 +87,7 @@ export class LocalAuthStrategy implements AuthenticationStrategy { return done(null, null, {message: AUTH_FAILED_MESSAGE}); } const user = users[0]; - if (!user.credentials || user.credentials.password !== password) { + if (user.credentials?.password !== password) { return done(null, null, {message: AUTH_FAILED_MESSAGE}); } // Authentication passed, return user profile diff --git a/examples/references-many/package.json b/examples/references-many/package.json index 32b5829283f7..8f08fc32a048 100644 --- a/examples/references-many/package.json +++ b/examples/references-many/package.json @@ -71,6 +71,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/rest-crud/package.json b/examples/rest-crud/package.json index 126fd42c3a87..15076f7d2ca8 100644 --- a/examples/rest-crud/package.json +++ b/examples/rest-crud/package.json @@ -68,6 +68,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/rpc-server/package.json b/examples/rpc-server/package.json index 1adf521ce496..d4e7ebb1b07a 100644 --- a/examples/rpc-server/package.json +++ b/examples/rpc-server/package.json @@ -53,6 +53,6 @@ "@types/express": "^4.17.25", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/rpc-server/src/application.ts b/examples/rpc-server/src/application.ts index 538bf34bbfaf..70483a1ead81 100644 --- a/examples/rpc-server/src/application.ts +++ b/examples/rpc-server/src/application.ts @@ -16,7 +16,7 @@ export class MyApplication extends Application { super(options); this.controller(GreetController); this.server(RPCServer); - this.options.port = this.options.port || 3000; + this.options.port = this.options.port ?? 3000; this.bind('rpcServer.config').to(this.options); } } diff --git a/examples/rpc-server/src/rpc.router.ts b/examples/rpc-server/src/rpc.router.ts index 9a70970b32eb..62084595ff98 100644 --- a/examples/rpc-server/src/rpc.router.ts +++ b/examples/rpc-server/src/rpc.router.ts @@ -9,8 +9,8 @@ import {RPCServer} from './rpc.server'; export function rpcRouter(server: RPCServer) { const jsonParser = parser.json(); - server.expressServer.post('*', jsonParser, async (request, response) => { - await routeHandler(server, request, response); + server.expressServer.post('*', jsonParser, (request, response, next) => { + routeHandler(server, request, response).catch(next); }); } diff --git a/examples/soap-calculator/package.json b/examples/soap-calculator/package.json index 56923f3824a1..c25a1567e848 100644 --- a/examples/soap-calculator/package.json +++ b/examples/soap-calculator/package.json @@ -63,6 +63,6 @@ "eslint": "^8.57.1", "mocha": "^12.0.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/socketio/package.json b/examples/socketio/package.json index f2e084d1a59c..7f0e814cb478 100644 --- a/examples/socketio/package.json +++ b/examples/socketio/package.json @@ -69,6 +69,6 @@ "eslint": "^8.57.1", "socket.io-client": "^4.8.3", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/todo-jwt/package.json b/examples/todo-jwt/package.json index 3e83d63ed270..c603dab4b658 100644 --- a/examples/todo-jwt/package.json +++ b/examples/todo-jwt/package.json @@ -75,6 +75,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/todo-list/package.json b/examples/todo-list/package.json index 591f0bd97559..0b7eaf61e5a1 100644 --- a/examples/todo-list/package.json +++ b/examples/todo-list/package.json @@ -71,6 +71,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts b/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts index 4d36bef46c0d..234a880b9847 100644 --- a/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts +++ b/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts @@ -31,6 +31,7 @@ describe('TodoListImageRepository', () => { testdb, async () => todoListRepo, ); + todoRepo = new TodoRepository(testdb, async () => todoListRepo); }); beforeEach(givenEmptyDatabase); diff --git a/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts b/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts index 716f5f78aba0..c9edcf6c356d 100644 --- a/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts +++ b/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts @@ -28,6 +28,10 @@ describe('TodoRepository', () => { async () => todoListImageRepo, ); todoRepo = new TodoRepository(testdb, async () => todoListRepo); + todoListImageRepo = new TodoListImageRepository( + testdb, + async () => todoListRepo, + ); }); beforeEach(givenEmptyDatabase); diff --git a/examples/todo/package.json b/examples/todo/package.json index b7382ec367bd..1f454e7165e8 100644 --- a/examples/todo/package.json +++ b/examples/todo/package.json @@ -71,6 +71,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/validation-app/package.json b/examples/validation-app/package.json index 7b3a2160f7de..d0e103a84d87 100644 --- a/examples/validation-app/package.json +++ b/examples/validation-app/package.json @@ -66,6 +66,6 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/examples/webpack/package.json b/examples/webpack/package.json index ad5163c7336e..aa9492a8c9e9 100644 --- a/examples/webpack/package.json +++ b/examples/webpack/package.json @@ -51,7 +51,7 @@ "events": "^3.3.0", "process": "^0.11.10", "puppeteer": "^25.10.0", - "typescript": "~5.2.2", + "typescript": "npm:@typescript/typescript6@~6.0.2", "util": "^0.12.5", "webpack": "^5.110.3", "webpack-cli": "^7.2.3" diff --git a/extensions/authentication-jwt/package.json b/extensions/authentication-jwt/package.json index fce2218d17f0..18333e910dac 100644 --- a/extensions/authentication-jwt/package.json +++ b/extensions/authentication-jwt/package.json @@ -62,6 +62,6 @@ "@types/lodash": "^4.17.25", "@types/node": "^20.19.43", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/extensions/authentication-jwt/src/services/refreshtoken.service.ts b/extensions/authentication-jwt/src/services/refreshtoken.service.ts index 9f65887db7ce..5ab5ef1643c0 100644 --- a/extensions/authentication-jwt/src/services/refreshtoken.service.ts +++ b/extensions/authentication-jwt/src/services/refreshtoken.service.ts @@ -95,7 +95,7 @@ export class RefreshtokenService { await this.refreshTokenRepository.delete( new RefreshToken({refreshToken: refreshToken}), ); - } catch (e) { + } catch { // ignore } } diff --git a/extensions/cron/src/types.ts b/extensions/cron/src/types.ts index 6af24423a758..a1720ab67002 100644 --- a/extensions/cron/src/types.ts +++ b/extensions/cron/src/types.ts @@ -52,10 +52,13 @@ export class CronJob extends BaseCronJob { } else { this.name = `job-${++CronJob.count}`; } - // Override `fireOnTick` to catch errors + // Override `fireOnTick` to catch errors. `cron` declares `fireOnTick` as a + // class field but installs it on the prototype, so it cannot be reached + // via `super`. + const fireOnTick = BaseCronJob.prototype.fireOnTick; this.fireOnTick = () => { try { - return super.fireOnTick(); + return fireOnTick.call(this); } catch (err) { this.emitter.emit('error', err); } diff --git a/extensions/graphql/src/graphql.container.ts b/extensions/graphql/src/graphql.container.ts index 3728aa21bf5b..206c3cb0c71d 100644 --- a/extensions/graphql/src/graphql.container.ts +++ b/extensions/graphql/src/graphql.container.ts @@ -84,9 +84,7 @@ export class LoopBackContainer implements ContainerType { } else { // Narrow down by key found = bindings.find(filterByKey(resolverBinding.key)); - if (!found) { - found = bindings[0]; - } + found ??= bindings[0]; } debug( diff --git a/extensions/graphql/src/graphql.server.ts b/extensions/graphql/src/graphql.server.ts index 84075b51fe08..7b402f1c005a 100644 --- a/extensions/graphql/src/graphql.server.ts +++ b/extensions/graphql/src/graphql.server.ts @@ -45,11 +45,10 @@ import { BuildSchemaOptions as TypeGrahpQLBuildSchemaOptions, buildSchema, } from 'type-graphql'; -import {Middleware} from 'type-graphql/build/typings/typings/middleware'; import {WebSocketServer} from 'ws'; import {LoopBackContainer} from './graphql.container'; import {GraphQLBindings, GraphQLTags} from './keys'; -import {GraphQLServerOptions} from './types'; +import {GraphQLMiddleware, GraphQLServerOptions} from './types'; const {json} = pkg; /** @@ -100,8 +99,8 @@ export class GraphQLServer extends Context implements Server { /** * Get a list of middleware */ - async getMiddlewareList(): Promise[]> { - const view = this.createView>( + async getMiddlewareList(): Promise[]> { + const view = this.createView>( filterByTag(GraphQLTags.MIDDLEWARE), ); return view.values(); @@ -111,8 +110,12 @@ export class GraphQLServer extends Context implements Server { * Register a GraphQL middleware * @param middleware - GraphQL middleware */ - middleware(middleware: Middleware): Binding> { - return this.bind>(BindingKey.generate(`graphql.middleware`)) + middleware( + middleware: GraphQLMiddleware, + ): Binding> { + return this.bind>( + BindingKey.generate(`graphql.middleware`), + ) .to(middleware) .tag(GraphQLTags.MIDDLEWARE); } diff --git a/extensions/graphql/src/keys.ts b/extensions/graphql/src/keys.ts index e11ade9a6123..0d1fd3c2dc65 100644 --- a/extensions/graphql/src/keys.ts +++ b/extensions/graphql/src/keys.ts @@ -37,7 +37,9 @@ export namespace GraphQLBindings { /** * Binding key for the GraphQL context resolver */ - export const GRAPHQL_CONTEXT_RESOLVER = BindingKey.create< + export const GRAPHQL_CONTEXT_RESOLVER: BindingKey< + ExpressMiddlewareOptions<{[key: string]: unknown}>['context'] + > = BindingKey.create< ExpressMiddlewareOptions<{[key: string]: unknown}>['context'] >('graphql.contextResolver'); diff --git a/extensions/graphql/src/types.ts b/extensions/graphql/src/types.ts index a15f26bae088..5ae70e491114 100644 --- a/extensions/graphql/src/types.ts +++ b/extensions/graphql/src/types.ts @@ -7,9 +7,21 @@ import {ApolloServerOptionsWithStaticSchema, BaseContext} from '@apollo/server'; import {HttpOptions} from '@loopback/http-server'; import {ExecutionArgs} from 'graphql'; import {GraphQLExecutionContextValue, SubscribeMessage} from 'graphql-ws'; +import {MiddlewareFn, MiddlewareInterface} from 'type-graphql'; export {Float, ID, Int, ResolverInterface} from 'type-graphql'; -export {Middleware as GraphQLMiddleware} from 'type-graphql/build/typings/typings/middleware'; + +/** + * A GraphQL middleware function or class. + * + * `type-graphql` declares this type as `Middleware` but does not export it from + * the package entry point, so it is declared here to match + * `type-graphql/build/typings/typings/middleware`. + */ +export type GraphQLMiddleware = + | MiddlewareFn + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | (new (...args: any[]) => MiddlewareInterface); /** * Options for GraphQL component diff --git a/extensions/pooling/package.json b/extensions/pooling/package.json index 588bc9ce43cf..017daff3be74 100644 --- a/extensions/pooling/package.json +++ b/extensions/pooling/package.json @@ -49,6 +49,6 @@ "@loopback/testlab": "^8.0.15", "@types/debug": "^4.1.13", "@types/node": "^20.19.43", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/extensions/sequelize/package.json b/extensions/sequelize/package.json index 30dca35b4963..ed3498f6661f 100644 --- a/extensions/sequelize/package.json +++ b/extensions/sequelize/package.json @@ -60,6 +60,6 @@ "pg": "^8.23.0", "pg-hstore": "^2.3.4", "sqlite3": "6.0.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/extensions/sequelize/src/sequelize/sequelize.repository.base.ts b/extensions/sequelize/src/sequelize/sequelize.repository.base.ts index 839672ed6b64..fbcc585f55af 100644 --- a/extensions/sequelize/src/sequelize/sequelize.repository.base.ts +++ b/extensions/sequelize/src/sequelize/sequelize.repository.base.ts @@ -550,9 +550,7 @@ export class SequelizeCrudRepository< return []; } - if (!sourceModel) { - sourceModel = this.sequelizeModel; - } + sourceModel ??= this.sequelizeModel; if (sourceModel === this.sequelizeModel) { const invalidInclusions = inclusionFilters.filter( @@ -663,8 +661,7 @@ export class SequelizeCrudRepository< } const entityClassCol = this.entityClass.definition.properties[columnName]; - const isBooleanColumn = - entityClassCol && entityClassCol.type === 'boolean'; + const isBooleanColumn = entityClassCol?.type === 'boolean'; if (isTruelyObject(conditionValue)) { sequelizeWhere[columnName] = {}; @@ -718,7 +715,7 @@ export class SequelizeCrudRepository< * Get Sequelize Model * @returns Sequelize Model Instance based on the definitions from `entityClass` */ - public getSequelizeModel(entityClass = this.entityClass) { + public getSequelizeModel(entityClass: typeof Entity = this.entityClass) { if (!this.dataSource.sequelize) { throw Error( `The datasource "${this.dataSource.name}" doesn't have sequelize instance bound to it.`, @@ -832,7 +829,7 @@ export class SequelizeCrudRepository< * @param {Entity} entityClass - The entity class for which the table name is being retrieved. * @returns {string} - The table name associated with the entity class. Which is used when performing the query. */ - getTableName(entityClass = this.entityClass) { + getTableName(entityClass: typeof Entity = this.entityClass) { let tableName = entityClass.name; // model class name if (entityClass.definition.name !== tableName) { @@ -934,7 +931,7 @@ export class SequelizeCrudRepository< const stringTypeArray = definition[propName].itemType === String || ['String', 'string'].includes( - definition[propName].itemType?.toString() || '', + definition[propName].itemType?.toString() ?? '', ); dataType = stringTypeArray ? DataTypes.ARRAY(DataTypes.STRING) @@ -1008,7 +1005,7 @@ export class SequelizeCrudRepository< if (typeof value === 'string') { try { return JSON.parse(value); - } catch (_error) { + } catch { return null; } } diff --git a/extensions/socketio/src/socketio.server.ts b/extensions/socketio/src/socketio.server.ts index db2e4398b788..16bcc425e188 100644 --- a/extensions/socketio/src/socketio.server.ts +++ b/extensions/socketio/src/socketio.server.ts @@ -144,9 +144,7 @@ export class SocketIoServer extends Context { if (meta instanceof RegExp || typeof meta === 'string') { meta = {namespace: meta} as SocketIoMetadata; } - if (meta == null) { - meta = getSocketIoMetadata(controllerClass) as SocketIoMetadata; - } + meta ??= getSocketIoMetadata(controllerClass) as SocketIoMetadata; const nsp = meta?.namespace ? this.io.of(meta.namespace) : this.io; if (meta?.name) { this.app.bind(getNamespaceKeyForName(meta.name)).to(nsp); @@ -280,14 +278,10 @@ function resolveHttpServerConfig( ); // Can't check falsiness, 0 is a valid port. - if (result.port == null) { - result.port = 3000; - } + result.port ??= 3000; - if (result.host == null) { - // Set it to '' so that the http server will listen on all interfaces - result.host = undefined; - } + // Set it to '' so that the http server will listen on all interfaces + result.host ??= undefined; return result; } diff --git a/fixtures/mock-oauth2-provider/src/mock-oauth2-social-app.ts b/fixtures/mock-oauth2-provider/src/mock-oauth2-social-app.ts index e43ba9284a09..82872eaa3fca 100644 --- a/fixtures/mock-oauth2-provider/src/mock-oauth2-social-app.ts +++ b/fixtures/mock-oauth2-provider/src/mock-oauth2-social-app.ts @@ -18,13 +18,52 @@ 'use strict'; import bodyParser from 'body-parser'; -import express from 'express'; +import express, {Request, RequestHandler, Response} from 'express'; import {Server} from 'http'; import jwt from 'jsonwebtoken'; import {MyUser} from './user-repository'; /* eslint-disable @typescript-eslint/naming-convention */ +/** + * Adapt an async handler so that rejections are passed to Express instead of + * becoming unhandled promise rejections. + */ +function asyncHandler( + handler: (req: Request, res: Response) => Promise, +): RequestHandler { + return (req, res, next) => { + handler(req, res).catch(next); + }; +} + +/** + * Hosts this mock provider is willing to redirect back to. + * + * A real authorization server matches `redirect_uri` against the callback urls + * registered for the client. This provider only ever serves test applications + * running on the same machine, so it accepts loopback hosts instead of + * redirecting wherever the request asks. + */ +const ALLOWED_REDIRECT_HOSTS = ['localhost', '127.0.0.1', '::1']; + +/** + * Parse `redirect_uri` and return it only when it points at a local test app. + * @param redirectUri - The `redirect_uri` taken from the request + */ +function parseRedirectUri(redirectUri: string) { + let url: URL; + try { + url = new URL(redirectUri); + } catch { + return undefined; + } + const isHttp = url.protocol === 'http:' || url.protocol === 'https:'; + // `URL` keeps IPv6 hosts in brackets + const host = url.hostname.replace(/^\[|]$/g, ''); + return isHttp && ALLOWED_REDIRECT_HOSTS.includes(host) ? url : undefined; +} + const app = express(); let server: Server; @@ -37,33 +76,37 @@ const urlencodedParser = bodyParser.urlencoded({extended: false}); /** * data structure for an app registration, also holds issued tokens for an app + * + * The maps are keyed by values taken from the request, so they are `Map`s + * rather than plain objects - a `__proto__` key would otherwise reach + * `Object.prototype`. */ interface App { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; client_secret: string; - tokens: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; - }; -} - -/** - * list of registered apps for this oauth2 provider identified by their client ids - */ -interface AppRegistry { - [clientId: string]: App; + /** + * access tokens issued for this app, keyed by the access code handed to the + * client + */ + tokens: Map; + /** + * signing key of each issued token, keyed by the token's `jti` claim + */ + issuedTokens: Map; } /** - * apps registered with this provider - * format: - * { clientId: {client_secret, list_of_tokens} } + * apps registered with this provider, keyed by their client ids */ -const registeredApps: AppRegistry = { - '1111': {client_secret: 'app1_secret', tokens: {}}, - '2222': {client_secret: 'app2_secret', tokens: {}}, -}; +const registeredApps = new Map([ + [ + '1111', + {client_secret: 'app1_secret', tokens: new Map(), issuedTokens: new Map()}, + ], + [ + '2222', + {client_secret: 'app2_secret', tokens: new Map(), issuedTokens: new Map()}, + ], +]); /** * user registry @@ -157,10 +200,13 @@ async function verifyToken(token: string) { if (unwrappedJwt == null) throw new Error('invalid token'); const tokenId = (unwrappedJwt.payload as jwt.JwtPayload).jti; if (!tokenId) throw new Error('invalid token'); - const registeredApp: App = - registeredApps[(unwrappedJwt.payload as jwt.JwtPayload).client_id]; + const registeredApp = registeredApps.get( + (unwrappedJwt.payload as jwt.JwtPayload).client_id, + ); if (registeredApp) { - const result = jwt.verify(token, registeredApp[tokenId].signingKey); + const issuedToken = registeredApp.issuedTokens.get(String(tokenId)); + if (!issuedToken) throw new Error('invalid token'); + const result = jwt.verify(token, issuedToken.signingKey); if (result) { return result as Record; } else { @@ -191,7 +237,7 @@ app.get('/oauth/dialog', function (req, res) { res.status(400).send({error: 'missing client_id'}); return; } - if (registeredApps[req.query.client_id as string]) { + if (registeredApps.has(req.query.client_id as string)) { let params = '?client_id=' + req.query.client_id + @@ -246,38 +292,54 @@ app.get('/login', function (req, response) { * 3. stores token * 4. redirects to callback url with access code */ -app.post('/login_submit', urlencodedParser, async function (req, res) { - if (!req.body.username) { - res.status(400).send({error: 'missing username'}); - return; - } - const user: MyUser | undefined = findUser( - req.body.username, - req.body.password, - ); - if (user) { - // get registered app - const registeredApp = registeredApps[req.body.client_id]; - // generate access code - const authCode = Math.floor(Math.random() * Math.floor(1000)); - // create a token for the access code - const result = await createJwt( - user, - req.body.scope, - user.signingKey, - req.body.client_id, +app.post( + '/login_submit', + urlencodedParser, + asyncHandler(async function (req, res) { + if (!req.body.username) { + res.status(400).send({error: 'missing username'}); + return; + } + const user: MyUser | undefined = findUser( + req.body.username, + req.body.password, ); - // store generated token - registeredApp.tokens[authCode] = {token: result.token}; - registeredApp[result.id] = {signingKey: user.signingKey, code: authCode}; - // redirect to call back url with the access code - let params = '?client_id=' + req.body.client_id; - params = params + '&&code=' + authCode; - res.redirect(req.body.redirect_uri + params); - } else { - res.sendStatus(401); - } -}); + if (user) { + // get registered app + const registeredApp = registeredApps.get(req.body.client_id); + if (!registeredApp) { + res.status(401).send({error: 'invalid client_id'}); + return; + } + // generate access code + const authCode = Math.floor(Math.random() * Math.floor(1000)); + // create a token for the access code + const result = await createJwt( + user, + req.body.scope, + user.signingKey, + req.body.client_id, + ); + // store generated token + registeredApp.tokens.set(String(authCode), {token: result.token}); + registeredApp.issuedTokens.set(String(result.id), { + signingKey: user.signingKey, + code: authCode, + }); + // redirect to call back url with the access code + const callbackUrl = parseRedirectUri(req.body.redirect_uri); + if (!callbackUrl) { + res.status(400).send({error: 'invalid redirect_uri'}); + return; + } + callbackUrl.searchParams.set('client_id', req.body.client_id); + callbackUrl.searchParams.set('code', String(authCode)); + res.redirect(callbackUrl.href); + } else { + res.sendStatus(401); + } + }), +); /** * Endpoint: POST '/oauth/token' @@ -290,12 +352,13 @@ app.post('/oauth/token', urlencodedParser, function (req, res) { res.status(400).send({error: 'missing client_id'}); return; } - if (registeredApps[req.body.client_id]) { + const registeredApp = registeredApps.get(req.body.client_id); + if (registeredApp) { //&& apps[req.query.client_id].client_secret === req.query.client_secret - const oauthStates = registeredApps[req.body.client_id].tokens; - if (oauthStates[req.body.code]) { + const oauthState = registeredApp.tokens.get(String(req.body.code)); + if (oauthState) { res.setHeader('Content-Type', 'application/json'); - res.send({access_token: oauthStates[req.body.code].token}); + res.send({access_token: oauthState.token}); } else { res.status(401).send({error: 'invalid code'}); } @@ -316,13 +379,13 @@ app.get('/oauth/token', function (req, res) { res.status(400).send({error: 'missing client_id'}); return; } - if (registeredApps[clientId]) { + const registeredApp = registeredApps.get(clientId); + if (registeredApp) { //&& apps[req.query.client_id].client_secret === req.query.client_secret - const oauthStates = registeredApps[clientId].tokens; - const code = req.query.code as string; - if (oauthStates[code]) { + const oauthState = registeredApp.tokens.get(req.query.code as string); + if (oauthState) { res.setHeader('Content-Type', 'application/json'); - res.send({access_token: oauthStates[code].token}); + res.send({access_token: oauthState.token}); } else { res.status(401).send({error: 'invalid code'}); } @@ -337,23 +400,26 @@ app.get('/oauth/token', function (req, res) { * * Verifies token and returns user profile */ -app.get('/verify', async function (req, res) { - try { - const token = (req.query.access_token ?? - req.header('Authorization')) as string; - if (!token) { - res.status(400).send({error: 'missing access_token'}); - return; +app.get( + '/verify', + asyncHandler(async function (req, res) { + try { + const token = (req.query.access_token ?? + req.header('Authorization')) as string; + if (!token) { + res.status(400).send({error: 'missing access_token'}); + return; + } + const result = await verifyToken(token); + const expirationTime = result.exp; + res.setHeader('Content-Type', 'application/json'); + res.send({...result, expirationTime: expirationTime}); + } catch (err) { + res.setHeader('Content-Type', 'application/json'); + res.status(401).send({error: err.message}); } - const result = await verifyToken(token); - const expirationTime = result.exp; - res.setHeader('Content-Type', 'application/json'); - res.send({...result, expirationTime: expirationTime}); - } catch (err) { - res.setHeader('Content-Type', 'application/json'); - res.status(401).send({error: err.message}); - } -}); + }), +); export function startApp(port = 9000) { server = app.listen(port); diff --git a/package-lock.json b/package-lock.json index 364537fda95b..fa5859aa7295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,8 +24,8 @@ "@npmcli/map-workspaces": "^6.0.0", "@npmcli/package-json": "^8.0.0", "@types/mocha": "^10.0.10", - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/eslint-plugin": "^8.70.0", + "@typescript-eslint/parser": "^8.70.0", "cross-env": "^10.1.0", "cz-conventional-changelog": "^3.3.0", "eslint": "^8.57.1", @@ -42,7 +42,7 @@ "mem-fs-editor": "^9.7.0", "open-cli": "^9.0.0", "prettier": "^3.8.3", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26", @@ -102,7 +102,7 @@ "loopback-connector-cloudant": "2.5.0", "ms": "2.1.3", "tslib": "^2.8.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -298,7 +298,7 @@ "@types/msgpack5": "^3.4.7", "@types/node": "^20.19.43", "@types/type-is": "^1.6.7", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -369,7 +369,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -411,7 +411,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -448,7 +448,7 @@ "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -492,7 +492,7 @@ "@types/express": "^4.17.25", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -535,7 +535,7 @@ "@types/multer": "^2.2.0", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -582,7 +582,7 @@ "graphql-ws": "^5.16.2", "rimraf": "^5.0.10", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -622,7 +622,7 @@ "@types/debug": "4.1.13", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -665,7 +665,7 @@ "@types/debug": "4.1.13", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -703,7 +703,7 @@ "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -755,7 +755,7 @@ "eslint": "^8.57.1", "lodash": "^4.18.1", "should": "^13.2.3", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -796,7 +796,7 @@ "@types/debug": "^4.1.13", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -836,7 +836,7 @@ "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -883,7 +883,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -961,7 +961,7 @@ "axios": "^1.16.0", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1007,7 +1007,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1053,7 +1053,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1092,7 +1092,7 @@ "@types/express": "^4.17.25", "@types/node": "^20.19.43", "eslint": "^8.57.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1138,7 +1138,7 @@ "eslint": "^8.57.1", "mocha": "^12.0.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1184,7 +1184,7 @@ "eslint": "^8.57.1", "socket.io-client": "^4.8.3", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1232,7 +1232,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1265,7 +1265,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1311,7 +1311,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1372,7 +1372,7 @@ "@types/node": "^20.19.43", "eslint": "^8.57.1", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1414,7 +1414,7 @@ "events": "^3.3.0", "process": "^0.11.10", "puppeteer": "^25.10.0", - "typescript": "~5.2.2", + "typescript": "npm:@typescript/typescript6@~6.0.2", "util": "^0.12.5", "webpack": "^5.110.3", "webpack-cli": "^7.2.3" @@ -1505,7 +1505,7 @@ "@types/lodash": "^4.17.25", "@types/node": "^20.19.43", "lodash": "^4.18.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1881,7 +1881,7 @@ "@loopback/testlab": "^8.0.15", "@types/debug": "^4.1.13", "@types/node": "^20.19.43", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -1928,7 +1928,7 @@ "pg": "^8.23.0", "pg-hstore": "^2.3.4", "sqlite3": "6.0.1", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -3454,23 +3454,28 @@ "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -9927,197 +9932,605 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz", + "integrity": "sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==", + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/type-utils": "8.70.0", + "@typescript-eslint/utils": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@typescript-eslint/parser": "^8.70.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.9.tgz", + "integrity": "sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==", + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", - "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.70.0.tgz", + "integrity": "sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", + "debug": "^4.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.0.tgz", + "integrity": "sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" + "@typescript-eslint/tsconfig-utils": "^8.70.0", + "@typescript-eslint/types": "^8.70.0", + "debug": "^4.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.0.tgz", + "integrity": "sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==", + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, - "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.0.tgz", + "integrity": "sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==", + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.0.tgz", + "integrity": "sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/utils": "8.70.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", + "node_modules/@typescript-eslint/types": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.0.tgz", + "integrity": "sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.0.tgz", + "integrity": "sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" + "@typescript-eslint/project-service": "8.70.0", + "@typescript-eslint/tsconfig-utils": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.0.tgz", + "integrity": "sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==", + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.0.tgz", + "integrity": "sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.70.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript/native": { + "name": "typescript", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/@typescript/old": { + "name": "typescript", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -21903,6 +22316,20 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/lerna/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/lerna/node_modules/validate-npm-package-name": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", @@ -35375,14 +35802,15 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-graphviz": { @@ -35967,15 +36395,16 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "name": "@typescript/typescript6", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript6/-/typescript6-6.0.2.tgz", + "integrity": "sha512-mbCddXd+jm7hfx7w2YU64/Av4/NqqeG3GoRZgxPcgoTxYjhrcfJRw9ULch71SS4G+Q3bOXFhRvPqjguN0Hyp5w==", + "license": "Apache-2.0", + "dependencies": { + "@typescript/old": "npm:typescript@^6" }, - "engines": { - "node": ">=14.17" + "bin": { + "tsc6": "bin/tsc6" } }, "node_modules/uid-safe": { @@ -39254,6 +39683,7 @@ "@loopback/eslint-config": "^16.0.1", "@types/mocha": "^10.0.10", "@types/node": "^20.19.43", + "@typescript/native": "npm:typescript@~7.0.2", "cross-spawn": "^7.0.6", "debug": "^4.4.3", "eslint": "^8.57.1", @@ -39265,7 +39695,7 @@ "prettier": "^3.8.3", "rimraf": "^5.0.10", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "bin": { "lb-clean": "bin/run-clean.js", @@ -39375,7 +39805,7 @@ "terminal-link": "^5.0.0", "tildify": "^2.0.0", "ts-morph": "^28.0.0", - "typescript": "~5.2.2", + "typescript": "npm:@typescript/typescript6@~6.0.2", "untildify": "^6.0.0", "update-notifier": "^7.3.1", "url-slug": "^5.1.0", @@ -40615,8 +41045,8 @@ "version": "16.0.1", "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/eslint-plugin": "^8.70.0", + "@typescript-eslint/parser": "^8.70.0", "eslint-config-prettier": "^9.1.1", "eslint-plugin-eslint-plugin": "^5.5.1", "eslint-plugin-mocha": "^10.5.0" @@ -40655,7 +41085,7 @@ "@types/on-finished": "^2.3.5", "@types/toposort": "^2.0.7", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" @@ -40692,7 +41122,7 @@ "@loopback/build": "^12.0.15", "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engines": { "node": "22 || 24 || 26" diff --git a/package.json b/package.json index 2231d7536514..6a1d053e43b8 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ "@npmcli/map-workspaces": "^6.0.0", "@npmcli/package-json": "^8.0.0", "@types/mocha": "^10.0.10", - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/eslint-plugin": "^8.70.0", + "@typescript-eslint/parser": "^8.70.0", "cross-env": "^10.1.0", "cz-conventional-changelog": "^3.3.0", "eslint": "^8.57.1", @@ -71,7 +71,7 @@ "mem-fs-editor": "^9.7.0", "open-cli": "^9.0.0", "prettier": "^3.8.3", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" }, "engineStrict": true, "engines": { diff --git a/packages/authentication/src/__tests__/fixtures/helper.ts b/packages/authentication/src/__tests__/fixtures/helper.ts index 6099d5eda748..43078d2d612a 100644 --- a/packages/authentication/src/__tests__/fixtures/helper.ts +++ b/packages/authentication/src/__tests__/fixtures/helper.ts @@ -96,7 +96,7 @@ export function createBearerAuthorizationHeaderValue( alternativePrefix?: string, ): string { // default type is 'Bearer ', unless another is specified - const prefix = alternativePrefix ? alternativePrefix : 'Bearer '; + const prefix = alternativePrefix ?? 'Bearer '; return prefix + token; } diff --git a/packages/authorization/src/__tests__/acceptance/authorization.options.acceptance.ts b/packages/authorization/src/__tests__/acceptance/authorization.options.acceptance.ts index 62c3a8abf0d2..5a9d2078877b 100644 --- a/packages/authorization/src/__tests__/acceptance/authorization.options.acceptance.ts +++ b/packages/authorization/src/__tests__/acceptance/authorization.options.acceptance.ts @@ -163,7 +163,7 @@ describe('Authorization', () => { try { await invokeMethod(controller, 'cancelOrder', reqCtx, ['order-01']); finalDecision = Allow; - } catch (err) { + } catch { finalDecision = Deny; } return finalDecision; @@ -174,7 +174,7 @@ describe('Authorization', () => { try { await invokeMethod(controller, 'placeOrder', reqCtx, ['prod-01', 10]); finalDecision = Allow; - } catch (err) { + } catch { finalDecision = Deny; } return finalDecision; diff --git a/packages/boot/src/booters/base-artifact.booter.ts b/packages/boot/src/booters/base-artifact.booter.ts index 50150a50bbd0..634c4eb489ea 100644 --- a/packages/boot/src/booters/base-artifact.booter.ts +++ b/packages/boot/src/booters/base-artifact.booter.ts @@ -96,9 +96,9 @@ export class BaseArtifactBooter implements Booter { const joinedExts = `@(${this.extensions.join('|')})`; - this.glob = this.options.glob - ? this.options.glob - : `/${joinedDirs}/${this.options.nested ? '**/*' : '*'}${joinedExts}`; + this.glob = + this.options.glob ?? + `/${joinedDirs}/${this.options.nested ? '**/*' : '*'}${joinedExts}`; } /** diff --git a/packages/boot/src/bootstrapper.ts b/packages/boot/src/bootstrapper.ts index 40c473b05c15..21ed0ce8399e 100644 --- a/packages/boot/src/bootstrapper.ts +++ b/packages/boot/src/bootstrapper.ts @@ -96,9 +96,7 @@ export class Bootstrapper { // names of booters that should be run), that is the value, otherwise it // is all the registered booters by default. const names = execOptions - ? execOptions.filter?.booters - ? execOptions.filter.booters - : defaultBooterNames + ? (execOptions.filter?.booters ?? defaultBooterNames) : defaultBooterNames; // Filter bindings by names diff --git a/packages/booter-lb3app/src/lb3app.booter.ts b/packages/booter-lb3app/src/lb3app.booter.ts index da1fef2295e5..50111db8156b 100644 --- a/packages/booter-lb3app/src/lb3app.booter.ts +++ b/packages/booter-lb3app/src/lb3app.booter.ts @@ -131,7 +131,7 @@ export class Lb3AppBooter implements Booter { } private mountFullApp(lb3App: Lb3Application, spec: OpenApiSpec) { - const restApiRoot = lb3App.get('restApiRoot') || '/'; + const restApiRoot = lb3App.get('restApiRoot') ?? '/'; debug('Mounting the entire LB3 app at %s', restApiRoot); const specInRoot = rebaseOpenApiSpec(spec, restApiRoot); this.app.mountExpressRouter('/', lb3App, specInRoot); diff --git a/packages/build/README.md b/packages/build/README.md index 55479348de04..08e69e43cb55 100644 --- a/packages/build/README.md +++ b/packages/build/README.md @@ -93,6 +93,35 @@ Now you run the scripts, such as: | ------------------ | ------------------------------------------------------------------------------------------------- | | `--copy-resources` | Copy all non-typescript files from `src` and `test` to `outDir`, preserving their relative paths. | + - TypeScript compiler + + `lb-tsc` compiles with the TypeScript 7 native compiler (installed as + `@typescript/native`), preferring the copy owned by the project being built. + If TypeScript 7 is not installed, it falls back to the `typescript` package. + + TypeScript 7 no longer ships the JavaScript compiler API, so `typescript` is + aliased to `@typescript/typescript6`. That keeps `require('typescript')` + working for tools that consume the API, such as `@typescript-eslint` and + editor language services. + + Note that TypeScript 7 removed `"moduleResolution": "node"` (node10). Remove + the option from your `tsconfig.json` - `"module": "commonjs"` resolves + modules the same way Node.js does without it. + + TypeScript 6 also stopped including every installed `@types/*` package + automatically, so `tsconfig.common.json` lists `"types": ["node", "mocha"]`. + A project that relies on other packages of global type declarations has to + list them explicitly: + + ```json + { + "extends": "@loopback/build/config/tsconfig.common.json", + "compilerOptions": { + "types": ["node", "mocha", "my-global-types"] + } + } + ``` + - Using [`ttypescript`](https://github.com/cevek/ttypescript) ### Stability: ⚠️Experimental⚠️ diff --git a/packages/build/bin/compile-package.js b/packages/build/bin/compile-package.js index 808d47227d85..85c4d08a6f1b 100755 --- a/packages/build/bin/compile-package.js +++ b/packages/build/bin/compile-package.js @@ -23,8 +23,29 @@ const path = require('path'); const fs = require('fs'); const {globSync} = require('glob'); const fse = require('fs-extra'); +// The `typescript` package is aliased to the TypeScript 6 JS compiler, which +// still ships the compiler API. TypeScript 7 (the native compiler) does not. const {buildOpts: buildOptions} = require('typescript'); +/** + * Resolve the TypeScript 7 native compiler (`@typescript/native`), preferring + * the copy owned by the package being built. Its `exports` map does not expose + * `lib/tsc.js`, so the CLI is located relative to the package manifest. + * + * Returns `undefined` when TypeScript 7 is not installed, in which case the + * `typescript` package (TypeScript 6) is used to compile. + */ +function resolveNativeTsc() { + try { + const manifest = require.resolve('@typescript/native/package.json', { + paths: [utils.getPackageDir(), __dirname], + }); + return path.join(path.dirname(manifest), 'bin', 'tsc'); + } catch (e) { + debug('TypeScript 7 (@typescript/native) is not installed: %s', e.message); + } +} + function run(argv, options) { if (options === true) { options = {dryRun: true}; @@ -48,12 +69,12 @@ function run(argv, options) { '--copy-resources', ); - let TSC_CLI = 'typescript/lib/tsc'; + let TSC_CLI = resolveNativeTsc() ?? 'typescript/lib/tsc'; if (useTtsc) { try { require.resolve('ttypescript'); TSC_CLI = 'ttypescript/lib/tsc'; - } catch (e) { + } catch { if (isUseTtscSet) { console.error( 'Error using the --use-ttypescript option - ttypescript is not installed', @@ -174,6 +195,11 @@ function run(argv, options) { * @param {string[]} args An array of arguments */ function validArgsForBuild(args) { + if (!Array.isArray(buildOptions)) { + debug('Cannot validate args for tsc -b - the compiler API is unavailable'); + return args; + } + const validBooleanOptions = []; const validValueOptions = []; @@ -194,6 +220,8 @@ function validArgsForBuild(args) { let validArgs = args; if (args.includes('-b') || args.includes('--build')) { validArgs = filterArgs(args, (arg, next) => { + // `-b` is re-added below as the first argument + if (arg === '-b' || arg === '--build') return 0; if (validBooleanOptions.includes(arg)) { return next === 'false' || next === 'true' ? 2 : 1; } diff --git a/packages/build/bin/utils.js b/packages/build/bin/utils.js index 135d7bef7f6c..2daeb243a573 100644 --- a/packages/build/bin/utils.js +++ b/packages/build/bin/utils.js @@ -70,7 +70,7 @@ function resolveCLI(cli, options = {resolveFromProjectFirst: true}) { const pkgDir = getPackageDir(); const resolved = resolveCLIFromProject(cli, pkgDir); if (resolved != null) return resolved; - } catch (e) { + } catch { // Ignore errors } return require.resolve(cli); diff --git a/packages/build/config/tsconfig.common.json b/packages/build/config/tsconfig.common.json index 8d69c2baae2a..79825d36329e 100644 --- a/packages/build/config/tsconfig.common.json +++ b/packages/build/config/tsconfig.common.json @@ -15,10 +15,13 @@ "incremental": true, + // TypeScript 6 no longer includes every `@types/*` package automatically, + // ambient type packages have to be listed explicitly. + "types": ["node", "mocha"], + "lib": ["es2020"], "module": "commonjs", "esModuleInterop": true, - "moduleResolution": "node", "target": "es2018", "sourceMap": true, "declaration": true, diff --git a/packages/build/package.json b/packages/build/package.json index 3c7b8491f093..f6e1ceeae98d 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -34,6 +34,7 @@ "@loopback/eslint-config": "^16.0.1", "@types/mocha": "^10.0.10", "@types/node": "^20.19.43", + "@typescript/native": "npm:typescript@~7.0.2", "cross-spawn": "^7.0.6", "debug": "^4.4.3", "eslint": "^8.57.1", @@ -45,6 +46,6 @@ "prettier": "^3.8.3", "rimraf": "^5.0.10", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/packages/build/test/integration/scripts.integration.js b/packages/build/test/integration/scripts.integration.js index edbaf5067e4e..bd3f3252cd90 100644 --- a/packages/build/test/integration/scripts.integration.js +++ b/packages/build/test/integration/scripts.integration.js @@ -185,7 +185,8 @@ describe('build', /** @this {Mocha.Suite} */ function () { '--locale en_US.UTF-8 should be honored', ); assert( - command.indexOf('tsc.js -b') !== -1, + // `tsc` for the TypeScript 7 native compiler, `tsc.js` for TypeScript 6 + /[\\/]tsc(\.js)? -b /.test(command), '-b should be the first argument', ); }); diff --git a/packages/cli/.yo-rc.json b/packages/cli/.yo-rc.json index 6973e6a7ffd9..d8b7ae537f0d 100644 --- a/packages/cli/.yo-rc.json +++ b/packages/cli/.yo-rc.json @@ -178,14 +178,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Project name for the application", - "name": "name" - } - ], + "arguments": [], "name": "app" }, "extension": { @@ -309,14 +302,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Project name for the extension", - "name": "name" - } - ], + "arguments": [], "name": "extension" }, "controller": { @@ -387,14 +373,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the controller", - "name": "name" - } - ], + "arguments": [], "name": "controller" }, "datasource": { @@ -458,14 +437,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the datasource", - "name": "name" - } - ], + "arguments": [], "name": "datasource" }, "import-lb3-models": { @@ -536,14 +508,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": true, - "description": "Path to your LoopBack 3.x application. This can be a project directory (e.g. \"my-lb3-app\") or the server file (e.g. \"my-lb3-app/server/server.js\").", - "name": "lb3app" - } - ], + "arguments": [], "name": "import-lb3-models" }, "model": { @@ -635,14 +600,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the model", - "name": "name" - } - ], + "arguments": [], "name": "model" }, "repository": { @@ -734,14 +692,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the repository ", - "name": "name" - } - ], + "arguments": [], "name": "repository" }, "service": { @@ -819,14 +770,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the service", - "name": "name" - } - ], + "arguments": [], "name": "service" }, "example": { @@ -890,14 +834,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "description": "Name of the example to clone", - "required": false, - "name": "example-name" - } - ], + "arguments": [], "name": "example" }, "openapi": { @@ -1015,14 +952,7 @@ "hide": false } }, - "arguments": [ - { - "description": "URL or file path of the OpenAPI spec", - "required": false, - "type": "String", - "name": "url" - } - ], + "arguments": [], "name": "openapi" }, "observer": { @@ -1093,14 +1023,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the observer", - "name": "name" - } - ], + "arguments": [], "name": "observer" }, "interceptor": { @@ -1178,14 +1101,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the interceptor", - "name": "name" - } - ], + "arguments": [], "name": "interceptor" }, "discover": { @@ -1309,14 +1225,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the discover", - "name": "name" - } - ], + "arguments": [], "name": "discover" }, "relation": { @@ -1650,14 +1559,7 @@ "hide": false } }, - "arguments": [ - { - "type": "String", - "required": false, - "description": "Name for the rest-config", - "name": "name" - } - ], + "arguments": [], "name": "rest-crud" }, "copyright": { diff --git a/packages/cli/bin/cli-main.js b/packages/cli/bin/cli-main.js index eb521bf7fba0..8c6cef47fb2c 100755 --- a/packages/cli/bin/cli-main.js +++ b/packages/cli/bin/cli-main.js @@ -46,21 +46,21 @@ const originalCommand = args[0]; if (tabCompletionCommands.includes(originalCommand)) { const yoJsonFile = path.join(__dirname, '../.yo-rc.json'); const config = fs.readJsonSync(yoJsonFile); - return runTabCompletionCommand(config.commands, originalCommand, console.log); -} - -const main = require('../lib/cli'); + runTabCompletionCommand(config.commands, originalCommand, console.log); +} else { + const main = require('../lib/cli'); -// Force version check with `lb4 --version` -const interval = opts.version ? 0 : undefined; + // Force version check with `lb4 --version` + const interval = opts.version ? 0 : undefined; -import('update-notifier') - .then(({default: updateNotifier}) => { - updateNotifier({ - pkg: pkg, - updateCheckInterval: interval, - }).notify({isGlobal: true}); - }) - .catch(() => {}); + import('update-notifier') + .then(({default: updateNotifier}) => { + updateNotifier({ + pkg: pkg, + updateCheckInterval: interval, + }).notify({isGlobal: true}); + }) + .catch(() => {}); -main(opts); + main(opts); +} diff --git a/packages/cli/generators/app/templates/src/index.ts.ejs b/packages/cli/generators/app/templates/src/index.ts.ejs index 24768091c944..eebbdbaae396 100644 --- a/packages/cli/generators/app/templates/src/index.ts.ejs +++ b/packages/cli/generators/app/templates/src/index.ts.ejs @@ -19,7 +19,7 @@ if (require.main === module) { const config = { rest: { port: +(process.env.PORT ?? 3000), - host: process.env.HOST || '127.0.0.1', + host: process.env.HOST ?? '127.0.0.1', // The `gracePeriodForClose` provides a graceful close for http/https // servers with keep-alive clients. The default value is `Infinity` // (don't force-close). If you want to immediately destroy all sockets diff --git a/packages/cli/generators/project/templates/tsconfig.json.ejs b/packages/cli/generators/project/templates/tsconfig.json.ejs index 782307ad4522..ea98a4a5c806 100644 --- a/packages/cli/generators/project/templates/tsconfig.json.ejs +++ b/packages/cli/generators/project/templates/tsconfig.json.ejs @@ -24,7 +24,6 @@ "lib": ["es2018", "esnext.asynciterable"], "module": "commonjs", "esModuleInterop": true, - "moduleResolution": "node", "target": "es2017", "sourceMap": true, "declaration": true diff --git a/packages/cli/lib/utils.js b/packages/cli/lib/utils.js index a4a3ee26b4a9..e2ecc645509b 100644 --- a/packages/cli/lib/utils.js +++ b/packages/cli/lib/utils.js @@ -454,7 +454,7 @@ exports.validateStringObject = function (type) { if (type === 'array' && !Array.isArray(result)) { return err; } - } catch (e) { + } catch { return err; } diff --git a/packages/cli/package.json b/packages/cli/package.json index 3b34b7af5d98..8debd66fbe0d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -73,7 +73,7 @@ "terminal-link": "^5.0.0", "tildify": "^2.0.0", "ts-morph": "^28.0.0", - "typescript": "~5.2.2", + "typescript": "npm:@typescript/typescript6@~6.0.2", "untildify": "^6.0.0", "update-notifier": "^7.3.1", "url-slug": "^5.1.0", @@ -110,12 +110,12 @@ "cross-spawn": "^7.0.6", "debug": "^4.4.3", "fs-extra": "^11.4.0", - "mocha": "^11.8.0", + "mocha": "^12.0.1", "nyc": "^18.0.0", "prettier": "^3.8.3", "rimraf": "^5.0.10", "source-map-support": "^0.5.21", - "typescript": "~5.2.2", + "typescript": "npm:@typescript/typescript6@~6.0.2", "@loopback/authentication": "^12.0.17", "@loopback/boot": "^8.0.16", "@loopback/build": "^12.0.15", @@ -152,8 +152,8 @@ "eslint-plugin-mocha": "^10.5.0", "@loopback/example-greeting-app": "^8.0.16", "@loopback/example-context": "^8.0.15", - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/eslint-plugin": "^8.70.0", + "@typescript-eslint/parser": "^8.70.0", "eslint-plugin-eslint-plugin": "^5.5.1", "eslint-config-prettier": "^9.1.1", "@loopback/repository-tests": "^0.25.16", @@ -195,7 +195,8 @@ "@loopback/monorepo": "^0.6.0", "@loopback/tsdocs": "^7.0.15", "@loopback/example-references-many": "^9.0.16", - "@loopback/sequelize": "^0.8.12" + "@loopback/sequelize": "^0.8.12", + "@typescript/native": "npm:typescript@~7.0.2" } } } diff --git a/packages/context/src/__tests__/unit/try-catch-finally.unit.ts b/packages/context/src/__tests__/unit/try-catch-finally.unit.ts index 697d04162252..b772e13f3eda 100644 --- a/packages/context/src/__tests__/unit/try-catch-finally.unit.ts +++ b/packages/context/src/__tests__/unit/try-catch-finally.unit.ts @@ -19,7 +19,7 @@ describe('tryWithFinally', () => { let finalActionInvoked = false; const action = () => 1; const finalAction = () => (finalActionInvoked = true); - // eslint-disable-next-line @typescript-eslint/no-floating-promises + tryWithFinally(action, finalAction); expect(finalActionInvoked).to.be.true(); }); @@ -56,7 +56,7 @@ describe('tryCatchFinally', () => { let finalActionInvoked = false; const action = () => 1; const finalAction = () => (finalActionInvoked = true); - // eslint-disable-next-line @typescript-eslint/no-floating-promises + tryCatchFinally(action, undefined, finalAction); expect(finalActionInvoked).to.be.true(); }); @@ -79,7 +79,7 @@ describe('tryCatchFinally', () => { errorActionInvoked = true; throw err; }; - // eslint-disable-next-line @typescript-eslint/no-floating-promises + tryCatchFinally(action, errorAction); expect(errorActionInvoked).to.be.false(); }); diff --git a/packages/context/src/binding-inspector.ts b/packages/context/src/binding-inspector.ts index 85a777b49a2c..17db19645eef 100644 --- a/packages/context/src/binding-inspector.ts +++ b/packages/context/src/binding-inspector.ts @@ -356,16 +356,14 @@ function buildBindingKey( ); // Derive the key from type + name let type = options.type ?? bindingTemplate.tagMap[ContextTags.TYPE]; - if (!type) { - type = - bindingTemplate.tagNames.find(t => namespaces[t] != null) ?? - ContextTags.CLASS; - } + type ??= + bindingTemplate.tagNames.find(t => namespaces[t] != null) ?? + ContextTags.CLASS; namespace = getNamespace(type, namespaces); } const name = - options.name ?? (bindingTemplate.tagMap[ContextTags.NAME] || cls.name); + options.name ?? bindingTemplate.tagMap[ContextTags.NAME] ?? cls.name; key = `${namespace}.${name}`; return key; diff --git a/packages/context/src/context-view.ts b/packages/context/src/context-view.ts index 22be275203dd..1f013cc29945 100644 --- a/packages/context/src/context-view.ts +++ b/packages/context/src/context-view.ts @@ -127,9 +127,7 @@ export class ContextView */ get bindings(): Readonly>[] { debug('Reading bindings'); - if (this._cachedBindings == null) { - this._cachedBindings = this.findBindings(); - } + this._cachedBindings ??= this.findBindings(); return this._cachedBindings; } diff --git a/packages/context/src/inject-config.ts b/packages/context/src/inject-config.ts index 04d48fda6a84..1dc692a9c688 100644 --- a/packages/context/src/inject-config.ts +++ b/packages/context/src/inject-config.ts @@ -138,7 +138,7 @@ function getCurrentBindingKey(session: ResolutionSession) { * @param session - Resolution session */ function getTargetBindingKey(injection: Injection, session: ResolutionSession) { - return injection.metadata.fromBinding || getCurrentBindingKey(session); + return injection.metadata.fromBinding ?? getCurrentBindingKey(session); } /** diff --git a/packages/context/src/resolution-session.ts b/packages/context/src/resolution-session.ts index 0ed8af400b22..a264840d05d3 100644 --- a/packages/context/src/resolution-session.ts +++ b/packages/context/src/resolution-session.ts @@ -55,7 +55,7 @@ export type ResolutionElement = BindingElement | InjectionElement; function isBinding( element: ResolutionElement | undefined, ): element is BindingElement { - return element != null && element.type === 'binding'; + return element?.type === 'binding'; } /** @@ -65,7 +65,7 @@ function isBinding( function isInjection( element: ResolutionElement | undefined, ): element is InjectionElement { - return element != null && element.type === 'injection'; + return element?.type === 'injection'; } /** diff --git a/packages/context/src/value-promise.ts b/packages/context/src/value-promise.ts index 4e4aa85c944a..d026c3bc3331 100644 --- a/packages/context/src/value-promise.ts +++ b/packages/context/src/value-promise.ts @@ -110,7 +110,7 @@ export function resolveMap( for (const key in map) { const valueOrPromise = resolver(map[key], key, map); if (isPromiseLike(valueOrPromise)) { - if (!asyncResolvers) asyncResolvers = []; + asyncResolvers ??= []; asyncResolvers.push(valueOrPromise.then(setter(key))); } else { if (valueOrPromise !== undefined) { @@ -168,7 +168,7 @@ export function resolveList( for (let ix = 0; ix < list.length; ix++) { const valueOrPromise = resolver(list[ix], ix, list); if (isPromiseLike(valueOrPromise)) { - if (!asyncResolvers) asyncResolvers = []; + asyncResolvers ??= []; asyncResolvers.push(valueOrPromise.then(setter(ix))); } else { result[ix] = valueOrPromise; diff --git a/packages/core/src/extension-point.ts b/packages/core/src/extension-point.ts index 3fed237d544c..192425e129d3 100644 --- a/packages/core/src/extension-point.ts +++ b/packages/core/src/extension-point.ts @@ -229,7 +229,7 @@ function inferExtensionPointName( ): string { if (currentBinding) { const name = - currentBinding.tagMap[CoreTags.EXTENSION_POINT] || + currentBinding.tagMap[CoreTags.EXTENSION_POINT] ?? currentBinding.tagMap[ContextTags.NAME]; if (name) return name; diff --git a/packages/core/src/lifecycle-registry.ts b/packages/core/src/lifecycle-registry.ts index 3178bd0dd555..a6c1c22fa7c2 100644 --- a/packages/core/src/lifecycle-registry.ts +++ b/packages/core/src/lifecycle-registry.ts @@ -99,11 +99,9 @@ export class LifeCycleObserverRegistry implements LifeCycleObserver { ): string { // First check if there is an explicit group name in the tag let group = binding.tagMap[CoreTags.LIFE_CYCLE_OBSERVER_GROUP]; - if (!group) { - // Fall back to a tag that matches one of the groups - group = this.options.orderedGroups.find(g => binding.tagMap[g] === g); - } - group = group || ''; + // Fall back to a tag that matches one of the groups + group ??= this.options.orderedGroups.find(g => binding.tagMap[g] === g); + group = group ?? ''; debug( 'Binding %s is configured with observer group %s', binding.key, diff --git a/packages/eslint-config/eslintrc.js b/packages/eslint-config/eslintrc.js index 6a9385df6153..ebd4b5acbfc7 100644 --- a/packages/eslint-config/eslintrc.js +++ b/packages/eslint-config/eslintrc.js @@ -69,6 +69,8 @@ module.exports = { '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-member-accessibility': 'off', '@typescript-eslint/no-var-requires': 'off', + // `no-require-imports` replaces `no-var-requires` in typescript-eslint v8 + '@typescript-eslint/no-require-imports': 'off', '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/no-object-literal-type-assertion': 'off', '@typescript-eslint/no-parameter-properties': 'off', @@ -76,6 +78,10 @@ module.exports = { '@typescript-eslint/prefer-interface': 'off', '@typescript-eslint/no-namespace': 'off', '@typescript-eslint/ban-types': 'off', + // `ban-types` was split into the following rules in typescript-eslint v8 + '@typescript-eslint/no-empty-object-type': 'off', + '@typescript-eslint/no-unsafe-function-type': 'off', + '@typescript-eslint/no-wrapper-object-types': 'off', '@typescript-eslint/no-triple-slash-reference': 'off', '@typescript-eslint/no-empty-interface': 'off', '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 9ddee37a0f6a..1319f5af0470 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -21,8 +21,8 @@ "eslint": "^8.57.1" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", + "@typescript-eslint/eslint-plugin": "^8.70.0", + "@typescript-eslint/parser": "^8.70.0", "eslint-config-prettier": "^9.1.1", "eslint-plugin-eslint-plugin": "^5.5.1", "eslint-plugin-mocha": "^10.5.0" diff --git a/packages/express/package.json b/packages/express/package.json index 58ba6e77b2c6..6320694acada 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -62,6 +62,6 @@ "@types/on-finished": "^2.3.5", "@types/toposort": "^2.0.7", "source-map-support": "^0.5.21", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/packages/filter/package.json b/packages/filter/package.json index c51bcc8b184f..01f3ad613da3 100644 --- a/packages/filter/package.json +++ b/packages/filter/package.json @@ -38,6 +38,6 @@ "@loopback/build": "^12.0.15", "@loopback/testlab": "^8.0.15", "@types/node": "^20.19.43", - "typescript": "~5.2.2" + "typescript": "npm:@typescript/typescript6@~6.0.2" } } diff --git a/packages/filter/src/query.ts b/packages/filter/src/query.ts index bdd4afe5c972..5d036bb888e5 100644 --- a/packages/filter/src/query.ts +++ b/packages/filter/src/query.ts @@ -610,9 +610,7 @@ export class FilterBuilder { * or an Order object for the field/direction pairs */ order(...o: (string | string[] | Order)[]): this { - if (!this.filter.order) { - this.filter.order = []; - } + this.filter.order ??= []; o.forEach(order => { if (typeof order === 'string') { this.validateOrder(order); @@ -646,9 +644,7 @@ export class FilterBuilder { * object for the relation/scope definitions */ include(...i: (string | string[] | Inclusion)[]): this { - if (this.filter.include == null) { - this.filter.include = []; - } + this.filter.include ??= []; for (const include of i) { if (typeof include === 'string') { this.filter.include.push({relation: include}); @@ -745,7 +741,7 @@ export function filterTemplate(strings: TemplateStringsArray, ...keys: any[]) { const result = tokens.join(''); try { return JSON.parse(result); - } catch (e) { + } catch { throw new Error('Invalid JSON: ' + result); } }; diff --git a/packages/http-caching-proxy/src/http-caching-proxy.ts b/packages/http-caching-proxy/src/http-caching-proxy.ts index 89c761aed051..e5af8267bc0b 100644 --- a/packages/http-caching-proxy/src/http-caching-proxy.ts +++ b/packages/http-caching-proxy/src/http-caching-proxy.ts @@ -146,7 +146,7 @@ export class HttpCachingProxy { const onerror = (error: Error) => { this.logError(request, error); // eslint-disable-next-line @typescript-eslint/no-explicit-any - response.statusCode = (error as any).statusCode || 502; + response.statusCode = (error as any).statusCode ?? 502; response.end(`${error.name}: ${error.message}`); }; diff --git a/packages/metadata/src/decorator-factory.ts b/packages/metadata/src/decorator-factory.ts index 0ba155e43d1e..e641d4a55d9d 100644 --- a/packages/metadata/src/decorator-factory.ts +++ b/packages/metadata/src/decorator-factory.ts @@ -579,7 +579,7 @@ export class ParameterDecoratorFactory extends DecoratorFactory< target: Object, methodName?: string, ) { - const method = methodName ? methodName : ''; + const method = methodName ?? ''; let methodMeta = meta[method]; if (methodMeta == null) { // Initialize the method metadata @@ -700,7 +700,7 @@ export class MethodParameterDecoratorFactory extends DecoratorFactory< methodName, ); // Default to the last parameter - if (index == null) index = numOfParams - 1; + index ??= numOfParams - 1; if (index < 0) { // Excessive decorations than the number of parameters detected const method = DecoratorFactory.getTargetName( diff --git a/packages/openapi-spec-builder/src/openapi-spec-builder.ts b/packages/openapi-spec-builder/src/openapi-spec-builder.ts index cb798873a3b9..6a6b752fc502 100644 --- a/packages/openapi-spec-builder/src/openapi-spec-builder.ts +++ b/packages/openapi-spec-builder/src/openapi-spec-builder.ts @@ -114,7 +114,7 @@ export class OpenApiSpecBuilder extends BuilderBase { spec: OperationObject | OperationSpecBuilder, ): this { if (spec instanceof OperationSpecBuilder) spec = spec.build(); - if (!this._spec.paths[path]) this._spec.paths[path] = {}; + this._spec.paths[path] ??= {}; this._spec.paths[path][verb] = spec; return this; } @@ -145,7 +145,7 @@ export class OpenApiSpecBuilder extends BuilderBase { */ withComponents(spec: ComponentsObject | ComponentsSpecBuilder): this { if (spec instanceof ComponentsSpecBuilder) spec = spec.build(); - if (!this._spec.components) this._spec.components = spec; + this._spec.components ??= spec; return this; } } @@ -190,7 +190,7 @@ export class OperationSpecBuilder extends BuilderBase { * @param parameterSpecs */ withParameter(...parameterSpecs: ParameterObject[]): this { - if (!this._spec.parameters) this._spec.parameters = []; + this._spec.parameters ??= []; this._spec.parameters.push(...parameterSpecs); return this; } @@ -251,7 +251,7 @@ export class OperationSpecBuilder extends BuilderBase { * @param tags */ withTags(tags: string | string[]): this { - if (!this._spec.tags) this._spec.tags = []; + this._spec.tags ??= []; if (typeof tags === 'string') tags = [tags]; this._spec.tags.push(...tags); return this; @@ -274,7 +274,7 @@ export class ComponentsSpecBuilder extends BuilderBase { * */ withSchema(name: string, schema: SchemaObject | ReferenceObject): this { - if (!this._spec.schemas) this._spec.schemas = {}; + this._spec.schemas ??= {}; this._spec.schemas[name] = schema; return this; } @@ -287,7 +287,7 @@ export class ComponentsSpecBuilder extends BuilderBase { * */ withResponse(name: string, response: ResponseObject | ReferenceObject): this { - if (!this._spec.responses) this._spec.responses = {}; + this._spec.responses ??= {}; this._spec.responses[name] = response; return this; } @@ -303,7 +303,7 @@ export class ComponentsSpecBuilder extends BuilderBase { name: string, parameter: ParameterObject | ReferenceObject, ): this { - if (!this._spec.parameters) this._spec.parameters = {}; + this._spec.parameters ??= {}; this._spec.parameters[name] = parameter; return this; } @@ -316,7 +316,7 @@ export class ComponentsSpecBuilder extends BuilderBase { * */ withExample(name: string, example: ExampleObject | ReferenceObject): this { - if (!this._spec.examples) this._spec.examples = {}; + this._spec.examples ??= {}; this._spec.examples[name] = example; return this; } @@ -332,7 +332,7 @@ export class ComponentsSpecBuilder extends BuilderBase { name: string, requestBody: RequestBodyObject | ReferenceObject, ): this { - if (!this._spec.requestBodies) this._spec.requestBodies = {}; + this._spec.requestBodies ??= {}; this._spec.requestBodies[name] = requestBody; return this; } @@ -345,7 +345,7 @@ export class ComponentsSpecBuilder extends BuilderBase { * */ withHeader(name: string, header: HeaderObject | ReferenceObject): this { - if (!this._spec.headers) this._spec.headers = {}; + this._spec.headers ??= {}; this._spec.headers[name] = header; return this; } @@ -361,7 +361,7 @@ export class ComponentsSpecBuilder extends BuilderBase { name: string, securityScheme: SecuritySchemeObject | ReferenceObject, ): this { - if (!this._spec.securitySchemes) this._spec.securitySchemes = {}; + this._spec.securitySchemes ??= {}; this._spec.securitySchemes[name] = securityScheme; return this; } @@ -374,7 +374,7 @@ export class ComponentsSpecBuilder extends BuilderBase { * */ withLink(name: string, link: LinkObject | ReferenceObject): this { - if (!this._spec.links) this._spec.links = {}; + this._spec.links ??= {}; this._spec.links[name] = link; return this; } @@ -387,7 +387,7 @@ export class ComponentsSpecBuilder extends BuilderBase { * */ withCallback(name: string, callback: CallbackObject | ReferenceObject): this { - if (!this._spec.callbacks) this._spec.callbacks = {}; + this._spec.callbacks ??= {}; this._spec.callbacks[name] = callback; return this; } diff --git a/packages/openapi-v3/src/controller-spec.ts b/packages/openapi-v3/src/controller-spec.ts index 424c8be32056..806871524631 100644 --- a/packages/openapi-v3/src/controller-spec.ts +++ b/packages/openapi-v3/src/controller-spec.ts @@ -355,21 +355,17 @@ function resolveControllerSpec(constructor: Function): ControllerSpec { operationSpec['x-operation-name'] = op; operationSpec['x-controller-name'] = - operationSpec['x-controller-name'] || constructor.name; - - if (operationSpec.operationId == null) { - // Build the operationId as `.` - // Please note API explorer (https://github.com/swagger-api/swagger-js/) - // will normalize it as `_` - operationSpec.operationId = - operationSpec['x-controller-name'] + - '.' + - operationSpec['x-operation-name']; - } + operationSpec['x-controller-name'] ?? constructor.name; - if (!spec.paths[path]) { - spec.paths[path] = {}; - } + // Build the operationId as `.` + // Please note API explorer (https://github.com/swagger-api/swagger-js/) + // will normalize it as `_` + operationSpec.operationId ??= + operationSpec['x-controller-name'] + + '.' + + operationSpec['x-operation-name']; + + spec.paths[path] ??= {}; if (spec.paths[path][verb]) { // Operations from subclasses override those from the base diff --git a/packages/openapi-v3/src/decorators/parameter.decorator.ts b/packages/openapi-v3/src/decorators/parameter.decorator.ts index fe9c7505a6f0..8f63ba91c128 100644 --- a/packages/openapi-v3/src/decorators/parameter.decorator.ts +++ b/packages/openapi-v3/src/decorators/parameter.decorator.ts @@ -41,7 +41,7 @@ export function param(paramSpec: ParameterObject) { paramSpec = {...paramSpec}; // Get the design time method parameter metadata const methodSig = MetadataInspector.getDesignTypeForMethod(target, member); - const paramTypes = methodSig?.parameterTypes || []; + const paramTypes = methodSig?.parameterTypes ?? []; // Map design-time parameter type to the OpenAPI param type diff --git a/packages/openapi-v3/src/decorators/request-body.decorator.ts b/packages/openapi-v3/src/decorators/request-body.decorator.ts index 8a4345df6872..76972391b636 100644 --- a/packages/openapi-v3/src/decorators/request-body.decorator.ts +++ b/packages/openapi-v3/src/decorators/request-body.decorator.ts @@ -93,7 +93,7 @@ export function requestBody(requestBodySpec?: Partial) { // Get the design time method parameter metadata const methodSig = MetadataInspector.getDesignTypeForMethod(target, member); - const paramTypes = methodSig?.parameterTypes || []; + const paramTypes = methodSig?.parameterTypes ?? []; const paramType = paramTypes[index]; const schema = resolveSchema(paramType); @@ -101,9 +101,7 @@ export function requestBody(requestBodySpec?: Partial) { if (debug.enabled) debug(' inferred schema: %s', inspect(schema, {depth: null})); requestBodySpec.content = _.mapValues(requestBodySpec.content, c => { - if (!c.schema) { - c.schema = schema; - } + c.schema ??= schema; return c; }); diff --git a/packages/repository-json-schema/src/build-schema.ts b/packages/repository-json-schema/src/build-schema.ts index 74f143369ea2..e0f97a1298f2 100644 --- a/packages/repository-json-schema/src/build-schema.ts +++ b/packages/repository-json-schema/src/build-schema.ts @@ -344,7 +344,7 @@ function buildSchemaTitle( options: JsonSchemaOptions, ) { if (options.title) return options.title; - const title = meta.title || ctor.name; + const title = meta.title ?? ctor.name; return title + getTitleSuffix(options); } @@ -489,7 +489,7 @@ export function modelToJsonSchema( indexInfo = {[p]: index}; } if (indexInfo && Object.keys(indexInfo).length) { - if (result.description === undefined) result.description = ''; + result.description ??= ''; if (result.description.includes('indexInfo')) { const indexInfoMatched = result.description.match(/\{"indexInfo".*$/s); if (indexInfoMatched) { diff --git a/packages/repository-tests/src/crud/transactions.suite.ts b/packages/repository-tests/src/crud/transactions.suite.ts index e82187c90128..cbe273e4b366 100644 --- a/packages/repository-tests/src/crud/transactions.suite.ts +++ b/packages/repository-tests/src/crud/transactions.suite.ts @@ -89,7 +89,7 @@ export function transactionSuite( afterEach(async () => { if (ds2) { - ds2.disconnect(); + await ds2.disconnect(); (ds2 as unknown) = undefined; } }); diff --git a/packages/repository/src/__tests__/unit/repositories/belongs-to-repository-factory.unit.ts b/packages/repository/src/__tests__/unit/repositories/belongs-to-repository-factory.unit.ts index 3846d899cc77..8c220545ec1f 100644 --- a/packages/repository/src/__tests__/unit/repositories/belongs-to-repository-factory.unit.ts +++ b/packages/repository/src/__tests__/unit/repositories/belongs-to-repository-factory.unit.ts @@ -144,7 +144,7 @@ describe('createBelongsToAccessor', () => { } function givenStubbedCompanyRepo() { - customerRepo = createStubInstance(CompanyRepository); + companyRepo = createStubInstance(CompanyRepository); } function givenBelongsToDefinition( diff --git a/packages/repository/src/mixins/repository.mixin.ts b/packages/repository/src/mixins/repository.mixin.ts index a2411ab002b2..22780cef0f69 100644 --- a/packages/repository/src/mixins/repository.mixin.ts +++ b/packages/repository/src/mixins/repository.mixin.ts @@ -131,12 +131,12 @@ export function RepositoryMixin>( const options = toOptions(nameOrOptions); // We have an instance of if (dataSource instanceof juggler.DataSource) { - const name = options.name || dataSource.name; + const name = options.name ?? dataSource.name; const namespace = options.namespace ?? RepositoryBindings.DATASOURCES; const key = `${namespace}.${name}`; return this.bind(key).to(dataSource).tag(RepositoryTags.DATASOURCE); } else if (typeof dataSource === 'function') { - options.name = options.name || dataSource.dataSourceName; + options.name = options.name ?? dataSource.dataSourceName; const binding = createBindingFromClass(dataSource, { namespace: RepositoryBindings.DATASOURCES, type: RepositoryTags.DATASOURCE, diff --git a/packages/repository/src/model.ts b/packages/repository/src/model.ts index 5424d2d2a6f1..529be2041aac 100644 --- a/packages/repository/src/model.ts +++ b/packages/repository/src/model.ts @@ -352,7 +352,7 @@ export class Model { }; const json: AnyObject = {}; - const hiddenProperties: string[] = def.settings.hiddenProperties || []; + const hiddenProperties: string[] = def.settings.hiddenProperties ?? []; for (const p in def.properties) { if (p in this && !hiddenProperties.includes(p)) { copyPropertyAsJson(p); @@ -383,7 +383,7 @@ export class Model { const obj: AnyObject = {}; if (options?.ignoreUnknownProperties === false) { - const hiddenProperties: string[] = def?.settings.hiddenProperties || []; + const hiddenProperties: string[] = def?.settings.hiddenProperties ?? []; for (const p in this) { if (!hiddenProperties.includes(p)) { const val = (this as AnyObject)[p]; diff --git a/packages/repository/src/types/buffer.ts b/packages/repository/src/types/buffer.ts index 3ebe4e74d8a7..72bba2ee60fb 100644 --- a/packages/repository/src/types/buffer.ts +++ b/packages/repository/src/types/buffer.ts @@ -36,7 +36,7 @@ export class BufferType implements Type { if (Buffer.isBuffer(value)) return value as Buffer; if (typeof value === 'string') { options = options ?? {}; - const encoding = options.encoding || 'utf-8'; + const encoding = options.encoding ?? 'utf-8'; return Buffer.from(value, encoding); } else if (Array.isArray(value)) { return Buffer.from(value); @@ -47,7 +47,7 @@ export class BufferType implements Type { serialize(value: Buffer | null | undefined, options?: Options) { if (value == null) return value; - const encoding = options?.encoding || 'base64'; + const encoding = options?.encoding ?? 'base64'; return value.toString(encoding); } } diff --git a/packages/rest/src/__tests__/acceptance/caching-interceptor/caching-interceptor.ts b/packages/rest/src/__tests__/acceptance/caching-interceptor/caching-interceptor.ts index 8b83ae646d11..b4100718522d 100644 --- a/packages/rest/src/__tests__/acceptance/caching-interceptor/caching-interceptor.ts +++ b/packages/rest/src/__tests__/acceptance/caching-interceptor/caching-interceptor.ts @@ -70,7 +70,7 @@ export async function cache( const req = await invocationCtx.get(RestBindings.Http.REQUEST, { optional: true, }); - if (!req || req.method.toLowerCase() !== 'get') { + if (req?.method.toLowerCase() !== 'get') { // The method is not invoked by an http request, no caching return next(); } diff --git a/packages/rest/src/rest.application.ts b/packages/rest/src/rest.application.ts index 7041addd9c1e..970d203f3e13 100644 --- a/packages/rest/src/rest.application.ts +++ b/packages/rest/src/rest.application.ts @@ -285,24 +285,6 @@ export class RestApplication extends Application implements HttpServerLike { */ route(route: RouteEntry): Binding; - /** - * Register a new route. - * - * @example - * ```ts - * function greet(name: string) { - * return `hello ${name}`; - * } - * app.route('get', '/', operationSpec, greet); - * ``` - */ - route( - verb: string, - path: string, - spec: OperationObject, - handler: Function, - ): Binding; - route( routeOrVerb: RouteEntry | string, path?: string, diff --git a/packages/rest/src/rest.server.ts b/packages/rest/src/rest.server.ts index 0d398e2fdaa5..38d5ef235cff 100644 --- a/packages/rest/src/rest.server.ts +++ b/packages/rest/src/rest.server.ts @@ -343,9 +343,9 @@ export class RestServer this.addOpenApiSpecEndpoint(p, mapping[p], router); } const explorerPaths = ['/swagger-ui', '/explorer']; - router.get(explorerPaths, (req, res, next) => - this._redirectToSwaggerUI(req, res, next), - ); + router.get(explorerPaths, (req, res, next) => { + this._redirectToSwaggerUI(req, res, next).catch(next); + }); this.expressMiddleware('middleware.apiSpec.defaults', router, { group: RestMiddlewareGroups.API_SPEC, upstreamGroups: RestMiddlewareGroups.CORS, @@ -376,7 +376,9 @@ export class RestServer ); } const newRouter = express.Router(); - newRouter.get(path, (req, res) => this._serveOpenApiSpec(req, res, form)); + newRouter.get(path, (req, res, next) => { + this._serveOpenApiSpec(req, res, form).catch(next); + }); this.expressMiddleware( () => newRouter, {}, @@ -387,7 +389,9 @@ export class RestServer }, ); } else { - router.get(path, (req, res) => this._serveOpenApiSpec(req, res, form)); + router.get(path, (req, res, next) => { + this._serveOpenApiSpec(req, res, form).catch(next); + }); } } @@ -1227,20 +1231,14 @@ function resolveRestServerConfig( ); // Can't check falsiness, 0 is a valid port. - if (result.port == null) { - result.port = 3000; - } + result.port ??= 3000; - if (result.host == null) { - // Set it to '' so that the http server will listen on all interfaces - result.host = undefined; - } + // Set it to '' so that the http server will listen on all interfaces + result.host ??= undefined; - if (!result.openApiSpec.endpointMapping) { - // mapping may be mutated by addOpenApiSpecEndpoint, be sure that doesn't - // pollute the default mapping configuration - result.openApiSpec.endpointMapping = cloneDeep(OPENAPI_SPEC_MAPPING); - } + // mapping may be mutated by addOpenApiSpecEndpoint, be sure that doesn't + // pollute the default mapping configuration + result.openApiSpec.endpointMapping ??= cloneDeep(OPENAPI_SPEC_MAPPING); result.apiExplorer = normalizeApiExplorerConfig(config.apiExplorer); diff --git a/packages/rest/src/router/controller-route.ts b/packages/rest/src/router/controller-route.ts index 33e204f9fffd..3b7fd02b6dad 100644 --- a/packages/rest/src/router/controller-route.ts +++ b/packages/rest/src/router/controller-route.ts @@ -67,7 +67,7 @@ export class ControllerRoute extends BaseRoute { controllerFactory?: ControllerFactory, methodName?: string, ) { - const controllerName = spec['x-controller-name'] || controllerCtor.name; + const controllerName = spec['x-controller-name'] ?? controllerCtor.name; methodName = methodName ?? spec['x-operation-name']; if (!methodName) { @@ -96,7 +96,7 @@ export class ControllerRoute extends BaseRoute { this._controllerFactory = controllerFactory ?? createControllerFactoryForClass(controllerCtor); this._controllerCtor = controllerCtor; - this._controllerName = controllerName || controllerCtor.name; + this._controllerName = controllerName ?? controllerCtor.name; this._methodName = methodName; } @@ -170,9 +170,7 @@ export function createControllerFactoryForClass( let inst = await ctx.get(`controllers.${controllerCtor.name}`, { optional: true, }); - if (inst === undefined) { - inst = await instantiateClass(controllerCtor, ctx); - } + inst ??= await instantiateClass(controllerCtor, ctx); return inst; }; } diff --git a/packages/rest/src/router/router-spec.ts b/packages/rest/src/router/router-spec.ts index 1de5b781ca73..3621d01371af 100644 --- a/packages/rest/src/router/router-spec.ts +++ b/packages/rest/src/router/router-spec.ts @@ -9,9 +9,9 @@ export type RouterSpec = Pick; export function assignRouterSpec(target: RouterSpec, additions: RouterSpec) { if (additions.components) { - if (!target.components) target.components = {}; + target.components ??= {}; for (const key in additions.components) { - if (!target.components[key]) target.components[key] = {}; + target.components[key] ??= {}; Object.assign(target.components[key], additions.components[key]); } } @@ -26,7 +26,7 @@ export function assignRouterSpec(target: RouterSpec, additions: RouterSpec) { } if (additions.tags && additions.tags.length > 0) { - if (!target.tags) target.tags = []; + target.tags ??= []; for (const tag of additions.tags) { // tags defined earlier take precedence if (target.tags.some(t => t.name === tag.name)) continue; diff --git a/packages/rest/src/router/routing-table.ts b/packages/rest/src/router/routing-table.ts index bc0677559a9e..3dc2313eab54 100644 --- a/packages/rest/src/router/routing-table.ts +++ b/packages/rest/src/router/routing-table.ts @@ -83,9 +83,7 @@ export class RoutingTable { for (const route of this._router.list()) { if (route.spec['x-visibility'] === 'undocumented') continue; - if (!paths[route.path]) { - paths[route.path] = {}; - } + paths[route.path] ??= {}; paths[route.path][route.verb] = route.spec; } diff --git a/packages/testlab/package.json b/packages/testlab/package.json index faf1d2d6d44b..a6ce44bd31a8 100644 --- a/packages/testlab/package.json +++ b/packages/testlab/package.json @@ -29,7 +29,6 @@ "README.md", "dist", "fixtures", - "should-as-function.d.ts", "src", "!*/__tests__" ], diff --git a/packages/testlab/src/expect.ts b/packages/testlab/src/expect.ts index be7f3f175573..a74adb9f893c 100644 --- a/packages/testlab/src/expect.ts +++ b/packages/testlab/src/expect.ts @@ -3,8 +3,7 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// +import {Internal} from './should-as-function'; const shouldAsFunction: Internal = require('should/as-function'); diff --git a/packages/testlab/src/http-server-config.ts b/packages/testlab/src/http-server-config.ts index 45f5f02c456a..793be72d718a 100644 --- a/packages/testlab/src/http-server-config.ts +++ b/packages/testlab/src/http-server-config.ts @@ -56,8 +56,8 @@ export function givenHttpServerConfig( if (isHttpsConfig(customConfig)) { const config: T = {...customConfig}; - if (config.host == null) config.host = defaults.host; - if (config.port == null) config.port = defaults.port; + config.host ??= defaults.host; + config.port ??= defaults.port; setupTlsConfig(config as HttpsOptions); assertHostPort(config); return config; @@ -65,8 +65,8 @@ export function givenHttpServerConfig( assertHttpConfig(customConfig); const config: T = {...customConfig}; - if (config.host == null) config.host = defaults.host; - if (config.port == null) config.port = defaults.port; + config.host ??= defaults.host; + config.port ??= defaults.port; assertHostPort(config); return config; } diff --git a/packages/testlab/should-as-function.d.ts b/packages/testlab/src/should-as-function.ts similarity index 90% rename from packages/testlab/should-as-function.d.ts rename to packages/testlab/src/should-as-function.ts index 6fe6886c2fc7..059e2d4cdf1e 100644 --- a/packages/testlab/should-as-function.d.ts +++ b/packages/testlab/src/should-as-function.ts @@ -26,8 +26,10 @@ interface Object { /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/unified-signatures */ +/* These are should.js API names, they are not ours to rename. */ +/* eslint-disable @typescript-eslint/naming-convention */ -interface ShouldAssertion { +export interface ShouldAssertion { // basic grammar a: ShouldAssertion; an: ShouldAssertion; @@ -141,9 +143,9 @@ interface ShouldAssertion { //stubs alwaysCalledOn(thisTarget: any): ShouldAssertion; - alwaysCalledWith(...arguments: any[]): ShouldAssertion; - alwaysCalledWithExactly(...arguments: any[]): ShouldAssertion; - alwaysCalledWithMatch(...arguments: any[]): ShouldAssertion; + alwaysCalledWith(...args: any[]): ShouldAssertion; + alwaysCalledWithExactly(...args: any[]): ShouldAssertion; + alwaysCalledWithMatch(...args: any[]): ShouldAssertion; alwaysCalledWithNew(): ShouldAssertion; alwaysThrew(exception?: any): ShouldAssertion; callCount(count: number): ShouldAssertion; @@ -152,12 +154,12 @@ interface ShouldAssertion { calledOnce(): ShouldAssertion; calledTwice(): ShouldAssertion; calledThrice(): ShouldAssertion; - calledWith(...arguments: any[]): ShouldAssertion; - calledWithExactly(...arguments: any[]): ShouldAssertion; - calledWithMatch(...arguments: any[]): ShouldAssertion; + calledWith(...args: any[]): ShouldAssertion; + calledWithExactly(...args: any[]): ShouldAssertion; + calledWithMatch(...args: any[]): ShouldAssertion; calledWithNew(): ShouldAssertion; - neverCalledWith(...arguments: any[]): ShouldAssertion; - neverCalledWithMatch(...arguments: any[]): ShouldAssertion; + neverCalledWith(...args: any[]): ShouldAssertion; + neverCalledWithMatch(...args: any[]): ShouldAssertion; threw(exception?: any): ShouldAssertion; // aliases @@ -178,20 +180,20 @@ interface ShouldAssertion { undefined(): ShouldAssertion; } -interface ShouldInternal { +export interface ShouldInternal { // should.js's extras exist(actual: any, msg?: string): void; exists(actual: any, msg?: string): void; not: ShouldInternal; } -interface Assertion { +export interface Assertion { add(name: string, func: Function): void; addChain(name: string, onCall?: Function): void; alias(from: string, to: string): void; } -interface Internal extends ShouldInternal { +export interface Internal extends ShouldInternal { (obj: any): ShouldAssertion; // Simple utility function for a bit more easier should assertion extension @@ -213,11 +215,6 @@ interface Internal extends ShouldInternal { inspect(value: any, obj: any): any; } -declare var should: Internal; -interface Window { - Should: Internal; -} - -declare module 'should' { - export = should; -} +/* The global `should` variable and the `Window.Should` property are not +declared here - this module wraps `should/as-function`, which deliberately +leaves the global scope and `Object.prototype` untouched. */