diff --git a/.env.test b/.env.test index 5d0e32a..0e7857e 100644 --- a/.env.test +++ b/.env.test @@ -2,3 +2,4 @@ PORT=3000 LOG_LEVEL=mute AC_JWKS_URL=http://auth.example.com ASSERT_CONFIGS_ON_START=false +START_AUX_HTTP_SERVER_ON_START=false \ No newline at end of file diff --git a/docs/application.md b/docs/application.md index 2a67d67..46e147a 100644 --- a/docs/application.md +++ b/docs/application.md @@ -75,6 +75,6 @@ Note: scope simply refers to and individual `Mesh` instance. For example, when r When application is processing HTTP requests, a number of request-scoped components can be bound to Router classes: -- `AcAuth` (Automation Cloud identity and authorisation data) +- `AuthContext` (Automation Cloud identity and authorisation data) - `KoaContext` (bound by string `"KoaContext"` service identifier) — [Koa](https://koajs.org) context object - `Logger` (rebound to `RequestLogger`) which includes request-specific data diff --git a/docs/auth.md b/docs/auth.md index 113f02d..8d3dd7d 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -6,12 +6,28 @@ Automation Cloud infrastructure features "highly sophisticated" request authenti Node Framework does its best to abstract away all the complexity, allowing apps to focus on what they are supposed to be doing. -`AcAuth` object exposes identity information of current request, as well as convenience methods for request authorisation. +The framework exports a default implementation of auth provider for Automation Cloud. Although, (starting from version 16.x) this service is optional and needs explicity installation (including its dependencies, i.e. a JWT service). + +```ts +class App extends Application { + + override createGlobalScope() { + // starting from version 16.x, the following lines are required to use AC auth (default) service implementation + const mesh = super.createGlobalScope(); + mesh.service(AuthProvider, AcAuthProvider); + mesh.service(JwtService, AutomationCloudJwtService); + } +} +``` + +`AuthContext` object exposes identity information of current request, as well as convenience methods for request authorisation. + +`AuthToken` object type is based on the implementation of the `AuthProvider` registered. You may specify any auth token object type, i.e `AuthContext`. In order to retrieve specific data from a auth token type, you need to retrieve it from the auth context object with `authContenxt.getToken()`. ```ts export class MyRouter extends Router { - @dep() auth!: AcAuth; + @dep() auth!: AuthContext; @Middleware() async authorise() { @@ -25,7 +41,7 @@ export class MyRouter extends Router { }) async helloOrg() { // throws 403 when organisationId cannot be extracted from request details - const organisationId = this.auth.requireOrganisationId(); + const organisationId = this.auth.getAuthToken().requireOrganisationId(); return { message: '👋 Hello ' + organisationId }; } @@ -35,7 +51,7 @@ export class MyRouter extends Router { }) async helloServiceAccount() { // throws 403 when serviceAccount info cannot be extracted from request details - const serviceAccountId = this.auth.requireServiceAccountId(); + const serviceAccountId = this.auth.getAuthToken().requireServiceAccountId(); return { message: '👋 Hello ' + serviceAccountId }; } } @@ -43,20 +59,22 @@ export class MyRouter extends Router { ## Mocking auth in tests -In integration tests it is useful to mock `AcAuth` by providing a custom implementation of `AcAuthProvider`: +In integration tests it is useful to mock `AuthContext` by providing a custom implementation of `AcAuthProvider`: ```ts class App extends Application { - override createHttpRequestScope() { - const mesh = super.createHttpRequestScope(); + override createGlobalScope() { + const mesh = super.createGlobalScope(); mesh.constant(AcAuthProvider, { async provide() { - return new AcAuth({ - authenticated: true, - organisationId: 'my-fake-org-id', - serviceAccountId: 'my-face-service-account-id', - }); + return new AuthContext( + new AcAuth({ + authenticated: true, + organisationId: 'my-fake-org-id', + serviceAccountId: 'my-face-service-account-id', + }) + ); } }); return mesh; diff --git a/docs/metrics.md b/docs/metrics.md index a3669c6..142d976 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -1,6 +1,6 @@ ## Metrics -Framework provides a standard facility to collect and expose Prometheus metrics. +Framework provides (global) standard metrics and that can be extended with `@nodescript/metrics`, to collect and expose Prometheus metrics. Following main use cases are supported: @@ -12,15 +12,15 @@ Following main use cases are supported: Create a `Metrics` class in `src/main/metrics.ts` and define your counters, histograms and gauges. ```ts -import { MetricsRegistry } from '@ubio/framework'; +import { metric, CounterMetric } from '@nodescript/metrics'; -export class Metrics extends MetricsRegistry { +export class Metrics { - dataCacheKeysRequested = this.counter('api_dataset_cache_keys_requested_total', + @metric() dataCacheKeysRequested = new CounterMetric('api_dataset_cache_keys_requested_total', 'Data Cache: Keys Requested'); - dataCacheKeysRetrieved = this.counter('api_dataset_cache_keys_retrieved_total', + @metric() dataCacheKeysRetrieved = new CounterMetric('api_dataset_cache_keys_retrieved_total', 'Data Cache: Keys Retrieved'); - dataCacheKeysStored = this.counter('api_dataset_cache_keys_stored_total', + @metric() dataCacheKeysStored = new CounterMetric('api_dataset_cache_keys_stored_total', 'Data Cache: Keys Stored'); } @@ -43,13 +43,13 @@ export class App extends Application { This will do two things: 1. bind `Metrics` to self in singleton scope -2. bind that instance to `MetricsRegistry` which is used to aggregate mutliple registries (including the global registry) +2. make it available to `@nodescript/metrics` so the app can expose the reports over http. Finally, for observing the metrics in your classes, simply `@dep() metrics: Metrics` and start using the counters, gauges and histograms you have defined. ### Global metrics -Global metrics are collected in `GlobalMetricsRegistry` which is automatically bound to all applications (you don't have to specify anything). +Global metrics are collected in `GlobalMetrics` which is automatically bound to all applications (you don't have to specify anything). Global metrics include `@MeasureAsync` decorator which can be used to annotate any Promise-returning method so that its performance can be reported and analyse. @@ -69,4 +69,10 @@ In this example, `app_method_latencies_seconds{class="MyClass",method="doWork"}` ### Metrics endpoint -MetricsRouter is automatically bound to all applications (you don't have to do anything). It serves `GET /metrics` endpoint and reports metrics from all registered registries. +By default, the application will run a http server (in a different port) that handles requests to `GET /metrics` endpoint, unless configured otherwise (i.e. `START_AUX_HTTP_SERVER_ON_START=false` is set). So, you don't have to do anything to expose the metrics reports. + +This endpoint reports metrics from all registered ones, which are: + +- class member decorated with `@metric()`; +- metric type instantiated (i.e. `new CounterMetric()`); +- and its class is bound to mesh container as a service (i.e `mesh.service(Metrics)`). diff --git a/docs/structure.md b/docs/structure.md index d149e1f..b5830f7 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -53,7 +53,7 @@ export class App extends Application { await this.mongoDb.client.connect(); // Add other code to execute on application startup await this.httpServer.startServer(); - }); + }; override async afterStop() { await this.httpServer.stopServer(); diff --git a/package-lock.json b/package-lock.json index 0ba1c91..189efcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,19 +10,15 @@ "license": "ISC", "dependencies": { "@koa/cors": "^5.0.0", - "@nodescript/logger": "^2.0.3", + "@nodescript/errors": "^1.2.0", + "@nodescript/http-server": "^2.10.1", + "@nodescript/logger": "^2.0.6", + "@nodescript/metrics": "^1.7.1", + "@nodescript/microframework": "^1.15.3", "@nodescript/pathmatcher": "^1.0.2", - "@types/koa": "^2.11.8", - "@types/koa__cors": "^3.0.2", - "@types/koa-conditional-get": "^2.0.0", - "@types/koa-etag": "^3.0.0", - "@types/node-fetch": "^2.5.8", - "@types/stoppable": "^1.1.0", - "@types/uuid": "^8.3.0", "@ubio/request": "^3.5.0", "ajv": "^8.1.0", "ajv-formats": "^2.0.2", - "chalk": "^4.1.0", "commander": "^7.2.0", "dotenv": "^16.3.1", "jsonwebtoken": "^9.0.0", @@ -31,9 +27,8 @@ "koa-compress": "^5.1.1", "koa-conditional-get": "^3.0.0", "koa-etag": "^4.0.0", - "mesh-config": "1.1.0", - "mesh-ioc": "^3.2.0", - "node-fetch": "^2.6.0", + "mesh-config": "^1.2.1", + "mesh-ioc": "^4.1.0", "reflect-metadata": "^0.1.13", "stoppable": "^1.1.0", "uuid": "^8.3.2" @@ -45,12 +40,17 @@ }, "devDependencies": { "@nodescript/eslint-config": "^1.0.4", - "@types/chalk": "^2.2.0", "@types/jsonwebtoken": "^8.5.0", + "@types/koa": "^2.11.8", + "@types/koa__cors": "^3.0.2", "@types/koa-compress": "^4.0.1", + "@types/koa-conditional-get": "^2.0.0", + "@types/koa-etag": "^3.0.0", "@types/mocha": "^8.2.0", "@types/node": "^14.14.32", + "@types/stoppable": "^1.1.0", "@types/supertest": "^2.0.10", + "@types/uuid": "^8.3.0", "eslint": "^8.24.0", "mocha": "^10.0.0", "mongodb": "^6.10.0", @@ -570,6 +570,12 @@ "node": ">= 8" } }, + "node_modules/@nodescript/errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nodescript/errors/-/errors-1.2.0.tgz", + "integrity": "sha512-QWrH/tFASZtwD3KmSBFPExVbc1cY2pRHKF7IocCj5khgC6HBoPjuOu/3Dnbkm23DWiWbFgW/Y3xtDjrghMPgpQ==", + "license": "ISC" + }, "node_modules/@nodescript/eslint-config": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@nodescript/eslint-config/-/eslint-config-1.0.4.tgz", @@ -584,20 +590,80 @@ "eslint-plugin-vue": "^9.5.1" } }, + "node_modules/@nodescript/http-server": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@nodescript/http-server/-/http-server-2.10.1.tgz", + "integrity": "sha512-LyROIIJJ98eKYRjhIniZERz7M8DcTNnECKcmzVW2XEHEFlh5zXdy5dOOuwQv6vKqQzWcE/LNU0yInzxMdmdEWQ==", + "license": "ISC", + "dependencies": { + "@nodescript/errors": "^1.2.0", + "@nodescript/logger": "^2.0.6", + "@nodescript/metrics": "^1.7.1", + "@nodescript/pathmatcher": "^1.3.0", + "@nodescript/protocomm": "^1.1.0", + "mesh-config": "^1.2.1", + "mesh-decorators": "^1.1.2", + "mesh-ioc": "^4.1.0", + "nanoevent": "^1.0.0" + } + }, "node_modules/@nodescript/logger": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodescript/logger/-/logger-2.0.3.tgz", - "integrity": "sha512-p7/z7RXb6Vo1UD8UGpGuymCqAIg6Wo33KCFKijU06HUD88DNe9LjByFGBG6IwLjzjUt2wGcKl6KBf6OdGVufYQ==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@nodescript/logger/-/logger-2.0.6.tgz", + "integrity": "sha512-jTS4IQtUnr8srL3DtCA7JCxTh7jlx/m0QC7+vI9g7R3RVGmjcMmbQPnsbGEPI8DUG8oAWXFqis7r7Nye6gcvuw==", + "license": "ISC" + }, + "node_modules/@nodescript/metrics": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@nodescript/metrics/-/metrics-1.7.1.tgz", + "integrity": "sha512-MUwJcKL52XRai35WRfNyAQ/egKD/me2zoEfm1QbIGm1epjlB+ZvTs8qtciekTLLxsx/BlJd3becAsXpmZhXguQ==", + "dependencies": { + "mesh-decorators": "^1.1.2", + "mesh-ioc": "^4.1.0" + } + }, + "node_modules/@nodescript/microframework": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@nodescript/microframework/-/microframework-1.15.3.tgz", + "integrity": "sha512-lB/fg7UmYRw2PkA/EiAXqQ38OAPulEY08+cdMu18xp0FVjT1HorAEbgMH5RMdZDaDE+KvlJVSeBqZ8SfVxWI8A==", + "license": "ISC", + "dependencies": { + "@nodescript/errors": "^1.2.0", + "@nodescript/http-server": "^2.10.1", + "@nodescript/logger": "^2.0.6", + "@nodescript/metrics": "^1.7.1", + "airtight": "^5.7.2", + "dotenv": "^16.0.3", + "jsonwebtoken": "^9.0.2", + "mesh-config": "^1.2.1", + "mesh-ioc": "^4.1.0", + "reflect-metadata": "^0.1.13" + } }, "node_modules/@nodescript/pathmatcher": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@nodescript/pathmatcher/-/pathmatcher-1.0.2.tgz", - "integrity": "sha512-CLUKBYfP4TCbivmcpAbZgMTf1zjq/qN5r/3KdkRgC7sTjop/GalZycdAmijtNEkFn5w0QHP2uXJOWpLKBscTVw==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@nodescript/pathmatcher/-/pathmatcher-1.3.0.tgz", + "integrity": "sha512-Jd7kkEkGQXKmkAt0RPTRhrlS3o33GkwX9MuJBMksH5vifalFKLIXzgg0dgfdRGwGKbDZDud+v0MSt4trgZ+2gQ==", + "license": "ISC" + }, + "node_modules/@nodescript/protocomm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nodescript/protocomm/-/protocomm-1.2.0.tgz", + "integrity": "sha512-mPbSubJ8OvxfHKHnYQtaLDuzNEGTTzDREPxjXuvomcjbvyBtGjl0Kt1e+QAYYF2Fl4dHE0cEn7ik6jzTg7Umsg==", + "license": "ISC", + "dependencies": { + "airtight": "^5.7.2", + "nanoevent": "^1.0.0" + }, + "engines": { + "node": ">=18" + } }, "node_modules/@types/accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -606,25 +672,17 @@ "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" } }, - "node_modules/@types/chalk": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz", - "integrity": "sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==", - "deprecated": "This is a stub types definition for chalk (https://github.com/chalk/chalk). chalk provides its own type definitions, so you don't need @types/chalk installed!", - "dev": true, - "dependencies": { - "chalk": "*" - } - }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -632,7 +690,8 @@ "node_modules/@types/content-disposition": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz", - "integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==" + "integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==", + "dev": true }, "node_modules/@types/cookiejar": { "version": "2.1.2", @@ -644,6 +703,7 @@ "version": "0.7.7", "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz", "integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==", + "dev": true, "dependencies": { "@types/connect": "*", "@types/express": "*", @@ -655,6 +715,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/@types/etag/-/etag-1.8.1.tgz", "integrity": "sha512-bsKkeSqN7HYyYntFRAmzcwx/dKW4Wa+KVMTInANlI72PWLQmOpZu96j0OqHZGArW4VQwCmJPteQlXaUDeOB0WQ==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -663,6 +724,7 @@ "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", @@ -674,6 +736,7 @@ "version": "4.17.31", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -691,12 +754,14 @@ "node_modules/@types/http-assert": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz", - "integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==" + "integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==", + "dev": true }, "node_modules/@types/http-errors": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.2.tgz", - "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==" + "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==", + "dev": true }, "node_modules/@types/json-schema": { "version": "7.0.11", @@ -722,12 +787,14 @@ "node_modules/@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", - "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" + "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==", + "dev": true }, "node_modules/@types/koa": { "version": "2.13.5", "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.5.tgz", "integrity": "sha512-HSUOdzKz3by4fnqagwthW/1w/yJspTgppyyalPVbgZf8jQWvdIXcVW5h2DGtw4zYntOaeRGx49r1hxoPWrD4aA==", + "dev": true, "dependencies": { "@types/accepts": "*", "@types/content-disposition": "*", @@ -743,6 +810,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/@types/koa__cors/-/koa__cors-3.3.0.tgz", "integrity": "sha512-FUN8YxcBakIs+walVe3+HcNP+Bxd0SB8BJHBWkglZ5C1XQWljlKcEFDG/dPiCIqwVCUbc5X0nYDlH62uEhdHMA==", + "dev": true, "dependencies": { "@types/koa": "*" } @@ -751,6 +819,7 @@ "version": "3.2.5", "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", "integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==", + "dev": true, "dependencies": { "@types/koa": "*" } @@ -769,6 +838,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/koa-conditional-get/-/koa-conditional-get-2.0.0.tgz", "integrity": "sha512-kzEIakwXkkgiJhQPR/IudINqX+xB2gVS+lJWDSEUP3cMYflcX470OOWrUEqOXlc2cCAOyznMo7fFmwfnvd9wyw==", + "dev": true, "dependencies": { "@types/koa": "*" } @@ -777,6 +847,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/koa-etag/-/koa-etag-3.0.0.tgz", "integrity": "sha512-gXQUtKGEnCy0sZLG+uE3wL4mvY1CBPcb6ECjpAoD8RGYy/8ACY1B084k8LTFPIdVcmy7GD6Y4n3up3jnupofcQ==", + "dev": true, "dependencies": { "@types/etag": "*", "@types/koa": "*" @@ -785,7 +856,8 @@ "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true }, "node_modules/@types/mocha": { "version": "8.2.3", @@ -810,17 +882,20 @@ "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true }, "node_modules/@types/range-parser": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true }, "node_modules/@types/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, "dependencies": { "@types/mime": "*", "@types/node": "*" @@ -830,6 +905,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/stoppable/-/stoppable-1.1.1.tgz", "integrity": "sha512-b8N+fCADRIYYrGZOcmOR8ZNBOqhktWTB/bMUl5LvGtT201QKJZOOH5UsFyI3qtteM6ZAJbJqZoBcLqqxKIwjhw==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -856,7 +932,8 @@ "node_modules/@types/uuid": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", @@ -1194,6 +1271,12 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/airtight": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/airtight/-/airtight-5.7.2.tgz", + "integrity": "sha512-Ho9mp/c2bQhIVyT4qvsiPDIsOGE+JXudQShP8pbFnpgQJ2ufk3Ti1RJIZPPuI+ZkxOY6wRlH/A7rNiw1TjewDQ==", + "license": "ISC" + }, "node_modules/ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", @@ -1274,6 +1357,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1543,6 +1627,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1655,6 +1740,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1665,7 +1751,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", @@ -4910,6 +4997,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -5458,14 +5546,21 @@ } }, "node_modules/jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", "dependencies": { "jws": "^3.2.2", - "lodash": "^4.17.21", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">=12", @@ -5683,7 +5778,44 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5691,6 +5823,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -5762,18 +5900,45 @@ } }, "node_modules/mesh-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mesh-config/-/mesh-config-1.1.0.tgz", - "integrity": "sha512-ZDrzJJmdjOXKyABcTI/mt+2yQUDH4XiUNRto7DU2zP4pIVGsTrpwArwSmtaVxt7S6ufwTeJJ7vfYIEXKh2DmSg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/mesh-config/-/mesh-config-1.2.1.tgz", + "integrity": "sha512-OSV2iZE/2hTcUEt3Bw0+JNmMwdiHLB9ksJHas2dCEXADVF/57MotpUCtqrWDMUBx88SWmM3Zv2c8P/rCobJyJQ==", + "license": "ISC", "dependencies": { - "mesh-ioc": "^3.2.0", - "reflect-utils": "^1.0.3" + "mesh-ioc": "^4.1.0", + "reflect-utils": "^1.1.1" + } + }, + "node_modules/mesh-config/node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/mesh-config/node_modules/reflect-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/reflect-utils/-/reflect-utils-1.1.1.tgz", + "integrity": "sha512-b1AKtKIHIQ9jYzfv5Xgi4/QGIad4qGjEX5vTZM4MQfzuWcEfyS6tDjg8NR1QZzVzMUDPvcxPmDgX5PudZ1dGTw==", + "license": "ISC", + "peerDependencies": { + "reflect-metadata": "^0.2.2" + } + }, + "node_modules/mesh-decorators": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/mesh-decorators/-/mesh-decorators-1.1.2.tgz", + "integrity": "sha512-tbsaiMOj9XaaL9vcvK1Dm/8GMQTQuDJGCz3A89612yEOyC34Bcb1ZFlJQevEn+HFbhTlkVXO3D1qXkO6YsfA+w==", + "license": "ISC", + "dependencies": { + "mesh-ioc": "^4.1.0" } }, "node_modules/mesh-ioc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mesh-ioc/-/mesh-ioc-3.2.0.tgz", - "integrity": "sha512-kLVoKX8mwTqy8MjlmyrSr+dpsFLSGJlC1HzfRcEdRR+ip4rWjEQHvwxd/2HFljFKoMyvXdB3J02fPbsVNEKZRg==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mesh-ioc/-/mesh-ioc-4.1.0.tgz", + "integrity": "sha512-7iNWDv6lm7u+pUK1xaG0193fnh8iulISUK6gunYtFsyspSpd9t6azRDbU42T6CsjsD51/a08m8d27sngIjnrwg==", + "license": "ISC" }, "node_modules/methods": { "version": "1.1.2", @@ -6017,6 +6182,12 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nanoevent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nanoevent/-/nanoevent-1.0.0.tgz", + "integrity": "sha512-nxipFgI/EuMStQJfL7kW7avXnP0Fyq5U9MIzvGrP7mMTmDNDtHaqtN+Br6uJFXB9AlILCbPPeGK8PEXZ/UKubg==", + "license": "ISC" + }, "node_modules/nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", @@ -6865,14 +7036,6 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, - "node_modules/reflect-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/reflect-utils/-/reflect-utils-1.0.3.tgz", - "integrity": "sha512-MzHKBN9UzDswy2kOoyTfwU/3jveH7c7j9jsfLaEqNfJpR3f3dhDJ9xhI7YWnB5LtvwfE5uELti2R6qac2WpyKA==", - "peerDependencies": { - "reflect-metadata": "^0.1.13" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -7537,6 +7700,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -8524,6 +8688,11 @@ "fastq": "^1.6.0" } }, + "@nodescript/errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nodescript/errors/-/errors-1.2.0.tgz", + "integrity": "sha512-QWrH/tFASZtwD3KmSBFPExVbc1cY2pRHKF7IocCj5khgC6HBoPjuOu/3Dnbkm23DWiWbFgW/Y3xtDjrghMPgpQ==" + }, "@nodescript/eslint-config": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@nodescript/eslint-config/-/eslint-config-1.0.4.tgz", @@ -8538,20 +8707,72 @@ "eslint-plugin-vue": "^9.5.1" } }, + "@nodescript/http-server": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@nodescript/http-server/-/http-server-2.10.1.tgz", + "integrity": "sha512-LyROIIJJ98eKYRjhIniZERz7M8DcTNnECKcmzVW2XEHEFlh5zXdy5dOOuwQv6vKqQzWcE/LNU0yInzxMdmdEWQ==", + "requires": { + "@nodescript/errors": "^1.2.0", + "@nodescript/logger": "^2.0.6", + "@nodescript/metrics": "^1.7.1", + "@nodescript/pathmatcher": "^1.3.0", + "@nodescript/protocomm": "^1.1.0", + "mesh-config": "^1.2.1", + "mesh-decorators": "^1.1.2", + "mesh-ioc": "^4.1.0", + "nanoevent": "^1.0.0" + } + }, "@nodescript/logger": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodescript/logger/-/logger-2.0.3.tgz", - "integrity": "sha512-p7/z7RXb6Vo1UD8UGpGuymCqAIg6Wo33KCFKijU06HUD88DNe9LjByFGBG6IwLjzjUt2wGcKl6KBf6OdGVufYQ==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@nodescript/logger/-/logger-2.0.6.tgz", + "integrity": "sha512-jTS4IQtUnr8srL3DtCA7JCxTh7jlx/m0QC7+vI9g7R3RVGmjcMmbQPnsbGEPI8DUG8oAWXFqis7r7Nye6gcvuw==" + }, + "@nodescript/metrics": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@nodescript/metrics/-/metrics-1.7.1.tgz", + "integrity": "sha512-MUwJcKL52XRai35WRfNyAQ/egKD/me2zoEfm1QbIGm1epjlB+ZvTs8qtciekTLLxsx/BlJd3becAsXpmZhXguQ==", + "requires": { + "mesh-decorators": "^1.1.2", + "mesh-ioc": "^4.1.0" + } + }, + "@nodescript/microframework": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@nodescript/microframework/-/microframework-1.15.3.tgz", + "integrity": "sha512-lB/fg7UmYRw2PkA/EiAXqQ38OAPulEY08+cdMu18xp0FVjT1HorAEbgMH5RMdZDaDE+KvlJVSeBqZ8SfVxWI8A==", + "requires": { + "@nodescript/errors": "^1.2.0", + "@nodescript/http-server": "^2.10.1", + "@nodescript/logger": "^2.0.6", + "@nodescript/metrics": "^1.7.1", + "airtight": "^5.7.2", + "dotenv": "^16.0.3", + "jsonwebtoken": "^9.0.2", + "mesh-config": "^1.2.1", + "mesh-ioc": "^4.1.0", + "reflect-metadata": "^0.1.13" + } }, "@nodescript/pathmatcher": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@nodescript/pathmatcher/-/pathmatcher-1.0.2.tgz", - "integrity": "sha512-CLUKBYfP4TCbivmcpAbZgMTf1zjq/qN5r/3KdkRgC7sTjop/GalZycdAmijtNEkFn5w0QHP2uXJOWpLKBscTVw==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@nodescript/pathmatcher/-/pathmatcher-1.3.0.tgz", + "integrity": "sha512-Jd7kkEkGQXKmkAt0RPTRhrlS3o33GkwX9MuJBMksH5vifalFKLIXzgg0dgfdRGwGKbDZDud+v0MSt4trgZ+2gQ==" + }, + "@nodescript/protocomm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nodescript/protocomm/-/protocomm-1.2.0.tgz", + "integrity": "sha512-mPbSubJ8OvxfHKHnYQtaLDuzNEGTTzDREPxjXuvomcjbvyBtGjl0Kt1e+QAYYF2Fl4dHE0cEn7ik6jzTg7Umsg==", + "requires": { + "airtight": "^5.7.2", + "nanoevent": "^1.0.0" + } }, "@types/accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "dev": true, "requires": { "@types/node": "*" } @@ -8560,24 +8781,17 @@ "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, "requires": { "@types/connect": "*", "@types/node": "*" } }, - "@types/chalk": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz", - "integrity": "sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==", - "dev": true, - "requires": { - "chalk": "*" - } - }, "@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, "requires": { "@types/node": "*" } @@ -8585,7 +8799,8 @@ "@types/content-disposition": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz", - "integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==" + "integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==", + "dev": true }, "@types/cookiejar": { "version": "2.1.2", @@ -8597,6 +8812,7 @@ "version": "0.7.7", "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz", "integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==", + "dev": true, "requires": { "@types/connect": "*", "@types/express": "*", @@ -8608,6 +8824,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/@types/etag/-/etag-1.8.1.tgz", "integrity": "sha512-bsKkeSqN7HYyYntFRAmzcwx/dKW4Wa+KVMTInANlI72PWLQmOpZu96j0OqHZGArW4VQwCmJPteQlXaUDeOB0WQ==", + "dev": true, "requires": { "@types/node": "*" } @@ -8616,6 +8833,7 @@ "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", @@ -8627,6 +8845,7 @@ "version": "4.17.31", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, "requires": { "@types/node": "*", "@types/qs": "*", @@ -8644,12 +8863,14 @@ "@types/http-assert": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz", - "integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==" + "integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==", + "dev": true }, "@types/http-errors": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.2.tgz", - "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==" + "integrity": "sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==", + "dev": true }, "@types/json-schema": { "version": "7.0.11", @@ -8675,12 +8896,14 @@ "@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", - "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" + "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==", + "dev": true }, "@types/koa": { "version": "2.13.5", "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.5.tgz", "integrity": "sha512-HSUOdzKz3by4fnqagwthW/1w/yJspTgppyyalPVbgZf8jQWvdIXcVW5h2DGtw4zYntOaeRGx49r1hxoPWrD4aA==", + "dev": true, "requires": { "@types/accepts": "*", "@types/content-disposition": "*", @@ -8696,6 +8919,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/@types/koa__cors/-/koa__cors-3.3.0.tgz", "integrity": "sha512-FUN8YxcBakIs+walVe3+HcNP+Bxd0SB8BJHBWkglZ5C1XQWljlKcEFDG/dPiCIqwVCUbc5X0nYDlH62uEhdHMA==", + "dev": true, "requires": { "@types/koa": "*" } @@ -8704,6 +8928,7 @@ "version": "3.2.5", "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", "integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==", + "dev": true, "requires": { "@types/koa": "*" } @@ -8722,6 +8947,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/koa-conditional-get/-/koa-conditional-get-2.0.0.tgz", "integrity": "sha512-kzEIakwXkkgiJhQPR/IudINqX+xB2gVS+lJWDSEUP3cMYflcX470OOWrUEqOXlc2cCAOyznMo7fFmwfnvd9wyw==", + "dev": true, "requires": { "@types/koa": "*" } @@ -8730,6 +8956,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/koa-etag/-/koa-etag-3.0.0.tgz", "integrity": "sha512-gXQUtKGEnCy0sZLG+uE3wL4mvY1CBPcb6ECjpAoD8RGYy/8ACY1B084k8LTFPIdVcmy7GD6Y4n3up3jnupofcQ==", + "dev": true, "requires": { "@types/etag": "*", "@types/koa": "*" @@ -8738,7 +8965,8 @@ "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true }, "@types/mocha": { "version": "8.2.3", @@ -8763,17 +8991,20 @@ "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true }, "@types/range-parser": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true }, "@types/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, "requires": { "@types/mime": "*", "@types/node": "*" @@ -8783,6 +9014,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/stoppable/-/stoppable-1.1.1.tgz", "integrity": "sha512-b8N+fCADRIYYrGZOcmOR8ZNBOqhktWTB/bMUl5LvGtT201QKJZOOH5UsFyI3qtteM6ZAJbJqZoBcLqqxKIwjhw==", + "dev": true, "requires": { "@types/node": "*" } @@ -8809,7 +9041,8 @@ "@types/uuid": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true }, "@types/webidl-conversions": { "version": "7.0.3", @@ -9029,6 +9262,11 @@ "dev": true, "requires": {} }, + "airtight": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/airtight/-/airtight-5.7.2.tgz", + "integrity": "sha512-Ho9mp/c2bQhIVyT4qvsiPDIsOGE+JXudQShP8pbFnpgQJ2ufk3Ti1RJIZPPuI+ZkxOY6wRlH/A7rNiw1TjewDQ==" + }, "ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", @@ -9081,6 +9319,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -9282,6 +9521,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -9366,6 +9606,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -9373,7 +9614,8 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "combined-stream": { "version": "1.0.8", @@ -11844,7 +12086,8 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "has-property-descriptors": { "version": "1.0.0", @@ -12236,14 +12479,20 @@ } }, "jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "requires": { "jws": "^3.2.2", - "lodash": "^4.17.21", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "dependencies": { "lru-cache": { @@ -12423,7 +12672,38 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, "lodash.merge": { "version": "4.6.2", @@ -12431,6 +12711,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -12484,18 +12769,40 @@ "dev": true }, "mesh-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mesh-config/-/mesh-config-1.1.0.tgz", - "integrity": "sha512-ZDrzJJmdjOXKyABcTI/mt+2yQUDH4XiUNRto7DU2zP4pIVGsTrpwArwSmtaVxt7S6ufwTeJJ7vfYIEXKh2DmSg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/mesh-config/-/mesh-config-1.2.1.tgz", + "integrity": "sha512-OSV2iZE/2hTcUEt3Bw0+JNmMwdiHLB9ksJHas2dCEXADVF/57MotpUCtqrWDMUBx88SWmM3Zv2c8P/rCobJyJQ==", + "requires": { + "mesh-ioc": "^4.1.0", + "reflect-utils": "^1.1.1" + }, + "dependencies": { + "reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "peer": true + }, + "reflect-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/reflect-utils/-/reflect-utils-1.1.1.tgz", + "integrity": "sha512-b1AKtKIHIQ9jYzfv5Xgi4/QGIad4qGjEX5vTZM4MQfzuWcEfyS6tDjg8NR1QZzVzMUDPvcxPmDgX5PudZ1dGTw==", + "requires": {} + } + } + }, + "mesh-decorators": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/mesh-decorators/-/mesh-decorators-1.1.2.tgz", + "integrity": "sha512-tbsaiMOj9XaaL9vcvK1Dm/8GMQTQuDJGCz3A89612yEOyC34Bcb1ZFlJQevEn+HFbhTlkVXO3D1qXkO6YsfA+w==", "requires": { - "mesh-ioc": "^3.2.0", - "reflect-utils": "^1.0.3" + "mesh-ioc": "^4.1.0" } }, "mesh-ioc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mesh-ioc/-/mesh-ioc-3.2.0.tgz", - "integrity": "sha512-kLVoKX8mwTqy8MjlmyrSr+dpsFLSGJlC1HzfRcEdRR+ip4rWjEQHvwxd/2HFljFKoMyvXdB3J02fPbsVNEKZRg==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mesh-ioc/-/mesh-ioc-4.1.0.tgz", + "integrity": "sha512-7iNWDv6lm7u+pUK1xaG0193fnh8iulISUK6gunYtFsyspSpd9t6azRDbU42T6CsjsD51/a08m8d27sngIjnrwg==" }, "methods": { "version": "1.1.2", @@ -12659,6 +12966,11 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nanoevent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nanoevent/-/nanoevent-1.0.0.tgz", + "integrity": "sha512-nxipFgI/EuMStQJfL7kW7avXnP0Fyq5U9MIzvGrP7mMTmDNDtHaqtN+Br6uJFXB9AlILCbPPeGK8PEXZ/UKubg==" + }, "nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", @@ -13296,12 +13608,6 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, - "reflect-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/reflect-utils/-/reflect-utils-1.0.3.tgz", - "integrity": "sha512-MzHKBN9UzDswy2kOoyTfwU/3jveH7c7j9jsfLaEqNfJpR3f3dhDJ9xhI7YWnB5LtvwfE5uELti2R6qac2WpyKA==", - "requires": {} - }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -13801,6 +14107,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } diff --git a/package.json b/package.json index 48f50ca..7cd54f4 100644 --- a/package.json +++ b/package.json @@ -42,12 +42,17 @@ "homepage": "https://github.com/ubio/node-framework#readme", "devDependencies": { "@nodescript/eslint-config": "^1.0.4", - "@types/chalk": "^2.2.0", "@types/jsonwebtoken": "^8.5.0", + "@types/koa": "^2.11.8", + "@types/koa__cors": "^3.0.2", "@types/koa-compress": "^4.0.1", + "@types/koa-conditional-get": "^2.0.0", + "@types/koa-etag": "^3.0.0", "@types/mocha": "^8.2.0", "@types/node": "^14.14.32", + "@types/stoppable": "^1.1.0", "@types/supertest": "^2.0.10", + "@types/uuid": "^8.3.0", "eslint": "^8.24.0", "mocha": "^10.0.0", "mongodb": "^6.10.0", @@ -59,19 +64,15 @@ }, "dependencies": { "@koa/cors": "^5.0.0", - "@nodescript/logger": "^2.0.3", + "@nodescript/errors": "^1.2.0", + "@nodescript/http-server": "^2.10.1", + "@nodescript/logger": "^2.0.6", + "@nodescript/metrics": "^1.7.1", + "@nodescript/microframework": "^1.15.3", "@nodescript/pathmatcher": "^1.0.2", - "@types/koa": "^2.11.8", - "@types/koa__cors": "^3.0.2", - "@types/koa-conditional-get": "^2.0.0", - "@types/koa-etag": "^3.0.0", - "@types/node-fetch": "^2.5.8", - "@types/stoppable": "^1.1.0", - "@types/uuid": "^8.3.0", "@ubio/request": "^3.5.0", "ajv": "^8.1.0", "ajv-formats": "^2.0.2", - "chalk": "^4.1.0", "commander": "^7.2.0", "dotenv": "^16.3.1", "jsonwebtoken": "^9.0.0", @@ -80,9 +81,8 @@ "koa-compress": "^5.1.1", "koa-conditional-get": "^3.0.0", "koa-etag": "^4.0.0", - "mesh-config": "1.1.0", - "mesh-ioc": "^3.2.0", - "node-fetch": "^2.6.0", + "mesh-config": "^1.2.1", + "mesh-ioc": "^4.1.0", "reflect-metadata": "^0.1.13", "stoppable": "^1.1.0", "uuid": "^8.3.2" diff --git a/src/main/ac-auth.ts b/src/main/ac-auth.ts index 67c23ae..6347b0d 100644 --- a/src/main/ac-auth.ts +++ b/src/main/ac-auth.ts @@ -58,16 +58,6 @@ export class AcAuth { this.actor = spec.jwtContext == null ? null : this.parseActor(spec.jwtContext); } - isAuthenticated() { - return this.actor != null; - } - - checkAuthenticated(): void { - if (!this.isAuthenticated()) { - throw new AuthenticationError(); - } - } - getOrganisationId(): string | null { return this.actor?.organisationId ?? null; } @@ -168,11 +158,6 @@ export class AcAuth { } } -export class AuthenticationError extends ClientError { - override status = 401; - override message = 'Authentication is required'; -} - export class AccessForbidden extends ClientError { override status = 403; } diff --git a/src/main/application.ts b/src/main/application.ts index 6ecb104..3ea5de0 100644 --- a/src/main/application.ts +++ b/src/main/application.ts @@ -1,58 +1,40 @@ import { Logger } from '@nodescript/logger'; -import dotenv from 'dotenv'; -import { Config, config, ConfigError, getMeshConfigs, ProcessEnvConfig } from 'mesh-config'; +import { AuxHttpServer, BaseApp } from '@nodescript/microframework'; +import { Config, config, ConfigError, getMeshConfigs } from 'mesh-config'; import { dep, Mesh } from 'mesh-ioc'; import { HttpRequestLogger, HttpServer } from './http.js'; -import { StandardLogger } from './logger.js'; -import { getGlobalMetrics } from './metrics/global.js'; -import { MetricsRouter } from './metrics/route.js'; -import { - AcAuthProvider, - AutomationCloudJwtService, - DefaultAcAuthProvider, - JwtService, -} from './services/index.js'; +import { GlobalMetrics } from './metrics/global.js'; + /** * Application is an IoC composition root where all modules should be registered * and provides minimal lifecycle framework (start, stop, beforeStart, afterStop). */ -export class Application { - - mesh = new Mesh('Global'); +export class Application extends BaseApp { @config({ default: false }) ASSERT_CONFIGS_ON_START!: boolean; + @config({ default: true }) START_AUX_HTTP_SERVER_ON_START!: boolean; @dep() httpServer!: HttpServer; - @dep() logger!: Logger; + @dep() auxHttpServer!: AuxHttpServer; constructor() { - // Some default implementations are bound for convenience but can be replaced as fit - this.mesh = this.createGlobalScope(); - this.mesh.connect(this); + super(new Mesh('App')); + this.createGlobalScope(); } createGlobalScope(): Mesh { - const mesh = new Mesh('Global'); - mesh.constant('httpRequestScope', () => this.createHttpRequestScope()); - mesh.service(Logger, StandardLogger); - mesh.alias('AppLogger', Logger); - mesh.service(ProcessEnvConfig); - mesh.alias(Config, ProcessEnvConfig); - mesh.service(HttpServer); - mesh.service(AutomationCloudJwtService); - mesh.service(AcAuthProvider, DefaultAcAuthProvider); - mesh.alias(JwtService, AutomationCloudJwtService); - mesh.constant('GlobalMetrics', getGlobalMetrics()); - return mesh; + this.mesh.constant('httpRequestScope', () => this.createHttpRequestScope()); + this.mesh.alias('AppLogger', Logger); + this.mesh.service(HttpServer); + this.mesh.service(GlobalMetrics); + return this.mesh; } createHttpRequestScope(): Mesh { - const mesh = new Mesh('HttpRequest'); - mesh.parent = this.mesh; + const mesh = new Mesh('HttpRequestScope', this.mesh); mesh.service(Logger, HttpRequestLogger); - mesh.service(MetricsRouter); return mesh; } @@ -60,33 +42,19 @@ export class Application { async afterStop(): Promise {} - async start() { - dotenv.config({ path: '.env' }); - if (process.env.NODE_ENV === 'development') { - dotenv.config({ path: '.env.dev' }); - } - if (process.env.NODE_ENV === 'test') { - dotenv.config({ path: '.env.test' }); - } - process.on('uncaughtException', error => { - this.logger.error('uncaughtException', { error }); - }); - process.on('unhandledRejection', error => { - this.logger.error('unhandledRejection', { error }); - }); - process.on('SIGTERM', () => this.logger.info('Received SIGTERM')); - process.on('SIGINT', () => this.logger.info('Received SIGINT')); - process.on('SIGTERM', () => this.stop()); - process.on('SIGINT', () => this.stop()); + override async start() { + await super.start(); if (this.ASSERT_CONFIGS_ON_START) { this.assertConfigs(); } + if (this.START_AUX_HTTP_SERVER_ON_START) { + await this.auxHttpServer.start(); + } await this.beforeStart(); } - async stop() { - // TODO uninstall process signals handlers better - process.removeAllListeners(); + override async stop() { + await super.stop(); await this.afterStop(); } diff --git a/src/main/http.ts b/src/main/http.ts index f9f3bff..c628638 100644 --- a/src/main/http.ts +++ b/src/main/http.ts @@ -12,11 +12,10 @@ import { dep, Mesh } from 'mesh-ioc'; import stoppable, { StoppableServer } from 'stoppable'; import { constants } from 'zlib'; -import { AcAuth } from './ac-auth.js'; import { ClientError } from './exception.js'; import { standardMiddleware } from './middleware.js'; import { Router } from './router.js'; -import { AcAuthProvider } from './services/index.js'; +import { AuthContext, AuthProvider } from './services/index.js'; import { findMeshInstances } from './util.js'; interface MiddlewareSpec { @@ -40,6 +39,7 @@ export class HttpServer extends Koa { @dep() protected logger!: Logger; @dep({ key: 'httpRequestScope' }) protected createRequestScope!: () => Mesh; + @dep() protected mesh!: Mesh; protected middlewares: MiddlewareSpec[] = [ { @@ -101,8 +101,8 @@ export class HttpServer extends Koa { }) }, { - name: 'acAuth', - middleware: this.createAcAuthMiddleware(), + name: 'auth', + middleware: this.createAuthMiddleware(), }, { name: 'routing', @@ -117,7 +117,15 @@ export class HttpServer extends Koa { } addStandardMiddleware(): this { - this.middlewares.forEach(m => this.use(m.middleware)); + this.middlewares.forEach(m => { + if (m.name === 'auth') { + if (this.mesh.tryResolve(AuthProvider) !== undefined) { + this.use(m.middleware); + } + } else { + this.use(m.middleware); + } + }); return this; } @@ -171,12 +179,12 @@ export class HttpServer extends Koa { }; } - protected createAcAuthMiddleware(): Middleware { + protected createAuthMiddleware(): Middleware { return async (ctx: Koa.Context, next: Koa.Next) => { const mesh: Mesh = ctx.mesh; - const provider = mesh.resolve(AcAuthProvider); - const acAuth = await provider.provide(ctx.headers); - mesh.constant(AcAuth, acAuth); + const provider = mesh.resolve(AuthProvider); + const authContext = await provider.provide(ctx.headers); + mesh.constant(AuthContext, authContext); return next(); }; } diff --git a/src/main/logger.ts b/src/main/logger.ts index a5ce9db..a1af4b4 100644 --- a/src/main/logger.ts +++ b/src/main/logger.ts @@ -1,7 +1,8 @@ import { ConsoleLogger, DefaultLogFormatter, LOG_LEVELS, LogfmtFormatter, Logger, LogLevel, LogPayload, StructuredLogFormatter } from '@nodescript/logger'; import { config } from 'mesh-config'; +import { dep } from 'mesh-ioc'; -import { getGlobalMetrics } from './metrics/global.js'; +import { GlobalMetrics } from './metrics/global.js'; export { LOG_LEVELS, @@ -19,6 +20,8 @@ export class StandardLogger extends ConsoleLogger { @config({ default: false }) LOG_LOGFMT!: boolean; + @dep() protected globalMetrics!: GlobalMetrics; + constructor() { super(); this.formatter = this.LOG_PRETTY ? new DefaultLogFormatter() : @@ -27,7 +30,7 @@ export class StandardLogger extends ConsoleLogger { } override write(payload: LogPayload) { - getGlobalMetrics().appLogsTotal.incr(1, { severity: payload.level }); + this.globalMetrics.appLogsTotal.incr(1, { severity: payload.level }); super.write(payload); } diff --git a/src/main/metrics/counter.ts b/src/main/metrics/counter.ts deleted file mode 100644 index d467e65..0000000 --- a/src/main/metrics/counter.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Metric, MetricDatum } from './metric.js'; - -export class CounterMetric extends Metric { - protected data: Map> = new Map(); - - getType() { - return 'counter'; - } - - get(labels: Partial = {}) { - return this.data.get(this.createMetricLabelsKey(labels)); - } - - incr(value: number = 1, labels: Partial = {}, timestamp?: number) { - const key = this.createMetricLabelsKey(labels); - const datum = this.data.get(key); - if (datum) { - datum.value += value; - datum.timestamp = timestamp; - } else { - this.data.set(key, { - value, - timestamp, - labels, - }); - } - } - - *generateReportLines() { - for (const datum of this.data.values()) { - yield [ - this.getMetricLineName(datum.labels), - datum.value, - datum.timestamp, - ].filter(x => x != null).join(' '); - } - } - -} diff --git a/src/main/metrics/gauge.ts b/src/main/metrics/gauge.ts deleted file mode 100644 index 0a57e6e..0000000 --- a/src/main/metrics/gauge.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Metric, MetricDatum } from './metric.js'; - -export class GaugeMetric extends Metric { - protected data: Map> = new Map(); - - getType() { - return 'gauge'; - } - - *generateReportLines() { - for (const datum of this.data.values()) { - yield [ - this.getMetricLineName(datum.labels), - datum.value, - datum.timestamp, - ].filter(x => x != null).join(' '); - } - } - - get(labels: Partial = {}) { - return this.data.get(this.createMetricLabelsKey(labels)); - } - - set(value: number, labels: Partial = {}, timestamp?: number) { - const key = this.createMetricLabelsKey(labels); - const datum = this.data.get(key); - if (datum) { - datum.value = value; - datum.timestamp = timestamp; - } else { - this.data.set(key, { - value, - timestamp, - labels, - }); - } - } - -} diff --git a/src/main/metrics/global.ts b/src/main/metrics/global.ts index 462be4a..0c9f8a4 100644 --- a/src/main/metrics/global.ts +++ b/src/main/metrics/global.ts @@ -1,35 +1,23 @@ -import { MetricsRegistry } from './registry.js'; +import { CounterMetric, GaugeMetric, HistogramMetric, metric } from '@nodescript/metrics'; -const METRICS_GLOBAL_KEY = Symbol.for('@ubio/framework:globalMetrics'); +const methodDurationMetric = new HistogramMetric('app_method_duration_seconds', + 'Performance measurements taken for a particular class method'); -export class GlobalMetricsRegistry extends MetricsRegistry { - methodDuration = this.histogram('app_method_duration_seconds', - 'Performance measurements taken for a particular class method'); - handlerDuration = this.histogram('app_handler_duration_seconds', +export class GlobalMetrics { + @metric() methodDuration = methodDurationMetric; + @metric() handlerDuration = new HistogramMetric('app_handler_duration_seconds', 'Application performance measurements'); - - mongoDocumentsTotal = this.gauge('mongo_documents_total', + @metric() mongoDocumentsTotal = new GaugeMetric('mongo_documents_total', 'Estimated count of MongoDB documents, per collection'); - - appLogsTotal = this.counter('app_logs_total', + @metric() appLogsTotal = new CounterMetric('app_logs_total', 'Total count of log lines by severity'); } -export function getGlobalMetrics(): GlobalMetricsRegistry { - let registry = (global as any)[METRICS_GLOBAL_KEY]; - if (!(registry instanceof GlobalMetricsRegistry)) { - registry = new GlobalMetricsRegistry(); - (global as any)[METRICS_GLOBAL_KEY] = registry; - } - return registry; -} - export function MeasureAsync() { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { - const globalMetrics = getGlobalMetrics(); const originalMethod = descriptor.value; descriptor.value = async function (this: any): Promise { - const end = globalMetrics.methodDuration.timer({ + const end = methodDurationMetric.timer({ class: target.constructor.name, method: propertyKey, }); diff --git a/src/main/metrics/histogram.ts b/src/main/metrics/histogram.ts deleted file mode 100644 index 92af396..0000000 --- a/src/main/metrics/histogram.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Metric } from './metric.js'; - -const DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]; - -export class HistogramMetric extends Metric { - protected buckets: number[]; - data: Map> = new Map(); - - constructor( - name: string, - help: string, - buckets: number[] = DEFAULT_BUCKETS, - ) { - super(name, help); - this.buckets = buckets.slice().sort((a, b) => a > b ? 1 : -1); - } - - getType() { - return 'histogram'; - } - - add(value: number, labels: Partial = {}, timestamp: number = Date.now()) { - const key = this.createMetricLabelsKey(labels); - const datum = this.data.get(key); - if (datum) { - datum.timestamp = timestamp; - datum.buckets = this.calcBuckets(datum.buckets, value); - datum.count += 1; - datum.sum += value; - } else { - this.data.set(key, { - labels, - timestamp, - buckets: this.calcBuckets([], value), - count: 1, - sum: value, - }); - } - } - - timer(labels: Partial = {}) { - const startedAt = process.hrtime(); - return () => { - const [sec, nanosec] = process.hrtime(startedAt); - const value = sec + nanosec * 1e-9; - this.add(value, labels); - }; - } - - async measure(fn: () => Promise, labels: Partial = {}): Promise { - const stop = this.timer(labels); - try { - return await fn(); - } finally { - stop(); - } - } - - protected calcBuckets(existingValues: number[], newValue: number) { - return this.buckets - .map(n => newValue <= n ? 1 : 0) - .map((incr, index) => { - return incr + (existingValues[index] || 0); - }); - } - - protected *generateReportLines() { - for (const datum of this.data.values()) { - for (const [i, le] of this.buckets.entries()) { - const prefix = this.getMetricLineName({ ...datum.labels, le: String(le) }, '_bucket'); - const sample = datum.buckets[i]; - yield [prefix, sample].join(' '); - } - yield [ - this.getMetricLineName({ ...datum.labels, le: '+Inf' }, '_bucket'), - datum.count - ].join(' '); - yield [ - this.getMetricLineName(datum.labels, '_sum'), - datum.sum - ].join(' '); - yield [ - this.getMetricLineName(datum.labels, '_count'), - datum.count - ].join(' '); - } - } - -} - -export interface HistogramDatum { - labels: Partial; - timestamp: number; - buckets: number[]; // correspond to configured buckets, w/o +Inf - count: number; - sum: number; -} diff --git a/src/main/metrics/index.ts b/src/main/metrics/index.ts index b3c70cf..f056117 100644 --- a/src/main/metrics/index.ts +++ b/src/main/metrics/index.ts @@ -1,7 +1 @@ -export * from './counter.js'; -export * from './gauge.js'; export * from './global.js'; -export * from './histogram.js'; -export * from './metric.js'; -export * from './registry.js'; -export * from './route.js'; diff --git a/src/main/metrics/metric.ts b/src/main/metrics/metric.ts deleted file mode 100644 index 535b206..0000000 --- a/src/main/metrics/metric.ts +++ /dev/null @@ -1,42 +0,0 @@ -export interface MetricDatum { - labels: Partial; - timestamp?: number; - value: number; -} - -export abstract class Metric { - - constructor( - public name: string, - public help: string, - ) { - } - - abstract getType(): string; - - protected abstract generateReportLines(): Iterable; - - getMetricLineName(labels: Partial, suffix: string = '') { - const fields = this.createMetricLabelsKey(labels); - return fields ? `${this.name}${suffix}{${fields}}` : this.name; - } - - report() { - const report = []; - report.push(`# HELP ${this.name} ${this.help}`); - report.push(`# TYPE ${this.name} ${this.getType()}`); - report.push(...this.generateReportLines()); - return report.join('\n'); - } - - protected createMetricLabelsKey(labels: Partial = {}) { - return Object.keys(labels) - .sort() - .map(k => { - const v = (labels as any)[k]; - return `${k}=${JSON.stringify(String(v))}`; - }) - .join(','); - } - -} diff --git a/src/main/metrics/registry.ts b/src/main/metrics/registry.ts deleted file mode 100644 index 7ddf427..0000000 --- a/src/main/metrics/registry.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Exception } from '../exception.js'; -import { CounterMetric } from './counter.js'; -import { GaugeMetric } from './gauge.js'; -import { HistogramMetric } from './histogram.js'; -import { Metric } from './metric.js'; - -export class MetricsRegistry { - protected registry: Map = new Map(); - - counter(name: string, help: string) { - const counter = new CounterMetric(name, help); - this.register(counter); - return counter; - } - - gauge(name: string, help: string) { - const gauge = new GaugeMetric(name, help); - this.register(gauge); - return gauge; - } - - histogram(name: string, help: string, buckets?: number[]) { - const histogram = new HistogramMetric(name, help, buckets); - this.register(histogram); - return histogram; - } - - protected register(metric: Metric) { - // It is prohibited to register a metric with same name twice - const existing = this.registry.get(metric.name); - if (existing) { - throw new MetricAlreadyDefined(metric.name); - } - this.registry.set(metric.name, metric); - } - - report() { - return [...this.registry.values()].map(_ => _.report()).join('\n\n'); - } -} - -export class MetricAlreadyDefined extends Exception { - constructor(metricName: string) { - super(`Metric ${metricName} is already defined; please store a reference to a metric instance`); - } -} diff --git a/src/main/metrics/route.ts b/src/main/metrics/route.ts deleted file mode 100644 index fad335d..0000000 --- a/src/main/metrics/route.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { dep, Mesh } from 'mesh-ioc'; - -import { Get, Router } from '../router.js'; -import { findMeshInstances } from '../util.js'; -import { MetricsRegistry } from './registry.js'; - -export class MetricsRouter extends Router { - - @dep() protected mesh!: Mesh; - - @Get({ - path: '/metrics', - summary: '(internal) Get current process metrics', - responses: { - 200: { - description: 'Prometheus metrics in text-based format', - contentType: 'text/plain', - } - } - }) - async metrics() { - this.ctx.type = 'text/plain; version=0.0.4'; - return this.getRegistries().map(_ => _.report()).join('\n\n'); - } - - protected getRegistries(): MetricsRegistry[] { - return findMeshInstances(this.mesh, MetricsRegistry); - } - -} diff --git a/src/main/router.ts b/src/main/router.ts index dda0cb4..0a2cb3c 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -7,7 +7,7 @@ import { config } from 'mesh-config'; import { dep } from 'mesh-ioc'; import { ClientError, Exception } from './exception.js'; -import { getGlobalMetrics } from './metrics/global.js'; +import { GlobalMetrics } from './metrics/global.js'; import { ajvErrorToMessage, AnyConstructor, Constructor, deepClone } from './util.js'; const ROUTES_KEY = Symbol('Route'); @@ -134,6 +134,7 @@ export class Router { @dep() protected logger!: Logger; @dep({ key: 'KoaContext' }) protected ctx!: koa.Context; + @dep() protected globalMetrics!: GlobalMetrics; @config({ default: false }) HTTP_VALIDATE_RESPONSES!: boolean; @@ -152,7 +153,7 @@ export class Router { route, ...getAfterHookRoutes(this.constructor as Constructor), ]; - await getGlobalMetrics().handlerDuration.measure(async () => { + await this.globalMetrics.handlerDuration.measure(async () => { // Route matched, now execute all middleware first, then execute the route itself const response = await this.handleRoutes(routes); this.ctx.body = response ?? this.ctx.body ?? {}; diff --git a/src/main/services/ac-auth-provider.ts b/src/main/services/ac-auth-provider.ts index 1441c93..73717b7 100644 --- a/src/main/services/ac-auth-provider.ts +++ b/src/main/services/ac-auth-provider.ts @@ -3,16 +3,13 @@ import { Request } from '@ubio/request'; import { config } from 'mesh-config'; import { dep } from 'mesh-ioc'; -import { AcAuth, AuthenticationError } from '../ac-auth.js'; +import { AcAuth } from '../ac-auth.js'; +import { getSingleValue } from '../util.js'; +import { AuthContext, AuthenticationError } from './auth-context.js'; +import { AuthHeaders, AuthProvider } from './auth-provider.js'; import { JwtService } from './jwt.js'; -export type AuthHeaders = Record; - -export abstract class AcAuthProvider { - abstract provide(headers: AuthHeaders): Promise; -} - -export class DefaultAcAuthProvider extends AcAuthProvider { +export class AcAuthProvider extends AuthProvider { clientRequest: Request; static middlewareCacheTtl: number = 60000; @@ -32,12 +29,13 @@ export class DefaultAcAuthProvider extends AcAuthProvider { }); } - async provide(headers: AuthHeaders): Promise { - const token = await this.getToken(headers as any); + async provide(headers: AuthHeaders) { + const token = await this.getToken(headers); if (token) { - return await this.createAuthFromToken(headers, token); + const acAuth = await this.createAuthFromToken(headers, token); + return new AuthContext(acAuth); } - return new AcAuth(); + return new AuthContext(null); } protected async createAuthFromToken(headers: AuthHeaders, token: string): Promise { @@ -57,9 +55,9 @@ export class DefaultAcAuthProvider extends AcAuthProvider { } } - protected async getToken(headers: { [name: string]: string | undefined }) { + protected async getToken(headers: AuthHeaders) { const authHeaderName = this.AC_AUTH_HEADER_NAME; - const upstreamAuth = headers[authHeaderName]; + const upstreamAuth = getSingleValue(headers[authHeaderName]); if (upstreamAuth) { const [prefix, token] = upstreamAuth.split(' '); if (prefix !== 'Bearer' || !token) { @@ -70,15 +68,15 @@ export class DefaultAcAuthProvider extends AcAuthProvider { } return token; } - const authorization = headers['authorization']; + const authorization = getSingleValue(headers['authorization']); if (authorization) { return await this.getTokenFromAuthMiddleware(authorization); } } protected async getTokenFromAuthMiddleware(authorization: string): Promise { - const cached = DefaultAcAuthProvider.middlewareTokensCache.get(authorization) || { authorisedAt: 0, token: '' }; - const invalid = cached.authorisedAt + DefaultAcAuthProvider.middlewareCacheTtl < Date.now(); + const cached = AcAuthProvider.middlewareTokensCache.get(authorization) || { authorisedAt: 0, token: '' }; + const invalid = cached.authorisedAt + AcAuthProvider.middlewareCacheTtl < Date.now(); if (invalid) { try { const url = this.AC_AUTH_VERIFY_URL; @@ -86,7 +84,7 @@ export class DefaultAcAuthProvider extends AcAuthProvider { headers: { authorization }, }; const { token } = await this.clientRequest.get(url, options); - DefaultAcAuthProvider.middlewareTokensCache.set(authorization, { + AcAuthProvider.middlewareTokensCache.set(authorization, { token, authorisedAt: Date.now(), }); @@ -102,19 +100,19 @@ export class DefaultAcAuthProvider extends AcAuthProvider { pruneCache() { const now = Date.now(); - const entries = DefaultAcAuthProvider.middlewareTokensCache.entries(); + const entries = AcAuthProvider.middlewareTokensCache.entries(); for (const [k, v] of entries) { - if (v.authorisedAt + DefaultAcAuthProvider.middlewareCacheTtl < now) { - DefaultAcAuthProvider.middlewareTokensCache.delete(k); + if (v.authorisedAt + AcAuthProvider.middlewareCacheTtl < now) { + AcAuthProvider.middlewareTokensCache.delete(k); } } } } -export class BypassAcAuthProvider extends AcAuthProvider { +export class BypassAcAuthProvider extends AuthProvider { async provide() { - return new AcAuth(); + return new AuthContext(null); } } diff --git a/src/main/services/auth-context.ts b/src/main/services/auth-context.ts new file mode 100644 index 0000000..e6f922a --- /dev/null +++ b/src/main/services/auth-context.ts @@ -0,0 +1,32 @@ +import { ClientError } from '@nodescript/errors'; + +export type AuthToken = object; + +export class AuthContext { + + constructor(private authToken: T) {} + + isAuthenticated() { + return this.authToken != null; + } + + checkAuthenticated(): void { + if (!this.isAuthenticated()) { + throw new AuthenticationError(); + } + } + + getAuthToken(): T { + return this.authToken; + } + + setAuthToken(authToken: T): void { + this.authToken = authToken; + } +} + + +export class AuthenticationError extends ClientError { + override status = 401; + override message = 'Authentication is required'; +} diff --git a/src/main/services/auth-provider.ts b/src/main/services/auth-provider.ts new file mode 100644 index 0000000..2cf6c9f --- /dev/null +++ b/src/main/services/auth-provider.ts @@ -0,0 +1,7 @@ +import { AuthContext, AuthToken } from './auth-context.js'; + +export type AuthHeaders = Record; + +export abstract class AuthProvider { + abstract provide(headers?: AuthHeaders): Promise>; +} diff --git a/src/main/services/index.ts b/src/main/services/index.ts index 34f7b16..397a731 100644 --- a/src/main/services/index.ts +++ b/src/main/services/index.ts @@ -1,3 +1,5 @@ export * from './ac-auth-provider.js'; +export * from './auth-provider.js'; +export * from './auth-context.js'; export * from './job-timeline.js'; export * from './jwt.js'; diff --git a/src/main/util.ts b/src/main/util.ts index f50ec71..603a06b 100644 --- a/src/main/util.ts +++ b/src/main/util.ts @@ -11,7 +11,7 @@ import { Exception } from './exception.js'; export type Constructor = new (...args: any[]) => T; export type AnyConstructor = new (...args: any[]) => {}; -export function deepClone(data: T): T { +export function deepClone(data: T): T | null { return data == null ? null : JSON.parse(JSON.stringify(data)); } @@ -105,3 +105,10 @@ export function findMeshInstances(mesh: Mesh, ctor: ServiceConstructor): T } return instances; } + +export function getSingleValue(value: T | T[] | undefined): T | undefined { + if (Array.isArray(value)) { + return value[0]; + } + return value; +} diff --git a/src/modules/mongodb.ts b/src/modules/mongodb.ts index e3212fb..8e7cc78 100644 --- a/src/modules/mongodb.ts +++ b/src/modules/mongodb.ts @@ -3,7 +3,7 @@ import { config } from 'mesh-config'; import { dep } from 'mesh-ioc'; import { Db, MongoClient } from 'mongodb'; -import { getGlobalMetrics } from '../main/index.js'; +import { GlobalMetrics } from '../main/index.js'; export class MongoDb { client: MongoClient; @@ -15,6 +15,7 @@ export class MongoDb { @config({ default: 10000 }) MONGO_METRICS_REFRESH_INTERVAL!: number; @dep() protected logger!: Logger; + @dep() protected globalMetrics!: GlobalMetrics; constructor() { this.client = new MongoClient(this.MONGO_URL, { @@ -52,12 +53,11 @@ export class MongoDb { protected async refreshMetricsLoop() { while (this.refreshingMetrics) { try { - const metrics = getGlobalMetrics(); const collections = await this.db.listCollections().toArray(); const counts = await Promise.all( collections.map(col => this.db.collection(col.name).estimatedDocumentCount())); for (const [i, col] of collections.entries()) { - metrics.mongoDocumentsTotal.set(counts[i], { + this.globalMetrics.mongoDocumentsTotal.set(counts[i], { collection: col.name, db: this.db.databaseName, }); diff --git a/src/test/integration/ac-auth-mocking.test.ts b/src/test/integration/ac-auth-mocking.test.ts index a891b43..f345a3e 100644 --- a/src/test/integration/ac-auth-mocking.test.ts +++ b/src/test/integration/ac-auth-mocking.test.ts @@ -2,42 +2,47 @@ import assert from 'assert'; import { dep } from 'mesh-ioc'; import supertest from 'supertest'; -import { AcAuth, AcAuthProvider, Application, Get, Router } from '../../main/index.js'; +import { AcAuth, Application, AuthContext, AuthProvider, Get, Router } from '../../main/index.js'; describe('Mocking AcAuth', () => { class MyRouter extends Router { - @dep() protected auth!: AcAuth; + @dep() protected auth!: AuthContext; @Get({ path: '/foo' }) foo() { - return { ...this.auth }; + return { ...this.auth.getAuthToken() }; } } class App extends Application { - override createHttpRequestScope() { - const mesh = super.createHttpRequestScope(); - mesh.service(MyRouter); - mesh.constant(AcAuthProvider, { + override createGlobalScope() { + const mesh = super.createGlobalScope(); + mesh.constant(AuthProvider, { async provide() { - return new AcAuth({ + return new AuthContext(new AcAuth({ jwtContext: { organisation_id: 'foo', service_account_id: 'service-account-worker', service_account_name: 'Bot', } - }); + })); } }); return mesh; } + override createHttpRequestScope() { + const mesh = super.createHttpRequestScope(); + mesh.service(MyRouter); + return mesh; + } + } const app = new App(); diff --git a/src/test/routes/access.ts b/src/test/routes/access.ts index 13291c6..2584442 100644 --- a/src/test/routes/access.ts +++ b/src/test/routes/access.ts @@ -1,10 +1,10 @@ import { dep } from 'mesh-ioc'; -import { AcAuth, Get, Router } from '../../main/index.js'; +import { AcAuth, AuthContext, Get, Router } from '../../main/index.js'; export class AccessRouter extends Router { - @dep() protected auth!: AcAuth; + @dep() protected auth!: AuthContext; @Get({ path: '/public', diff --git a/src/test/specs/jwt.test.ts b/src/test/specs/jwt.test.ts index 28b3e62..e1693ba 100644 --- a/src/test/specs/jwt.test.ts +++ b/src/test/specs/jwt.test.ts @@ -3,7 +3,7 @@ import crypto from 'crypto'; import jsonwebtoken from 'jsonwebtoken'; import { v4 as uuid } from 'uuid'; -import { Application, AutomationCloudJwtService } from '../../main/index.js'; +import { Application, AutomationCloudJwtService, JwtService } from '../../main/index.js'; describe('AutomationCloudJwt', () => { @@ -23,6 +23,8 @@ describe('AutomationCloudJwt', () => { }; const app = new Application(); + app.mesh.service(AutomationCloudJwtService); + app.mesh.alias(JwtService, AutomationCloudJwtService); beforeEach(async () => { jwtService = app.mesh.resolve(AutomationCloudJwtService); diff --git a/src/test/specs/metrics.test.ts b/src/test/specs/metrics.test.ts index f13fd1e..eff7edc 100644 --- a/src/test/specs/metrics.test.ts +++ b/src/test/specs/metrics.test.ts @@ -1,161 +1,11 @@ +import { generateMetricsReport } from '@nodescript/metrics'; import assert from 'assert'; import supertest from 'supertest'; -import theredoc from 'theredoc'; -import { - Application, - CounterMetric, - GaugeMetric, - getGlobalMetrics, - HistogramMetric -} from '../../main/index.js'; +import { Application } from '../../main/index.js'; import { FooRouter } from '../routes/foo.js'; -describe('CounterMetric', () => { - - it('increments an new counter', () => { - const counter = new CounterMetric('foo', 'Foo help'); - counter.incr(1, { foo: 'one' }); - const datum = counter.get({ foo: 'one' }); - assert.ok(datum); - assert.strictEqual(datum?.value, 1); - }); - - it('increments an existing counter', () => { - const counter = new CounterMetric('foo', 'Foo help'); - counter.incr(1, { foo: 'one' }); - counter.incr(1, { foo: 'one' }); - const datum = counter.get({ foo: 'one' }); - assert.ok(datum); - assert.strictEqual(datum?.value, 2); - }); - - it('increments without labels', () => { - const counter = new CounterMetric('foo', 'Foo help'); - counter.incr(); - const datum = counter.get(); - assert.ok(datum); - assert.strictEqual(datum?.value, 1); - }); - - it('compiles a report', () => { - const counter = new CounterMetric('foo', 'Foo help'); - counter.incr(1, {}, 123123123); - counter.incr(1, { lbl: 'one' }, 123123123); - counter.incr(2, { lbl: 'two' }); - counter.incr(3, { lbl: 'three', foo: '1' }, 123123123); - assert.strictEqual(counter.report().trim(), theredoc` - # HELP foo Foo help - # TYPE foo counter - foo 1 123123123 - foo{lbl="one"} 1 123123123 - foo{lbl="two"} 2 - foo{foo="1",lbl="three"} 3 123123123 - `.trim()); - }); - -}); - -describe('GaugeMetric', () => { - - it('sets a new value', () => { - const gauge = new GaugeMetric('foo', 'Foo help'); - gauge.set(1, { foo: 'one' }); - const datum = gauge.get({ foo: 'one' }); - assert.ok(datum); - assert.strictEqual(datum?.value, 1); - }); - - it('overwrites a existing value', () => { - const counter = new GaugeMetric('foo', 'Foo help'); - counter.set(2, { foo: 'one' }); - counter.set(5, { foo: 'one' }); - const datum = counter.get({ foo: 'one' }); - assert.ok(datum); - assert.strictEqual(datum?.value, 5); - }); - - it('sets without labels', () => { - const counter = new GaugeMetric('foo', 'Foo help'); - counter.set(5); - const datum = counter.get(); - assert.ok(datum); - assert.strictEqual(datum?.value, 5); - }); - - it('compiles a report', () => { - const counter = new GaugeMetric('foo', 'Foo help'); - counter.set(1, {}, 123123123); - counter.set(1, { lbl: 'one' }, 123123123); - counter.set(2, { lbl: 'two' }, 123123123); - counter.set(3, { lbl: 'three', foo: '1' }, 123123123); - assert.strictEqual(counter.report().trim(), theredoc` - # HELP foo Foo help - # TYPE foo gauge - foo 1 123123123 - foo{lbl="one"} 1 123123123 - foo{lbl="two"} 2 123123123 - foo{foo="1",lbl="three"} 3 123123123 - `.trim()); - }); - -}); - -describe('HistogramMetric', () => { - - it('compiles a report', () => { - const histogram = new HistogramMetric('foo', 'Foo help'); - histogram.add(0.16, { lbl: 'one' }); - histogram.add(0.15, { lbl: 'one' }); - histogram.add(0.24, { lbl: 'one' }); - histogram.add(0.11, { lbl: 'one' }); - histogram.add(0.55, { lbl: 'one' }); - - histogram.add(0.1, { lbl: 'two' }); - histogram.add(0.6, { lbl: 'two' }); - histogram.add(0.9, { lbl: 'two' }); - histogram.add(1.0, { lbl: 'two' }); - histogram.add(1.05, { lbl: 'two' }); - histogram.add(2, { lbl: 'two' }); - histogram.add(3, { lbl: 'two' }); - - assert.strictEqual(histogram.report().trim(), theredoc` - # HELP foo Foo help - # TYPE foo histogram - foo_bucket{lbl="one",le="0.005"} 0 - foo_bucket{lbl="one",le="0.01"} 0 - foo_bucket{lbl="one",le="0.025"} 0 - foo_bucket{lbl="one",le="0.05"} 0 - foo_bucket{lbl="one",le="0.1"} 0 - foo_bucket{lbl="one",le="0.25"} 4 - foo_bucket{lbl="one",le="0.5"} 4 - foo_bucket{lbl="one",le="1"} 5 - foo_bucket{lbl="one",le="2.5"} 5 - foo_bucket{lbl="one",le="5"} 5 - foo_bucket{lbl="one",le="10"} 5 - foo_bucket{lbl="one",le="+Inf"} 5 - foo_sum{lbl="one"} 1.21 - foo_count{lbl="one"} 5 - foo_bucket{lbl="two",le="0.005"} 0 - foo_bucket{lbl="two",le="0.01"} 0 - foo_bucket{lbl="two",le="0.025"} 0 - foo_bucket{lbl="two",le="0.05"} 0 - foo_bucket{lbl="two",le="0.1"} 1 - foo_bucket{lbl="two",le="0.25"} 1 - foo_bucket{lbl="two",le="0.5"} 1 - foo_bucket{lbl="two",le="1"} 4 - foo_bucket{lbl="two",le="2.5"} 6 - foo_bucket{lbl="two",le="5"} 7 - foo_bucket{lbl="two",le="10"} 7 - foo_bucket{lbl="two",le="+Inf"} 7 - foo_sum{lbl="two"} 8.65 - foo_count{lbl="two"} 7 - `.trim()); - }); - -}); - -describe('Routes execution histogram metric', () => { +describe('Routes execution metrics', () => { class App extends Application { @@ -185,7 +35,7 @@ describe('Routes execution histogram metric', () => { await request.get('/foo/1'); await request.get('/foo/2'); - const metricsReport = getGlobalMetrics().report(); + const metricsReport = generateMetricsReport(app.mesh); assert.match(metricsReport, /path="\/foo\/{fooId}"/); assert.doesNotMatch(metricsReport, /path="\/foo\/1"/); diff --git a/src/test/specs/services/ac-auth.test.ts b/src/test/specs/services/ac-auth.test.ts index 22b99a4..ad80ec6 100644 --- a/src/test/specs/services/ac-auth.test.ts +++ b/src/test/specs/services/ac-auth.test.ts @@ -7,7 +7,6 @@ import { Mesh } from 'mesh-ioc'; import { AcAuthProvider, AuthenticationError, - DefaultAcAuthProvider, JwtService, StandardLogger, } from '../../../main/index.js'; @@ -16,15 +15,14 @@ describe('AcAuthProvider', () => { let mesh: Mesh; let fetchMock: request.FetchMock; - let authProvider: DefaultAcAuthProvider; + let authProvider: AcAuthProvider; let headers: any = {}; let jwt: any = {}; beforeEach(() => { mesh = new Mesh(); mesh.service(Logger, StandardLogger); - mesh.service(DefaultAcAuthProvider); - mesh.alias(AcAuthProvider, DefaultAcAuthProvider); + mesh.service(AcAuthProvider); mesh.service(Config, ProcessEnvConfig); mesh.constant(JwtService, { async decodeAndVerify(token: string) { @@ -35,7 +33,7 @@ describe('AcAuthProvider', () => { } }); fetchMock = request.fetchMock({ status: 200 }, { token: 'jwt-token-here' }); - authProvider = mesh.resolve(DefaultAcAuthProvider); + authProvider = mesh.resolve(AcAuthProvider); authProvider.clientRequest.config.fetch = fetchMock; jwt = { context: { @@ -48,12 +46,12 @@ describe('AcAuthProvider', () => { afterEach(() => { jwt = {}; headers = {}; - DefaultAcAuthProvider.middlewareTokensCache = new Map(); + AcAuthProvider.middlewareTokensCache = new Map(); }); describe('x-ubio-auth header exists', () => { beforeEach(() => { - const authHeader = mesh.resolve(DefaultAcAuthProvider).AC_AUTH_HEADER_NAME; + const authHeader = mesh.resolve(AcAuthProvider).AC_AUTH_HEADER_NAME; headers[authHeader] = 'Bearer jwt-token-here'; }); @@ -68,7 +66,7 @@ describe('AcAuthProvider', () => { }); it('throws when jwt is not valid', async () => { - const authHeader = mesh.resolve(DefaultAcAuthProvider).AC_AUTH_HEADER_NAME; + const authHeader = mesh.resolve(AcAuthProvider).AC_AUTH_HEADER_NAME; headers[authHeader] = 'Bearer unknown-jwt-token'; try { await authProvider.provide(headers); @@ -94,8 +92,8 @@ describe('AcAuthProvider', () => { it('does not send request if Authorization is cached', async () => { const ttl = 60000; const margin = 1000; - DefaultAcAuthProvider.middlewareCacheTtl = ttl; - DefaultAcAuthProvider.middlewareTokensCache.set('AUTH', { + AcAuthProvider.middlewareCacheTtl = ttl; + AcAuthProvider.middlewareTokensCache.set('AUTH', { token: 'jwt-token-here', authorisedAt: Date.now() - ttl + margin }); @@ -109,8 +107,8 @@ describe('AcAuthProvider', () => { it('sends request if cache has expired', async () => { const ttl = 60000; const margin = 1000; - DefaultAcAuthProvider.middlewareCacheTtl = ttl; - DefaultAcAuthProvider.middlewareTokensCache.set('AUTH', { token: 'jwt-token-here', authorisedAt: Date.now() - ttl - margin }); + AcAuthProvider.middlewareCacheTtl = ttl; + AcAuthProvider.middlewareTokensCache.set('AUTH', { token: 'jwt-token-here', authorisedAt: Date.now() - ttl - margin }); headers['authorization'] = 'AUTH'; assert.strictEqual(fetchMock.spy.called, false); const auth = await authProvider.provide(headers); @@ -140,7 +138,7 @@ describe('AcAuthProvider', () => { describe('acAuth', () => { beforeEach(() => { - const authHeader = mesh.resolve(DefaultAcAuthProvider).AC_AUTH_HEADER_NAME; + const authHeader = mesh.resolve(AcAuthProvider).AC_AUTH_HEADER_NAME; headers[authHeader] = 'Bearer jwt-token-here'; }); @@ -149,7 +147,7 @@ describe('AcAuthProvider', () => { it('sets auth.organisationId', async () => { jwt.context.organisation_id = 'some-user-org-id'; const auth = await authProvider.provide(headers); - const organisationId = auth.getOrganisationId(); + const organisationId = auth.getAuthToken()?.getOrganisationId(); assert.strictEqual(organisationId, 'some-user-org-id'); }); }); @@ -158,7 +156,7 @@ describe('AcAuthProvider', () => { it('sets auth.organisationId', async () => { headers['x-ubio-organisation-id'] = 'org-id-from-header'; const auth = await authProvider.provide(headers); - const organisationId = auth.getOrganisationId(); + const organisationId = auth.getAuthToken()?.getOrganisationId(); assert.strictEqual(organisationId, 'org-id-from-header'); }); }); @@ -168,7 +166,7 @@ describe('AcAuthProvider', () => { jwt.context['organisation_id'] = 'org-id-from-jwt'; headers['x-ubio-organisation-id'] = 'org-id-from-header'; const auth = await authProvider.provide(headers); - const organisationId = auth.getOrganisationId(); + const organisationId = auth.getAuthToken()?.getOrganisationId(); assert.strictEqual(organisationId, 'org-id-from-jwt'); }); }); @@ -182,7 +180,7 @@ describe('AcAuthProvider', () => { service_account_name: 'Bot' }; const auth = await authProvider.provide(headers); - const serviceAccount = auth.actor; + const serviceAccount = auth.getAuthToken()?.actor; assert.ok(serviceAccount?.type === 'ServiceAccount'); assert.strictEqual(serviceAccount.id, 'some-service-account-id'); assert.strictEqual(serviceAccount.name, 'Bot'); @@ -195,7 +193,7 @@ describe('AcAuthProvider', () => { organisation_id: 'ubio-organisation-id', }; const auth = await authProvider.provide(headers); - const serviceAccount = auth.actor; + const serviceAccount = auth.getAuthToken()?.actor; assert.ok(serviceAccount?.type === 'ServiceAccount'); assert.strictEqual(serviceAccount.id, 'some-service-account-id'); assert.strictEqual(serviceAccount.name, 'Bot'); @@ -209,7 +207,7 @@ describe('AcAuthProvider', () => { client_name: 'Ron Swanson', }; const auth = await authProvider.provide(headers); - const serviceAccount = auth.actor; + const serviceAccount = auth.getAuthToken()?.actor; assert.ok(serviceAccount?.type === 'ServiceAccount'); assert.strictEqual(serviceAccount.clientId, 'ClientA'); assert.strictEqual(serviceAccount.clientName, 'Ron Swanson'); @@ -226,7 +224,7 @@ describe('AcAuthProvider', () => { organisation_id: 'ubio-organisation-id', }; const auth = await authProvider.provide(headers); - const client = auth.actor; + const client = auth.getAuthToken()?.actor; assert.ok(client?.type === 'Client'); assert.strictEqual(client.id, 'some-client-id'); assert.strictEqual(client.name, 'UbioAir'); @@ -241,7 +239,7 @@ describe('AcAuthProvider', () => { client_name: 'UbioAir', }; const auth = await authProvider.provide(headers); - const actor = auth.actor; + const actor = auth.getAuthToken()?.actor; assert.ok(actor?.type === 'JobAccessToken'); }); }); @@ -256,7 +254,7 @@ describe('AcAuthProvider', () => { organisation_id: 'ubio-organisation-id', }; const auth = await authProvider.provide(headers); - const user = auth.actor; + const user = auth.getAuthToken()?.actor; assert.ok(user?.type === 'User'); assert.strictEqual(user.id, 'some-user-id'); assert.strictEqual(user.name, 'Travel Aggregator'); @@ -269,7 +267,7 @@ describe('AcAuthProvider', () => { organisation_id: 'ubio-organisation-id', }; const auth = await authProvider.provide(headers); - const user = auth.actor; + const user = auth.getAuthToken()?.actor; assert.ok(user?.type === 'User'); assert.strictEqual(user.id, 'some-user-id'); assert.strictEqual(user.name, ''); @@ -284,7 +282,7 @@ describe('AcAuthProvider', () => { user_name: 'some-user-name' }; const auth = await authProvider.provide(headers); - const user = auth.actor; + const user = auth.getAuthToken()?.actor; assert.ok(user == null); }); }); @@ -300,7 +298,7 @@ describe('AcAuthProvider', () => { organisation_id: 'ubio-organisation-id', }; const auth = await authProvider.provide(headers); - const jobAccessToken = auth.actor; + const jobAccessToken = auth.getAuthToken()?.actor; assert.ok(jobAccessToken?.type === 'JobAccessToken'); assert.strictEqual(jobAccessToken.jobId, 'some-job-id'); assert.strictEqual(jobAccessToken.clientId, 'some-client-id'); @@ -315,7 +313,7 @@ describe('AcAuthProvider', () => { organisation_id: 'ubio-organisation-id', }; const auth = await authProvider.provide(headers); - const jobAccessToken = auth.actor; + const jobAccessToken = auth.getAuthToken()?.actor; assert.ok(jobAccessToken == null); }); @@ -326,7 +324,7 @@ describe('AcAuthProvider', () => { client_name: 'Travel Aggregator', }; const auth = await authProvider.provide(headers); - const jobAccessToken = auth.actor; + const jobAccessToken = auth.getAuthToken()?.actor; assert.ok(jobAccessToken == null); }); }); diff --git a/src/test/specs/services/bypass-auth-provider.test.ts b/src/test/specs/services/bypass-auth-provider.test.ts index 6300c86..c277916 100644 --- a/src/test/specs/services/bypass-auth-provider.test.ts +++ b/src/test/specs/services/bypass-auth-provider.test.ts @@ -2,8 +2,8 @@ import assert from 'assert'; import supertest from 'supertest'; import { - AcAuthProvider, Application, + AuthProvider, BypassAcAuthProvider, } from '../../../main/index.js'; import { AccessRouter } from '../../routes/access.js'; @@ -12,9 +12,14 @@ describe('BypassAuthProvider', () => { class App extends Application { + override createGlobalScope() { + const mesh = super.createGlobalScope(); + mesh.service(AuthProvider, BypassAcAuthProvider); + return mesh; + } + override createHttpRequestScope() { const mesh = super.createHttpRequestScope(); - mesh.service(AcAuthProvider, BypassAcAuthProvider); mesh.service(AccessRouter); return mesh; }