@prosopo/provider-mock 2.8.36 → 2.8.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/db.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { MongoDatabase } from "@prosopo/database";
2
+ import type { Logger } from "@prosopo/logger";
3
+ import type { Tables } from "@prosopo/types-database";
4
+ import type mongoose from "mongoose";
5
+ declare enum TableNames {
6
+ ja4 = "ja4"
7
+ }
8
+ type JA4Data = {
9
+ application?: string;
10
+ library?: string;
11
+ device?: string;
12
+ os?: string;
13
+ user_agent_string: string;
14
+ certificate_authority?: string;
15
+ observation_count?: number;
16
+ verified?: boolean;
17
+ notes?: string;
18
+ ja4_fingerprint: string;
19
+ ja4_fingerprint_string?: string;
20
+ ja4s_fingerprint?: string;
21
+ ja4h_fingerprint?: string;
22
+ ja4x_fingerprint?: string;
23
+ ja4t_fingerprint?: string;
24
+ ja4ts_fingerprint?: string;
25
+ ja4tscan_fingerprint?: string;
26
+ };
27
+ type JA4Record = JA4Data & mongoose.Document;
28
+ export declare class JA4Database extends MongoDatabase {
29
+ tables: Tables<TableNames>;
30
+ constructor(url: string, dbname?: string, authSource?: string, logger?: Logger);
31
+ connect(): Promise<void>;
32
+ getTables(): Tables<TableNames>;
33
+ getJA4Records(): Promise<JA4Record[]>;
34
+ getJA4RecordByFingerprintAndUserAgent(ja4Fingerprint: string, userAgentString: string): Promise<JA4Record | null>;
35
+ addOrUpdateJA4Record(ja4Record: JA4Data): Promise<JA4Record | null>;
36
+ }
37
+ export {};
38
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAGrC,aAAK,UAAU;IACd,GAAG,QAAQ;CACX;AAED,KAAK,OAAO,GAAG;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,KAAK,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC;AAgC7C,qBAAa,WAAY,SAAQ,aAAa;IAC7C,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;gBAG1B,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM;IAMD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IASvC,SAAS,IAAI,MAAM,CAAC,UAAU,CAAC;IAUzB,aAAa,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAIrC,qCAAqC,CAC1C,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,GACrB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAStB,oBAAoB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;CAezE"}
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":""}
package/dist/start.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { handleErrors } from "@prosopo/api-express-router";
2
- import { getLogger, LogLevel } from "@prosopo/common";
3
2
  import { i18nMiddleware } from "@prosopo/locale";
3
+ import { getLogger, LogLevel } from "@prosopo/logger";
4
4
  import cors from "cors";
5
5
  import express from "express";
6
6
  import { prosopoRouter } from "./api.js";
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,gCAAgC,CAAC,CAAC;AAE/E,KAAK,UAAU,QAAQ;IACtB,MAAM,MAAM,GAAG,OAAO,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,CAAC;IAEvB,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACnB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3B,MAAM,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAEzB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAClB,GAAG,EAAE,6CAA6C,OAAO,EAAE;SAC3D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAAC;IACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/provider-mock",
3
- "version": "2.8.36",
3
+ "version": "2.8.96",
4
4
  "author": "PROSOPO LIMITED <info@prosopo.io>",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -18,27 +18,29 @@
18
18
  "npm": "^11"
19
19
  },
20
20
  "scripts": {
21
- "build": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.esm.config.ts --mode $NODE_ENV",
21
+ "build": "npm run build:cross-env -- --mode ${NODE_ENV:-development}",
22
+ "build:cross-env": "vite build --config vite.esm.config.ts",
22
23
  "build:tsc": "tsc --build --verbose",
23
24
  "build:cjs": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV",
24
25
  "typecheck": "tsc --project tsconfig.types.json",
25
26
  "bundle": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.config.ts --mode $NODE_ENV"
26
27
  },
27
28
  "dependencies": {
28
- "@prosopo/api-express-router": "3.0.40",
29
- "@prosopo/common": "3.1.26",
30
- "@prosopo/database": "3.5.6",
31
- "@prosopo/dotenv": "3.0.31",
32
- "@prosopo/locale": "3.1.26",
33
- "@prosopo/provider": "3.13.7",
34
- "@prosopo/types": "3.6.4",
35
- "@prosopo/types-database": "4.0.6",
29
+ "@prosopo/api-express-router": "3.1.17",
30
+ "@prosopo/common": "3.1.38",
31
+ "@prosopo/logger": "1.0.2",
32
+ "@prosopo/database": "3.13.7",
33
+ "@prosopo/dotenv": "3.0.43",
34
+ "@prosopo/locale": "3.2.4",
35
+ "@prosopo/provider": "4.7.1",
36
+ "@prosopo/types": "4.3.0",
37
+ "@prosopo/types-database": "4.8.0",
36
38
  "cors": "2.8.5",
37
39
  "express": "4.21.2",
38
40
  "mongoose": "8.13.0"
39
41
  },
40
42
  "devDependencies": {
41
- "@prosopo/config": "3.1.26",
43
+ "@prosopo/config": "3.3.1",
42
44
  "@types/node": "22.10.2",
43
45
  "@vitest/coverage-v8": "3.2.4",
44
46
  "concurrently": "9.0.1",
@@ -52,7 +54,8 @@
52
54
  },
53
55
  "repository": {
54
56
  "type": "git",
55
- "url": "git+ssh://git@github.com/prosopo/provider.git"
57
+ "url": "git+https://github.com/prosopo/captcha.git",
58
+ "directory": "demos/provider-mock"
56
59
  },
57
60
  "bugs": {
58
61
  "url": "https://github.com/prosopo/captcha/issues"
package/src/api.ts ADDED
@@ -0,0 +1,117 @@
1
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import { ProsopoApiError } from "@prosopo/common";
16
+ import { getLogger } from "@prosopo/logger";
17
+ import { getJA4 } from "@prosopo/provider";
18
+ import {
19
+ ClientApiPaths,
20
+ VerifySolutionBody,
21
+ decodeProcaptchaOutput,
22
+ } from "@prosopo/types";
23
+ import type { VerifySolutionBodyTypeOutput } from "@prosopo/types";
24
+ import express, { type Router } from "express";
25
+ import { JA4Database } from "./db.js";
26
+
27
+ /**
28
+ * Returns a router connected to the database which can interact with the Proposo protocol
29
+ *
30
+ * @return {Router} - A middleware router that can interact with the Prosopo protocol
31
+ */
32
+ export function prosopoRouter(): Router {
33
+ const router = express.Router();
34
+ const db = new JA4Database(
35
+ process.env.MONGO_URL || "mongodb://localhost:27017",
36
+ process.env.MONGO_DBNAME || "client",
37
+ process.env.MONGO_AUTH_SOURCE || "admin",
38
+ );
39
+
40
+ /**
41
+ * Verifies a user's solution as being approved or not
42
+ *
43
+ * @param {string} userAccount - Dapp User id
44
+ * @param {string} commitmentId - The captcha solution to look up
45
+ */
46
+ router.post(
47
+ ClientApiPaths.VerifyImageCaptchaSolutionDapp,
48
+ async (req, res, next) => {
49
+ let body: VerifySolutionBodyTypeOutput;
50
+ try {
51
+ body = VerifySolutionBody.parse(req.body);
52
+ } catch (err) {
53
+ return next(
54
+ new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
55
+ context: { error: err, code: 400 },
56
+ logLevel: "info",
57
+ }),
58
+ );
59
+ }
60
+ try {
61
+ const { token } = body;
62
+ const { user, dapp, commitmentId } = decodeProcaptchaOutput(token);
63
+ const testCommitmentId = "0x123456789test";
64
+ const testAccount = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
65
+ const testDapp = "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM";
66
+ let statusMessage = "API.USER_NOT_VERIFIED";
67
+ let approved = false;
68
+ if (
69
+ (user && user === testAccount) ||
70
+ (commitmentId && commitmentId === testCommitmentId) ||
71
+ (dapp && dapp === testDapp)
72
+ ) {
73
+ approved = true;
74
+ statusMessage = "API.USER_VERIFIED";
75
+ return res.json({
76
+ status: req.t(statusMessage),
77
+ verified: approved,
78
+ commitmentId: testCommitmentId,
79
+ });
80
+ }
81
+
82
+ return res.json({
83
+ status: req.t(statusMessage),
84
+ verified: false,
85
+ });
86
+ } catch (err) {
87
+ return next(
88
+ new ProsopoApiError("API.UNKNOWN", {
89
+ context: { error: err, code: 500 },
90
+ }),
91
+ );
92
+ }
93
+ },
94
+ );
95
+
96
+ router.get("/test", async (req, res) => {
97
+ try {
98
+ const logger = getLogger("info", import.meta.url);
99
+ const ja4PlusFingerprint = await getJA4(req.headers, logger);
100
+ await db.connect();
101
+ await db.addOrUpdateJA4Record({
102
+ ja4_fingerprint: ja4PlusFingerprint.ja4PlusFingerprint,
103
+ user_agent_string: req.headers["user-agent"] || "",
104
+ });
105
+ await db.close();
106
+ return res.json({
107
+ ja4: ja4PlusFingerprint.ja4PlusFingerprint,
108
+ ua: req.headers["user-agent"],
109
+ });
110
+ } catch (e) {
111
+ console.error("Error parsing ClientHello:", e);
112
+ return res.status(500).send("Error parsing ClientHello.");
113
+ }
114
+ });
115
+
116
+ return router;
117
+ }
package/src/db.ts ADDED
@@ -0,0 +1,141 @@
1
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import { ProsopoDBError } from "@prosopo/common";
16
+ import { MongoDatabase } from "@prosopo/database";
17
+ import type { Logger } from "@prosopo/logger";
18
+ import type { Tables } from "@prosopo/types-database";
19
+ import type mongoose from "mongoose";
20
+ import { Schema } from "mongoose";
21
+
22
+ enum TableNames {
23
+ ja4 = "ja4",
24
+ }
25
+
26
+ type JA4Data = {
27
+ application?: string;
28
+ library?: string;
29
+ device?: string;
30
+ os?: string;
31
+ user_agent_string: string;
32
+ certificate_authority?: string;
33
+ observation_count?: number;
34
+ verified?: boolean;
35
+ notes?: string;
36
+ ja4_fingerprint: string;
37
+ ja4_fingerprint_string?: string;
38
+ ja4s_fingerprint?: string;
39
+ ja4h_fingerprint?: string;
40
+ ja4x_fingerprint?: string;
41
+ ja4t_fingerprint?: string;
42
+ ja4ts_fingerprint?: string;
43
+ ja4tscan_fingerprint?: string;
44
+ };
45
+
46
+ type JA4Record = JA4Data & mongoose.Document;
47
+
48
+ const JA4Schema = new Schema({
49
+ application: String,
50
+ library: String,
51
+ device: String,
52
+ os: String,
53
+ user_agent_string: String,
54
+ certificate_authority: String,
55
+ observation_count: { type: Number, default: 1 },
56
+ verified: { type: Boolean, default: false },
57
+ notes: { type: String, default: "" },
58
+ ja4_fingerprint: { type: String, required: true },
59
+ ja4_fingerprint_string: String,
60
+ ja4s_fingerprint: String,
61
+ ja4h_fingerprint: String,
62
+ ja4x_fingerprint: String,
63
+ ja4t_fingerprint: String,
64
+ ja4ts_fingerprint: String,
65
+ ja4tscan_fingerprint: String,
66
+ });
67
+
68
+ JA4Schema.index({ ja4_fingerprint: 1, user_agent_string: 1 }, { unique: true });
69
+
70
+ const DATA_TABLES = [
71
+ {
72
+ collectionName: TableNames.ja4,
73
+ modelName: TableNames.ja4,
74
+ schema: JA4Schema,
75
+ },
76
+ ];
77
+
78
+ export class JA4Database extends MongoDatabase {
79
+ tables: Tables<TableNames>;
80
+
81
+ constructor(
82
+ url: string,
83
+ dbname?: string,
84
+ authSource?: string,
85
+ logger?: Logger,
86
+ ) {
87
+ super(url, dbname, authSource, logger);
88
+ this.tables = {} as Tables<TableNames>;
89
+ }
90
+
91
+ override async connect(): Promise<void> {
92
+ await super.connect();
93
+ DATA_TABLES.map(({ collectionName, modelName, schema }) => {
94
+ if (this.connection) {
95
+ this.tables[collectionName] = this.connection.model(modelName, schema);
96
+ }
97
+ });
98
+ }
99
+
100
+ getTables(): Tables<TableNames> {
101
+ if (!this.tables) {
102
+ throw new ProsopoDBError("DATABASE.TABLES_UNDEFINED", {
103
+ context: { failedFuncName: this.getTables.name },
104
+ logger: this.logger,
105
+ });
106
+ }
107
+ return this.tables;
108
+ }
109
+
110
+ async getJA4Records(): Promise<JA4Record[]> {
111
+ return this.tables.ja4.find<JA4Record>({});
112
+ }
113
+
114
+ async getJA4RecordByFingerprintAndUserAgent(
115
+ ja4Fingerprint: string,
116
+ userAgentString: string,
117
+ ): Promise<JA4Record | null> {
118
+ return this.tables.ja4.findOne<JA4Record>({
119
+ ja4_fingerprint: ja4Fingerprint,
120
+ user_agent_string: userAgentString,
121
+ });
122
+ }
123
+
124
+ // add a ja4 record or update an existing one if the user agent string and ja4 fingerprint match, incrementing the observation count
125
+
126
+ async addOrUpdateJA4Record(ja4Record: JA4Data): Promise<JA4Record | null> {
127
+ const existingRecord = await this.getJA4RecordByFingerprintAndUserAgent(
128
+ ja4Record.ja4_fingerprint,
129
+ ja4Record.user_agent_string ? ja4Record.user_agent_string : "",
130
+ );
131
+ if (existingRecord) {
132
+ existingRecord.observation_count =
133
+ (existingRecord.observation_count || 0) + 1;
134
+ await existingRecord.save();
135
+ return existingRecord;
136
+ }
137
+ const newRecord = new this.tables.ja4(ja4Record);
138
+ await newRecord.save();
139
+ return newRecord;
140
+ }
141
+ }
package/src/start.ts ADDED
@@ -0,0 +1,43 @@
1
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import { handleErrors } from "@prosopo/api-express-router";
16
+ import { i18nMiddleware } from "@prosopo/locale";
17
+ import { LogLevel, getLogger } from "@prosopo/logger";
18
+ import cors from "cors";
19
+ import express from "express";
20
+ import { prosopoRouter } from "./api.js";
21
+ const logger = getLogger(LogLevel.enum.info, "prosopo:provider-mock:start.ts");
22
+
23
+ async function startApi() {
24
+ const apiApp = express();
25
+ const apiPort = "9229";
26
+
27
+ apiApp.use(cors());
28
+ apiApp.use(express.json());
29
+ apiApp.use(await i18nMiddleware({}));
30
+ apiApp.use(prosopoRouter());
31
+ apiApp.use(handleErrors);
32
+
33
+ apiApp.listen(apiPort, () => {
34
+ logger.info(() => ({
35
+ msg: `Prosopo app listening at http://localhost:${apiPort}`,
36
+ }));
37
+ });
38
+ }
39
+
40
+ startApi().catch((error) => {
41
+ logger.error(() => ({ err: error, msg: "Failed to start API" }));
42
+ process.exit(1);
43
+ });
@@ -0,0 +1,41 @@
1
+ {
2
+ "extends": "../../tsconfig.cjs.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist",
6
+ "module": "NodeNext"
7
+ },
8
+ "include": ["src", "src/**/*.json"],
9
+ "references": [
10
+ {
11
+ "path": "../../dev/config/tsconfig.cjs.json"
12
+ },
13
+ {
14
+ "path": "../../packages/api-express-router"
15
+ },
16
+ {
17
+ "path": "../../packages/common"
18
+ },
19
+ {
20
+ "path": "../../packages/logger"
21
+ },
22
+ {
23
+ "path": "../../packages/provider"
24
+ },
25
+ {
26
+ "path": "../../packages/types"
27
+ },
28
+ {
29
+ "path": "../../packages/database"
30
+ },
31
+ {
32
+ "path": "../../packages/dotenv"
33
+ },
34
+ {
35
+ "path": "../../packages/locale"
36
+ },
37
+ {
38
+ "path": "../../packages/types-database"
39
+ }
40
+ ]
41
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "extends": "../../tsconfig.esm.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist",
6
+ "module": "NodeNext"
7
+ },
8
+ "include": [
9
+ "src",
10
+ "src/**/*.json",
11
+ "src/**/*.ts",
12
+ "src/**/*.tsx",
13
+ "src/**/*.d.ts"
14
+ ],
15
+ "references": [
16
+ {
17
+ "path": "../../dev/config/tsconfig.json"
18
+ },
19
+ {
20
+ "path": "../../packages/api-express-router"
21
+ },
22
+ {
23
+ "path": "../../packages/common"
24
+ },
25
+ {
26
+ "path": "../../packages/logger"
27
+ },
28
+ {
29
+ "path": "../../packages/provider"
30
+ },
31
+ {
32
+ "path": "../../packages/types"
33
+ },
34
+ {
35
+ "path": "../../packages/database"
36
+ },
37
+ {
38
+ "path": "../../packages/dotenv"
39
+ },
40
+ {
41
+ "path": "../../packages/locale"
42
+ },
43
+ {
44
+ "path": "../../packages/types-database"
45
+ }
46
+ ]
47
+ }