From 25bd3f5b547c70a2676eca05581f79924821cc38 Mon Sep 17 00:00:00 2001 From: Chizzy Date: Mon, 23 Mar 2026 19:19:26 +0100 Subject: [PATCH] feat: campaigns API (Cairo) --- .env.example | 11 + .gitignore | 9 +- CODEBASE_FINDINGS.md | 86 +++ README.md | 44 +- combined.log | 3 - dist/appMiddlewares/index.js | 2 +- dist/appMiddlewares/jwtAuth.api.js | 44 ++ .../v1/Donation/donation.controller.js | 1 + dist/components/v1/Donation/donation.dto.js | 2 + .../components/v1/Donation/donation.entity.js | 66 ++ .../v1/Donation/donation.service.js | 1 + .../v1/Donation/donation.validation.js | 17 + dist/components/v1/audit/auditLog.entity.js | 73 ++ .../v1/campaign/campaign.controller.js | 81 +++ .../components/v1/campaign/campaign.entity.js | 73 ++ .../components/v1/campaign/campaign.routes.js | 30 + .../v1/campaign/campaign.service.js | 81 +++ .../v1/campaign/campaign.validation.js | 47 ++ .../distribution/distribution.controller.js | 61 ++ .../v1/distribution/distribution.dto.js | 2 + .../v1/distribution/distribution.service.js | 107 +++ .../distribution/distribution.validation.js | 39 ++ .../v1/distribution/distrubtion.routes.js | 13 + .../permission.controller.js | 7 - dist/components/v1/routes.api.v1.js | 10 + dist/components/v1/routes.v1.js | 6 +- dist/components/v1/user/user.entity.js | 5 + dist/components/v1/wallet/wallet.entity.js | 2 +- dist/config/persistence/data-source.js | 45 +- dist/config/persistence/seeder.js | 129 +--- dist/index.js | 82 ++- dist/services/cairo/campaignFactory.client.js | 11 + dist/services/cairo/campaignFactory.mock.js | 13 + .../cairo/campaignFactory.starknet.js | 71 ++ dist/services/cairo/retry.js | 28 + dist/utils/apiResponse.js | 21 + dist/utils/index.js | 40 +- error.log | 1 - eslint.config.mjs | 8 + package-lock.json | 663 +++++++++++++++++- package.json | 22 +- src/__tests__/cairo.client.mock.test.ts | 26 + src/__tests__/campaign.service.test.ts | 140 ++++ src/__tests__/campaign.validation.test.ts | 58 ++ src/__tests__/retry.test.ts | 37 + src/appMiddlewares/index.ts | 2 +- src/appMiddlewares/jwtAuth.api.ts | 55 ++ .../v1/Donation/donation.validation.ts | 1 - src/components/v1/audit/auditLog.entity.ts | 41 ++ .../v1/campaign/campaign.controller.ts | 97 +++ src/components/v1/campaign/campaign.entity.ts | 41 ++ src/components/v1/campaign/campaign.routes.ts | 31 + .../v1/campaign/campaign.service.ts | 98 +++ .../v1/campaign/campaign.validation.ts | 46 ++ .../distribution/distribution.controller.ts | 38 +- .../v1/distribution/distribution.service.ts | 14 + .../v1/distribution/distrubtion.routes.ts | 4 +- src/components/v1/platform/platform.utils.ts | 2 +- .../permission.controller.ts | 11 - src/components/v1/routes.api.v1.ts | 10 + src/components/v1/routes.v1.ts | 4 - src/components/v1/user/user.entity.ts | 3 + src/components/v1/wallet/wallet.entity.ts | 2 +- src/config/persistence/data-source.ts | 126 ++-- src/config/persistence/seeder.ts | 136 +--- src/index.ts | 50 +- .../CreateCampaignAndAudit1760000000001.js | 63 ++ src/migrations/CreateCoreEntities.js | 22 +- src/services/cairo/campaignFactory.client.ts | 26 + src/services/cairo/campaignFactory.mock.ts | 12 + .../cairo/campaignFactory.starknet.ts | 77 ++ src/services/cairo/retry.ts | 33 + src/types/global.ts | 6 + src/utils/apiResponse.ts | 26 + src/utils/index.ts | 8 +- tsconfig.build.tsbuildinfo | 1 - 76 files changed, 2979 insertions(+), 425 deletions(-) create mode 100644 CODEBASE_FINDINGS.md delete mode 100644 combined.log create mode 100644 dist/appMiddlewares/jwtAuth.api.js create mode 100644 dist/components/v1/Donation/donation.controller.js create mode 100644 dist/components/v1/Donation/donation.dto.js create mode 100644 dist/components/v1/Donation/donation.entity.js create mode 100644 dist/components/v1/Donation/donation.service.js create mode 100644 dist/components/v1/Donation/donation.validation.js create mode 100644 dist/components/v1/audit/auditLog.entity.js create mode 100644 dist/components/v1/campaign/campaign.controller.js create mode 100644 dist/components/v1/campaign/campaign.entity.js create mode 100644 dist/components/v1/campaign/campaign.routes.js create mode 100644 dist/components/v1/campaign/campaign.service.js create mode 100644 dist/components/v1/campaign/campaign.validation.js create mode 100644 dist/components/v1/distribution/distribution.controller.js create mode 100644 dist/components/v1/distribution/distribution.dto.js create mode 100644 dist/components/v1/distribution/distribution.service.js create mode 100644 dist/components/v1/distribution/distribution.validation.js create mode 100644 dist/components/v1/distribution/distrubtion.routes.js create mode 100644 dist/components/v1/routes.api.v1.js create mode 100644 dist/services/cairo/campaignFactory.client.js create mode 100644 dist/services/cairo/campaignFactory.mock.js create mode 100644 dist/services/cairo/campaignFactory.starknet.js create mode 100644 dist/services/cairo/retry.js create mode 100644 dist/utils/apiResponse.js delete mode 100644 error.log create mode 100644 src/__tests__/cairo.client.mock.test.ts create mode 100644 src/__tests__/campaign.service.test.ts create mode 100644 src/__tests__/campaign.validation.test.ts create mode 100644 src/__tests__/retry.test.ts create mode 100644 src/appMiddlewares/jwtAuth.api.ts create mode 100644 src/components/v1/audit/auditLog.entity.ts create mode 100644 src/components/v1/campaign/campaign.controller.ts create mode 100644 src/components/v1/campaign/campaign.entity.ts create mode 100644 src/components/v1/campaign/campaign.routes.ts create mode 100644 src/components/v1/campaign/campaign.service.ts create mode 100644 src/components/v1/campaign/campaign.validation.ts create mode 100644 src/components/v1/routes.api.v1.ts create mode 100644 src/migrations/CreateCampaignAndAudit1760000000001.js create mode 100644 src/services/cairo/campaignFactory.client.ts create mode 100644 src/services/cairo/campaignFactory.mock.ts create mode 100644 src/services/cairo/campaignFactory.starknet.ts create mode 100644 src/services/cairo/retry.ts create mode 100644 src/utils/apiResponse.ts delete mode 100644 tsconfig.build.tsbuildinfo diff --git a/.env.example b/.env.example index 675e7ef..4546c36 100644 --- a/.env.example +++ b/.env.example @@ -29,3 +29,14 @@ DATABASE_USERNAME=postgres DATABASE_PASSWORD=postgres ################################################################################ + +# Cairo/StarkNet integration +# Set CAIRO_MOCK=true for local development without a chain connection. +CAIRO_MOCK=true + +# When CAIRO_MOCK is false/empty, set these: +# CAIRO_RPC_URL= +# CAIRO_ACCOUNT_ADDRESS= +# CAIRO_PRIVATE_KEY= +# CAIRO_FACTORY_CONTRACT_ADDRESS= +# Optional: CAMPAIGN_CREATED_EVENT_KEY= diff --git a/.gitignore b/.gitignore index 97aca2e..baaff48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ .env -node_modules \ No newline at end of file +node_modules +docs +scripts +coverage +coverage/** +*.log + +*.tsbuildinfo diff --git a/CODEBASE_FINDINGS.md b/CODEBASE_FINDINGS.md new file mode 100644 index 0000000..a16d8b2 --- /dev/null +++ b/CODEBASE_FINDINGS.md @@ -0,0 +1,86 @@ +# Codebase Findings + Setup Notes (Fundable Backend API) + +Date: 2026-03-23 + +This repo is a TypeScript/Node/Express API backed by PostgreSQL via TypeORM. The main entrypoint is `src/index.ts` and the compiled output is `dist/`. + +## Quick Local Setup + +1) Install deps: + +- `npm ci` + +2) Create `.env`: + +- `Copy-Item .env.example .env` + +Minimal `.env` to boot locally: + +```env +NODE_ENV=local_dev +PORT=8002 + +DATABASE_HOST=localhost +DATABASE_PORT=5432 +DATABASE_USERNAME=postgres +DATABASE_PASSWORD=postgres +DATABASE_NAME=fundable_db + +# Cairo/StarkNet integration (local mock) +CAIRO_MOCK=true +``` + +3) Ensure DB exists + migrate: + +- `npm run ensure-db` +- `npm run typeorm -- migration:run` + +4) Build + start: + +- `npm run build` +- `node --env-file=.env dist/index.js` + +## Startup Notes + +- Seeding is disabled (no-op) to avoid missing-module startup blockers: `src/config/persistence/seeder.ts`. +- DB config is validated early and fails fast if required env vars are missing: `src/config/persistence/data-source.ts`. + +## Current Routes + +- Health: `GET /` → `{ success: false, message: ... }` +- V1 (existing): `/v1/*` + - Distributions: `GET /v1/distributions`, `POST /v1/distributions` +- API V1 (new): `/api/v1/*` + - Campaigns (new): `POST /api/v1/campaigns` (JWT required, 5/hour per user) + +## Campaign Creation (Cairo) + +Endpoint: + +- `POST /api/v1/campaigns` + +Body: + +```json +{ + "campaign_ref": "ABCDE", + "target_amount": "1000", + "donation_token": "0x1" +} +``` + +Behavior: + +- Validates input (5-char ref, positive u256 string, StarkNet address format). +- Enforces uniqueness on `campaign_ref` (DB + unique index). +- Requires JWT with `sub` and a `walletAddress` (or `address`) claim. +- Rate limited: max 5 campaign creates per user per hour. +- Persists to DB (`campaigns` table) and writes an audit entry (`audit_logs`). +- On-chain integration: + - Local dev: `CAIRO_MOCK=true` returns mock tx hash + campaign id. + - Real chain: set `CAIRO_MOCK=false` and provide `CAIRO_RPC_URL`, `CAIRO_ACCOUNT_ADDRESS`, `CAIRO_PRIVATE_KEY`, `CAIRO_FACTORY_CONTRACT_ADDRESS`. + +## Tests + +- `npm test` runs `node --test` with `c8` coverage checks (scoped to the new feature modules via `package.json`). + diff --git a/README.md b/README.md index c66e2e3..7d659e8 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,50 @@ src/ To run tests, use the following command: ```bash -pnpm test +npm test ``` -Tests are located in the `__tests__` folder and use a library like **Jest** for unit and integration tests. +Tests are located in `src/__tests__` and run with Node's test runner (`node --test`) + coverage (`c8`). + +--- + +## API: Create Campaign (Cairo) + +**POST** `/api/v1/campaigns` (JWT required) + +Request body: + +```json +{ + "campaign_ref": "ABCDE", + "target_amount": "1000", + "donation_token": "0x1" +} +``` + +Notes: + +- Rate limit: max **5** campaign creations per user per hour. +- Auth: send `Authorization: Bearer ` and include a `walletAddress` (or `address`) claim in the token. + +### Cairo/StarkNet config (non-mock) + +Set these env vars to enable real on-chain calls: + +- `CAIRO_RPC_URL` +- `CAIRO_ACCOUNT_ADDRESS` +- `CAIRO_PRIVATE_KEY` +- `CAIRO_FACTORY_CONTRACT_ADDRESS` + +Optional: + +- `CAMPAIGN_CREATED_EVENT_KEY` (event key to extract `campaign_id` from the tx receipt; otherwise falls back to tx hash) + +### Mock mode (local) + +For local development without a chain connection: + +- `CAIRO_MOCK=true` --- diff --git a/combined.log b/combined.log deleted file mode 100644 index 8f0fb53..0000000 --- a/combined.log +++ /dev/null @@ -1,3 +0,0 @@ -{"level":"info","message":"[+]Logging Service Started","timestamp":"2025-07-02T00:00:04.163Z"} -{"level":"info","message":"[+] CLOUD Server Running ... on Port -> 8002","timestamp":"2025-07-02T00:00:04.166Z"} -{"level":"error","message":"{\"name\":\"AggregateError\",\"message\":\"\",\"path1\":\"at internalConnectMultiple (node:net:1117:18)\",\"path2\":\"at afterConnectMultiple (node:net:1684:7)\"}","timestamp":"2025-07-02T00:00:04.171Z"} diff --git a/dist/appMiddlewares/index.js b/dist/appMiddlewares/index.js index 98afe9a..700aba8 100644 --- a/dist/appMiddlewares/index.js +++ b/dist/appMiddlewares/index.js @@ -5,7 +5,7 @@ const verifyAllowedMethods = (req, res, next) => { try { if (req.method === 'OPTIONS') { res.header('Access-Control-Allow-Methods', 'POST, PUT, PATCH, GET, DELETE'); - return res.status(403).json('Invalid header method'); + return res.sendStatus(204); } else return next(); diff --git a/dist/appMiddlewares/jwtAuth.api.js b/dist/appMiddlewares/jwtAuth.api.js new file mode 100644 index 0000000..c2cf054 --- /dev/null +++ b/dist/appMiddlewares/jwtAuth.api.js @@ -0,0 +1,44 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.requireJwtAuthApi = void 0; +const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); +const config_1 = __importDefault(require("../config")); +const apiResponse_1 = require("../utils/apiResponse"); +const requireJwtAuthApi = (req, res, next) => { + const header = req.headers.authorization; + const token = header?.startsWith("Bearer ") ? header.slice("Bearer ".length).trim() : ""; + if (!token) { + return (0, apiResponse_1.sendError)(res, 401, { + code: "AUTH_MISSING_TOKEN", + message: "Missing authentication token", + }); + } + try { + const decoded = jsonwebtoken_1.default.verify(token, config_1.default.authConfig.jwtSecret); + const userId = decoded.sub ?? decoded.userId ?? decoded.id; + if (!userId) { + return (0, apiResponse_1.sendError)(res, 401, { + code: "AUTH_INVALID_TOKEN", + message: "Invalid authentication token", + }); + } + req.auth = { + userId: String(userId), + walletAddress: decoded.walletAddress ?? decoded.address, + email: decoded.email, + claims: decoded, + }; + return next(); + } + catch (error) { + return (0, apiResponse_1.sendError)(res, 401, { + code: "AUTH_INVALID_TOKEN", + message: "Invalid authentication token", + details: error instanceof Error ? { name: error.name } : {}, + }); + } +}; +exports.requireJwtAuthApi = requireJwtAuthApi; diff --git a/dist/components/v1/Donation/donation.controller.js b/dist/components/v1/Donation/donation.controller.js new file mode 100644 index 0000000..3918c74 --- /dev/null +++ b/dist/components/v1/Donation/donation.controller.js @@ -0,0 +1 @@ +"use strict"; diff --git a/dist/components/v1/Donation/donation.dto.js b/dist/components/v1/Donation/donation.dto.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/components/v1/Donation/donation.dto.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/dist/components/v1/Donation/donation.entity.js b/dist/components/v1/Donation/donation.entity.js new file mode 100644 index 0000000..a30cbef --- /dev/null +++ b/dist/components/v1/Donation/donation.entity.js @@ -0,0 +1,66 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CampaignEntity = void 0; +const typeorm_1 = require("typeorm"); +const utils_1 = require("../../../utils"); +let CampaignEntity = class CampaignEntity { + campaignId; + campaignRef; + targetAmount; + donationToken; + transactionHash; + createdAt; + generateId() { + if (!this.campaignId) { + this.campaignId = (0, utils_1.uuid)(); + } + } +}; +exports.CampaignEntity = CampaignEntity; +__decorate([ + (0, typeorm_1.PrimaryColumn)("text", { name: "campaign_id" }), + __metadata("design:type", String) +], CampaignEntity.prototype, "campaignId", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "campaign_ref", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "campaignRef", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "target_amount", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "targetAmount", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "donation_token", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "donationToken", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "transaction_hash", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "transactionHash", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)({ + name: "created_at", + type: "timestamp", + default: () => "CURRENT_TIMESTAMP", + }), + __metadata("design:type", Date) +], CampaignEntity.prototype, "createdAt", void 0); +__decorate([ + (0, typeorm_1.BeforeInsert)(), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], CampaignEntity.prototype, "generateId", null); +exports.CampaignEntity = CampaignEntity = __decorate([ + (0, typeorm_1.Entity)("Campaign") +], CampaignEntity); +exports.default = CampaignEntity; diff --git a/dist/components/v1/Donation/donation.service.js b/dist/components/v1/Donation/donation.service.js new file mode 100644 index 0000000..3918c74 --- /dev/null +++ b/dist/components/v1/Donation/donation.service.js @@ -0,0 +1 @@ +"use strict"; diff --git a/dist/components/v1/Donation/donation.validation.js b/dist/components/v1/Donation/donation.validation.js new file mode 100644 index 0000000..01fa69c --- /dev/null +++ b/dist/components/v1/Donation/donation.validation.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createDonationSchema = void 0; +const zod_1 = require("zod"); +const enums_1 = require("../../../types/enums"); +const ethereumAddressRegex = /^0x[a-fA-F0-9]{40}$/; +const decimalStringRegex = /^\d+(\.\d+)?$/; +exports.createDonationSchema = zod_1.z.object({ + campaignId: zod_1.z.string().min(1, "campaignId is required"), + donorAddress: zod_1.z.string().regex(ethereumAddressRegex, "donorAddress must be a valid Ethereum address"), + donationToken: zod_1.z.string().regex(ethereumAddressRegex, "donationToken must be a valid Ethereum address"), + donationAmount: zod_1.z.string().regex(decimalStringRegex, "donationAmount must be a valid decimal string"), + transactionHash: zod_1.z.string().regex(/^0x([A-Fa-f0-9]{64})$/, "transactionHash must be a valid transaction hash"), + network: zod_1.z.nativeEnum(enums_1.Network, { + errorMap: () => ({ message: "network must be a valid Network" }), + }), +}); diff --git a/dist/components/v1/audit/auditLog.entity.js b/dist/components/v1/audit/auditLog.entity.js new file mode 100644 index 0000000..4a995a3 --- /dev/null +++ b/dist/components/v1/audit/auditLog.entity.js @@ -0,0 +1,73 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AuditLogEntity = void 0; +const typeorm_1 = require("typeorm"); +const utils_1 = require("../../../utils"); +let AuditLogEntity = class AuditLogEntity { + auditId; + userId; + action; + entity; + entityId; + details; + createdAt; + ensureId() { + if (!this.auditId) + this.auditId = (0, utils_1.uuid)(); + } +}; +exports.AuditLogEntity = AuditLogEntity; +__decorate([ + (0, typeorm_1.PrimaryColumn)("text", { name: "audit_id" }), + __metadata("design:type", String) +], AuditLogEntity.prototype, "auditId", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "user_id", nullable: false }), + __metadata("design:type", String) +], AuditLogEntity.prototype, "userId", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { nullable: false }), + __metadata("design:type", String) +], AuditLogEntity.prototype, "action", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { nullable: false }), + __metadata("design:type", String) +], AuditLogEntity.prototype, "entity", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "entity_id", nullable: false }), + __metadata("design:type", String) +], AuditLogEntity.prototype, "entityId", void 0); +__decorate([ + (0, typeorm_1.Column)("jsonb", { nullable: true }), + __metadata("design:type", Object) +], AuditLogEntity.prototype, "details", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)({ + name: "created_at", + type: "timestamp", + precision: 3, + default: () => "CURRENT_TIMESTAMP", + }), + __metadata("design:type", Date) +], AuditLogEntity.prototype, "createdAt", void 0); +__decorate([ + (0, typeorm_1.BeforeInsert)(), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], AuditLogEntity.prototype, "ensureId", null); +exports.AuditLogEntity = AuditLogEntity = __decorate([ + (0, typeorm_1.Entity)("audit_logs"), + (0, typeorm_1.Index)("audit_logs_user_id_idx", ["userId"]), + (0, typeorm_1.Index)("audit_logs_entity_idx", ["entity", "entityId"]) +], AuditLogEntity); +exports.default = AuditLogEntity; diff --git a/dist/components/v1/campaign/campaign.controller.js b/dist/components/v1/campaign/campaign.controller.js new file mode 100644 index 0000000..64a56a0 --- /dev/null +++ b/dist/components/v1/campaign/campaign.controller.js @@ -0,0 +1,81 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createCampaign = void 0; +const zod_1 = require("zod"); +const apiResponse_1 = require("../../../utils/apiResponse"); +const campaign_validation_1 = require("./campaign.validation"); +const data_source_1 = __importDefault(require("../../../config/persistence/data-source")); +const campaign_entity_1 = __importDefault(require("./campaign.entity")); +const auditLog_entity_1 = __importDefault(require("../audit/auditLog.entity")); +const campaign_service_1 = require("./campaign.service"); +const campaignFactory_client_1 = require("../../../services/cairo/campaignFactory.client"); +const logger_1 = __importDefault(require("../../../utils/logger")); +const wallet_entity_1 = __importDefault(require("../wallet/wallet.entity")); +const user_entity_1 = __importDefault(require("../user/user.entity")); +const mapCreateCampaignError = (error) => { + const code = typeof error?.code === "string" ? error.code : "INTERNAL_ERROR"; + switch (code) { + case "DUPLICATE_CAMPAIGN_REF": + return { status: 409, code, message: "campaign_ref already exists" }; + case "INSUFFICIENT_BALANCE": + return { status: 400, code, message: "Insufficient wallet balance for transaction fees" }; + case "WALLET_NOT_FOUND": + return { status: 400, code, message: "Wallet not found" }; + case "MISSING_WALLET_ADDRESS": + return { status: 400, code, message: "Token does not include a wallet address claim" }; + default: + return { + status: 500, + code, + message: error instanceof Error ? error.message : "Internal server error", + }; + } +}; +const createCampaign = async (req, res) => { + if (!req.auth?.userId) { + return (0, apiResponse_1.sendError)(res, 401, { code: "AUTH_REQUIRED", message: "Authentication required" }); + } + try { + const parsed = campaign_validation_1.createCampaignSchema.parse(req.body); + if (!data_source_1.default.isInitialized) { + return (0, apiResponse_1.sendError)(res, 500, { code: "DB_NOT_READY", message: "Database not initialized" }); + } + const service = new campaign_service_1.CampaignService(data_source_1.default.getRepository(campaign_entity_1.default), data_source_1.default.getRepository(auditLog_entity_1.default), data_source_1.default.getRepository(wallet_entity_1.default), data_source_1.default.getRepository(user_entity_1.default), (0, campaignFactory_client_1.createCairoCampaignClient)()); + const saved = await service.createCampaign({ + userId: req.auth.userId, + walletAddress: req.auth.walletAddress, + campaignRef: parsed.campaign_ref, + targetAmount: parsed.target_amount, + donationToken: parsed.donation_token, + }); + return (0, apiResponse_1.sendSuccess)(res, { + campaign_id: saved.campaignId, + campaign_ref: saved.campaignRef, + target_amount: saved.targetAmount, + donation_token: saved.donationToken, + transaction_hash: saved.transactionHash, + created_at: saved.createdAt.toISOString(), + }, 201); + } + catch (error) { + if (error instanceof zod_1.ZodError) { + const issue = error.issues?.[0]; + return (0, apiResponse_1.sendError)(res, 400, { + code: "VALIDATION_ERROR", + message: issue ? `${issue.path.join(".")} ${issue.message}` : "Invalid request", + details: { issues: error.issues }, + }); + } + const mapped = mapCreateCampaignError(error); + logger_1.default.error(JSON.stringify({ + name: error?.name, + code: mapped.code, + message: mapped.message, + })); + return (0, apiResponse_1.sendError)(res, mapped.status, { code: mapped.code, message: mapped.message }); + } +}; +exports.createCampaign = createCampaign; diff --git a/dist/components/v1/campaign/campaign.entity.js b/dist/components/v1/campaign/campaign.entity.js new file mode 100644 index 0000000..d859b25 --- /dev/null +++ b/dist/components/v1/campaign/campaign.entity.js @@ -0,0 +1,73 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CampaignEntity = void 0; +const typeorm_1 = require("typeorm"); +const utils_1 = require("../../../utils"); +let CampaignEntity = class CampaignEntity { + campaignId; + userId; + campaignRef; + targetAmount; + donationToken; + transactionHash; + createdAt; + ensureId() { + if (!this.campaignId) + this.campaignId = (0, utils_1.uuid)(); + } +}; +exports.CampaignEntity = CampaignEntity; +__decorate([ + (0, typeorm_1.PrimaryColumn)("text", { name: "campaign_id" }), + __metadata("design:type", String) +], CampaignEntity.prototype, "campaignId", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "user_id", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "userId", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "campaign_ref", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "campaignRef", void 0); +__decorate([ + (0, typeorm_1.Column)("numeric", { name: "target_amount", precision: 78, scale: 0, nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "targetAmount", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "donation_token", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "donationToken", void 0); +__decorate([ + (0, typeorm_1.Column)("text", { name: "transaction_hash", nullable: false }), + __metadata("design:type", String) +], CampaignEntity.prototype, "transactionHash", void 0); +__decorate([ + (0, typeorm_1.CreateDateColumn)({ + name: "created_at", + type: "timestamp", + precision: 3, + default: () => "CURRENT_TIMESTAMP", + }), + __metadata("design:type", Date) +], CampaignEntity.prototype, "createdAt", void 0); +__decorate([ + (0, typeorm_1.BeforeInsert)(), + __metadata("design:type", Function), + __metadata("design:paramtypes", []), + __metadata("design:returntype", void 0) +], CampaignEntity.prototype, "ensureId", null); +exports.CampaignEntity = CampaignEntity = __decorate([ + (0, typeorm_1.Entity)("campaigns"), + (0, typeorm_1.Index)("campaigns_campaign_ref_key", ["campaignRef"], { unique: true }), + (0, typeorm_1.Index)("campaigns_user_id_idx", ["userId"]) +], CampaignEntity); +exports.default = CampaignEntity; diff --git a/dist/components/v1/campaign/campaign.routes.js b/dist/components/v1/campaign/campaign.routes.js new file mode 100644 index 0000000..01062b1 --- /dev/null +++ b/dist/components/v1/campaign/campaign.routes.js @@ -0,0 +1,30 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const express_1 = require("express"); +const express_rate_limit_1 = __importDefault(require("express-rate-limit")); +const jwtAuth_api_1 = require("../../../appMiddlewares/jwtAuth.api"); +const apiResponse_1 = require("../../../utils/apiResponse"); +const campaign_controller_1 = require("./campaign.controller"); +const campaignsRateLimiter = (0, express_rate_limit_1.default)({ + windowMs: 60 * 60 * 1000, + limit: 5, + standardHeaders: "draft-6", + legacyHeaders: false, + keyGenerator: (req) => { + const r = req; + return r.auth?.userId ?? req.ip ?? "unknown"; + }, + handler: (req, res) => { + return (0, apiResponse_1.sendError)(res, 429, { + code: "RATE_LIMITED", + message: "Too many campaigns created. Try again later.", + details: { limit: 5, windowSeconds: 3600 }, + }); + }, +}); +const router = (0, express_1.Router)(); +router.post("/", jwtAuth_api_1.requireJwtAuthApi, campaignsRateLimiter, campaign_controller_1.createCampaign); +exports.default = router; diff --git a/dist/components/v1/campaign/campaign.service.js b/dist/components/v1/campaign/campaign.service.js new file mode 100644 index 0000000..13463cf --- /dev/null +++ b/dist/components/v1/campaign/campaign.service.js @@ -0,0 +1,81 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CampaignService = void 0; +const logger_1 = __importDefault(require("../../../utils/logger")); +class CampaignService { + campaignRepository; + auditRepository; + walletRepository; + userRepository; + cairoClient; + constructor(campaignRepository, auditRepository, walletRepository, userRepository, cairoClient) { + this.campaignRepository = campaignRepository; + this.auditRepository = auditRepository; + this.walletRepository = walletRepository; + this.userRepository = userRepository; + this.cairoClient = cairoClient; + } + async createCampaign(args) { + const { userId, walletAddress, campaignRef, targetAmount, donationToken } = args; + const existing = await this.campaignRepository.findOne({ where: { campaignRef } }); + if (existing) { + const error = new Error("Duplicate campaign_ref"); + error.code = "DUPLICATE_CAMPAIGN_REF"; + throw error; + } + if (!walletAddress) { + const error = new Error("Missing wallet address in token claims"); + error.code = "MISSING_WALLET_ADDRESS"; + throw error; + } + const wallet = await this.walletRepository.findOne({ where: { address: walletAddress } }); + if (!wallet) { + const error = new Error("Wallet not found"); + error.code = "WALLET_NOT_FOUND"; + throw error; + } + const walletBalance = Number.parseFloat(wallet.balance ?? "0"); + if (!Number.isFinite(walletBalance) || walletBalance <= 0) { + const error = new Error("Insufficient wallet balance for fees"); + error.code = "INSUFFICIENT_BALANCE"; + throw error; + } + const { transactionHash, campaignId } = await this.cairoClient.createCampaign({ + campaignRef, + targetAmount, + donationToken, + }); + const saved = await this.campaignRepository.save(this.campaignRepository.create({ + campaignId, + userId, + campaignRef, + targetAmount, + donationToken, + transactionHash, + })); + await this.auditRepository.save(this.auditRepository.create({ + userId, + action: "campaign.create", + entity: "campaign", + entityId: saved.campaignId, + details: { + campaignRef, + donationToken, + transactionHash, + }, + })); + const user = await this.userRepository.findOne({ where: { id: userId } }); + if (user) { + const nextCount = (user.campaignCount ?? 0) + 1; + await this.userRepository.update({ id: userId }, { campaignCount: nextCount }); + } + else { + logger_1.default.info(`User ${userId} not found; skipped campaignCount update`); + } + return saved; + } +} +exports.CampaignService = CampaignService; diff --git a/dist/components/v1/campaign/campaign.validation.js b/dist/components/v1/campaign/campaign.validation.js new file mode 100644 index 0000000..061b625 --- /dev/null +++ b/dist/components/v1/campaign/campaign.validation.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.toU256Parts = exports.createCampaignSchema = exports.isValidStarknetAddress = exports.isPositiveU256String = void 0; +const zod_1 = require("zod"); +const MAX_U256 = (1n << 256n) - 1n; +const isPositiveU256String = (value) => { + if (!/^\d+$/.test(value)) + return false; + const asBigInt = BigInt(value); + return asBigInt > 0n && asBigInt <= MAX_U256; +}; +exports.isPositiveU256String = isPositiveU256String; +const isValidStarknetAddress = (value) => { + if (!/^0x[0-9a-fA-F]{1,64}$/.test(value)) + return false; + try { + const n = BigInt(value); + return n >= 0n && n < (1n << 251n); + } + catch { + return false; + } +}; +exports.isValidStarknetAddress = isValidStarknetAddress; +exports.createCampaignSchema = zod_1.z.object({ + campaign_ref: zod_1.z + .string() + .trim() + .length(5, "campaign_ref must be exactly 5 characters long") + .refine((s) => s.trim().length === 5, "campaign_ref cannot be empty"), + target_amount: zod_1.z + .string() + .trim() + .refine((s) => (0, exports.isPositiveU256String)(s), "target_amount must be a positive u256 integer string"), + donation_token: zod_1.z + .string() + .trim() + .refine((s) => (0, exports.isValidStarknetAddress)(s), "donation_token must be a valid contract address"), +}); +const toU256Parts = (value) => { + const n = BigInt(value); + const lowMask = (1n << 128n) - 1n; + const low = n & lowMask; + const high = n >> 128n; + return { low: `0x${low.toString(16)}`, high: `0x${high.toString(16)}` }; +}; +exports.toU256Parts = toU256Parts; diff --git a/dist/components/v1/distribution/distribution.controller.js b/dist/components/v1/distribution/distribution.controller.js new file mode 100644 index 0000000..c546667 --- /dev/null +++ b/dist/components/v1/distribution/distribution.controller.js @@ -0,0 +1,61 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.listDistributions = exports.createDistribution = void 0; +const data_source_1 = __importDefault(require("../../../config/persistence/data-source")); +const distribution_entity_1 = require("./distribution.entity"); +const distribution_service_1 = require("./distribution.service"); +const getDistributionService = () => { + if (!data_source_1.default.isInitialized) { + throw new Error("Database not initialized"); + } + const distributionRepository = data_source_1.default.getRepository(distribution_entity_1.DistributionEntity); + return new distribution_service_1.DistributionService(distributionRepository); +}; +const createDistribution = async (req, res) => { + try { + const distributionService = getDistributionService(); + const validatedData = req.body; + const distribution = await distributionService.createDistribution(validatedData); + const response = { + data: distribution, + success: true, + message: "Distribution created successfully", + }; + res.status(201).json(response); + } + catch (error) { + console.error("Error in createDistribution:", error); + const errorResponse = { + data: null, + success: false, + message: error instanceof Error ? error.message : "Internal server error", + }; + res.status(500).json(errorResponse); + } +}; +exports.createDistribution = createDistribution; +const listDistributions = async (_req, res) => { + try { + const distributionService = getDistributionService(); + const distributions = await distributionService.listDistributions(); + const response = { + data: distributions, + success: true, + message: "Distributions fetched successfully", + }; + res.status(200).json(response); + } + catch (error) { + console.error("Error in listDistributions:", error); + const errorResponse = { + data: null, + success: false, + message: error instanceof Error ? error.message : "Internal server error", + }; + res.status(500).json(errorResponse); + } +}; +exports.listDistributions = listDistributions; diff --git a/dist/components/v1/distribution/distribution.dto.js b/dist/components/v1/distribution/distribution.dto.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/components/v1/distribution/distribution.dto.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/dist/components/v1/distribution/distribution.service.js b/dist/components/v1/distribution/distribution.service.js new file mode 100644 index 0000000..02565c4 --- /dev/null +++ b/dist/components/v1/distribution/distribution.service.js @@ -0,0 +1,107 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DistributionService = void 0; +const enums_1 = require("../../../types/enums"); +class DistributionService { + distributionRepository; + constructor(distributionRepository) { + this.distributionRepository = distributionRepository; + } + async createDistribution(createDistributionDto) { + try { + const distributionData = this.prepareDistributionData(createDistributionDto); + const distribution = this.distributionRepository.create(distributionData); + const savedDistribution = await this.distributionRepository.save(distribution); + return this.formatDistributionResponse(savedDistribution); + } + catch (error) { + console.error("Error creating distribution:", error); + throw new Error("Failed to create distribution"); + } + } + async listDistributions(limit = 50) { + try { + const distributions = await this.distributionRepository.find({ + order: { createdAt: "DESC" }, + take: Math.min(Math.max(limit, 1), 200), + }); + return distributions.map((d) => this.formatDistributionResponse(d)); + } + catch (error) { + console.error("Error listing distributions:", error); + throw new Error("Failed to list distributions"); + } + } + prepareDistributionData(data) { + const distributionData = { + userAddress: data.userAddress.toLowerCase(), + tokenAddress: data.tokenAddress.toLowerCase(), + tokenSymbol: data.tokenSymbol.toUpperCase(), + tokenDecimals: data.tokenDecimals, + totalAmount: data.totalAmount, + feeAmount: data.feeAmount, + totalRecipients: data.totalRecipients, + distributionType: data.distributionType, + chainName: data.chainName || "", + status: enums_1.DistributionStatus.PENDING, + network: data.network || enums_1.Network.MAINNET, + transactionHash: null, + blockNumber: null, + blockTimestamp: null, + }; + if (data.usdRate) { + distributionData.usdRate = data.usdRate; + distributionData.totalUsdAmount = this.calculateTotalUsdAmount(data.totalAmount, data.usdRate); + } + else { + distributionData.usdRate = "0"; + distributionData.totalUsdAmount = "0"; + } + if (data.metadata) { + distributionData.metadata = this.processMetadata(data.metadata); + } + return distributionData; + } + calculateTotalUsdAmount(totalAmount, usdRate) { + try { + const amount = Number.parseFloat(totalAmount); + const rate = Number.parseFloat(usdRate); + const totalUsd = amount * rate; + return totalUsd.toString(); + } + catch (error) { + console.warn("Error calculating total USD amount:", error); + return "0"; + } + } + processMetadata(metadata) { + const processedMetadata = { ...metadata }; + delete processedMetadata.__proto__; + delete processedMetadata.constructor; + return processedMetadata; + } + formatDistributionResponse(distribution) { + return { + id: distribution.id, + userAddress: distribution.userAddress, + transactionHash: distribution.transactionHash, + tokenAddress: distribution.tokenAddress, + tokenSymbol: distribution.tokenSymbol, + tokenDecimals: distribution.tokenDecimals, + totalAmount: distribution.totalAmount, + feeAmount: distribution.feeAmount, + usdRate: distribution.usdRate, + totalUsdAmount: distribution.totalUsdAmount, + totalRecipients: distribution.totalRecipients, + distributionType: distribution.distributionType, + chainName: distribution.chainName, + status: distribution.status, + blockNumber: distribution.blockNumber, + blockTimestamp: distribution.blockTimestamp, + network: distribution.network, + createdAt: distribution.createdAt, + metadata: distribution.metadata, + }; + } +} +exports.DistributionService = DistributionService; diff --git a/dist/components/v1/distribution/distribution.validation.js b/dist/components/v1/distribution/distribution.validation.js new file mode 100644 index 0000000..73752cb --- /dev/null +++ b/dist/components/v1/distribution/distribution.validation.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createDistributionSchema = exports.ValidationError = void 0; +const zod_1 = require("zod"); +const enums_1 = require("../../../types/enums"); +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = "ValidationError"; + } +} +exports.ValidationError = ValidationError; +// Helper function to validate Ethereum addresses +const ethereumAddressRegex = /^0x[a-fA-F0-9]{40}$/; +const decimalStringRegex = /^\d+(\.\d+)?$/; +exports.createDistributionSchema = zod_1.z.object({ + userAddress: zod_1.z.string().regex(ethereumAddressRegex, "userAddress must be a valid Ethereum address"), + tokenAddress: zod_1.z.string().regex(ethereumAddressRegex, "tokenAddress must be a valid Ethereum address"), + tokenSymbol: zod_1.z.string().min(1, "tokenSymbol is required").max(20, "tokenSymbol must be max 20 characters"), + tokenDecimals: zod_1.z + .number() + .int("tokenDecimals must be an integer") + .min(0, "tokenDecimals must be at least 0") + .max(30, "tokenDecimals must be at most 30"), + totalAmount: zod_1.z.string().regex(decimalStringRegex, "totalAmount must be a valid decimal string"), + feeAmount: zod_1.z.string().regex(decimalStringRegex, "feeAmount must be a valid decimal string"), + totalRecipients: zod_1.z.number().int("totalRecipients must be an integer").min(1, "totalRecipients must be at least 1"), + distributionType: zod_1.z.nativeEnum(enums_1.DistributionType, { + errorMap: () => ({ message: "distributionType must be a valid DistributionType" }), + }), + usdRate: zod_1.z.string().regex(decimalStringRegex, "usdRate must be a valid decimal string").optional(), + chainName: zod_1.z.string().max(50, "chainName must be max 50 characters").optional(), + network: zod_1.z + .nativeEnum(enums_1.Network, { + errorMap: () => ({ message: "network must be a valid Network" }), + }) + .optional(), + metadata: zod_1.z.record(zod_1.z.any()).optional(), +}); diff --git a/dist/components/v1/distribution/distrubtion.routes.js b/dist/components/v1/distribution/distrubtion.routes.js new file mode 100644 index 0000000..0e5d4c3 --- /dev/null +++ b/dist/components/v1/distribution/distrubtion.routes.js @@ -0,0 +1,13 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const enhancedRouter_1 = __importDefault(require("../../../utils/enhancedRouter")); +const policy_middleware_1 = __importDefault(require("../../../appMiddlewares/policy.middleware")); +const distribution_validation_1 = require("./distribution.validation"); +const distribution_controller_1 = require("./distribution.controller"); +const distributionRouter = new enhancedRouter_1.default(); +distributionRouter.get("/", distribution_controller_1.listDistributions); +distributionRouter.post("/", (0, policy_middleware_1.default)(distribution_validation_1.createDistributionSchema), distribution_controller_1.createDistribution); +exports.default = distributionRouter.getRouter(); diff --git a/dist/components/v1/platform/platformControllers/permission.controller.js b/dist/components/v1/platform/platformControllers/permission.controller.js index 7fc6dd8..9c7521b 100644 --- a/dist/components/v1/platform/platformControllers/permission.controller.js +++ b/dist/components/v1/platform/platformControllers/permission.controller.js @@ -15,7 +15,6 @@ const platformConstants_1 = __importDefault(require("../../../../config/platform const data_source_1 = __importDefault(require("../../../../config/persistence/data-source")); const platform_utils_1 = require("../platform.utils"); const permission_services_1 = __importDefault(require("../platformServices/permission.services")); -const useraccess_services_1 = __importDefault(require("../../auth/authServices/useraccess.services")); const addPermissions = async (req, res) => { const { permissions } = req.body; const permissionNames = permissions.map((perm) => perm.name); @@ -148,12 +147,6 @@ const deleteRole = async (req, res) => { if (!roleExist) { throw new errorHandler_1.NotFoundError(responseMessages_1.default.dataNotExist('Role')); } - const userAccessExists = await useraccess_services_1.default.findOne({ - where: { Role: { id: +roleId } }, - }); - if (userAccessExists) { - throw new errorHandler_1.InvalidRequestError('This role cannot be deleted, it has an existing user(s).'); - } await role_services_1.default.delete({ id: +roleId }); return (0, helper_1.handleResponse)(res, { message: responseMessages_1.default.fieldSuccessAction('Role', 'deleted'), diff --git a/dist/components/v1/routes.api.v1.js b/dist/components/v1/routes.api.v1.js new file mode 100644 index 0000000..7f39529 --- /dev/null +++ b/dist/components/v1/routes.api.v1.js @@ -0,0 +1,10 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const enhancedRouter_1 = __importDefault(require("../../utils/enhancedRouter")); +const campaign_routes_1 = __importDefault(require("./campaign/campaign.routes")); +const router = new enhancedRouter_1.default(); +router.use("/campaigns", campaign_routes_1.default); +exports.default = router.getRouter(); diff --git a/dist/components/v1/routes.v1.js b/dist/components/v1/routes.v1.js index ca6d611..2f8b353 100644 --- a/dist/components/v1/routes.v1.js +++ b/dist/components/v1/routes.v1.js @@ -4,9 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); const enhancedRouter_1 = __importDefault(require("../../utils/enhancedRouter")); -const platform_routes_1 = __importDefault(require("./platform/platform.routes")); -const wallet_routes_1 = __importDefault(require("./wallet/wallet.routes")); +const distrubtion_routes_1 = __importDefault(require("./distribution/distrubtion.routes")); const routerV1 = new enhancedRouter_1.default(); -routerV1.use('/platform', platform_routes_1.default); -routerV1.use('/wallets', wallet_routes_1.default); +routerV1.use("/distributions", distrubtion_routes_1.default); exports.default = routerV1.getRouter(); diff --git a/dist/components/v1/user/user.entity.js b/dist/components/v1/user/user.entity.js index e1eff31..fa4b102 100644 --- a/dist/components/v1/user/user.entity.js +++ b/dist/components/v1/user/user.entity.js @@ -16,6 +16,7 @@ let UserEntity = class UserEntity { id; username; email; + campaignCount; createdAt; updatedAt; generateId() { @@ -37,6 +38,10 @@ __decorate([ (0, typeorm_1.Column)("text", { nullable: false, unique: true }), __metadata("design:type", String) ], UserEntity.prototype, "email", void 0); +__decorate([ + (0, typeorm_1.Column)("integer", { name: "campaign_count", default: 0 }), + __metadata("design:type", Number) +], UserEntity.prototype, "campaignCount", void 0); __decorate([ (0, typeorm_1.CreateDateColumn)({ name: "created_at", diff --git a/dist/components/v1/wallet/wallet.entity.js b/dist/components/v1/wallet/wallet.entity.js index 5c05b59..5048945 100644 --- a/dist/components/v1/wallet/wallet.entity.js +++ b/dist/components/v1/wallet/wallet.entity.js @@ -84,7 +84,7 @@ __decorate([ __metadata("design:returntype", void 0) ], WalletEntity.prototype, "generateId", null); exports.WalletEntity = WalletEntity = __decorate([ - (0, typeorm_1.Entity)("Wallet"), + (0, typeorm_1.Entity)("wallet"), (0, typeorm_1.Index)("Wallet_address_idx", ["address"]), (0, typeorm_1.Index)("Wallet_network_idx", ["network"]) ], WalletEntity); diff --git a/dist/config/persistence/data-source.js b/dist/config/persistence/data-source.js index 49a0621..1ad50f4 100644 --- a/dist/config/persistence/data-source.js +++ b/dist/config/persistence/data-source.js @@ -13,17 +13,33 @@ const permission_entity_1 = require("../../components/v1/platform/platformEntiti const wallet_entity_1 = __importDefault(require("../../components/v1/wallet/wallet.entity")); const distribution_entity_1 = __importDefault(require("../../components/v1/distribution/distribution.entity")); const feeConfig_entity_1 = __importDefault(require("../../components/v1/feeConfig/feeConfig.entity")); -const user_entity_1 = __importDefault(require("src/components/v1/user/user.entity")); +const user_entity_1 = __importDefault(require("../../components/v1/user/user.entity")); +const campaign_entity_1 = __importDefault(require("../../components/v1/campaign/campaign.entity")); +const auditLog_entity_1 = __importDefault(require("../../components/v1/audit/auditLog.entity")); const { dbConfigs } = __1.default; +const missingDbConfigKeys = [ + ['DATABASE_HOST', dbConfigs.DATABASE_HOST], + ['DATABASE_PORT', dbConfigs.DATABASE_PORT], + ['DATABASE_USERNAME', dbConfigs.DATABASE_USERNAME], + ['DATABASE_PASSWORD', dbConfigs.DATABASE_PASSWORD], + ['DATABASE_NAME', dbConfigs.DATABASE_NAME], +] + .filter(([, value]) => !value) + .map(([key]) => key); +if (missingDbConfigKeys.length) { + throw new Error(`Missing required database env vars: ${missingDbConfigKeys.join(', ')}`); +} +const databasePort = Number(dbConfigs.DATABASE_PORT ?? 5432); const AppDataSource = new typeorm_1.DataSource({ host: dbConfigs.DATABASE_HOST, - port: +dbConfigs.DATABASE_PORT, + port: Number.isNaN(databasePort) ? 5432 : databasePort, username: dbConfigs.DATABASE_USERNAME, - password: dbConfigs.DATABASE_PASSWORD, + password: String(dbConfigs.DATABASE_PASSWORD ?? ''), database: dbConfigs.DATABASE_NAME, - type: "postgres", + type: 'postgres', + connectTimeoutMS: 5000, synchronize: false, - logging: true, + logging: __1.default.isDev || __1.default.isLocalDev || __1.default.isTestMode, entities: [ permission_entity_1.RoleEntity, platform_entity_1.default, @@ -32,27 +48,26 @@ const AppDataSource = new typeorm_1.DataSource({ distribution_entity_1.default, feeConfig_entity_1.default, user_entity_1.default, + campaign_entity_1.default, + auditLog_entity_1.default, ], - migrations: ["src/migrations/*.js"], - ssl: { - rejectUnauthorized: false, - }, + migrations: ['src/migrations/*.js'], + ...(__1.default.isProd || __1.default.isStaging + ? { ssl: { rejectUnauthorized: false } } + : {}), }); const resetDatabase = async () => { try { - // Connect to the database await AppDataSource.initialize(); const queryRunner = AppDataSource.createQueryRunner(); - // Drop all tables - logger_1.default.info("Dropping all tables..."); + logger_1.default.info('Dropping all tables...'); await queryRunner.clearDatabase(); - logger_1.default.info("All tables dropped. Database reset successfully!"); + logger_1.default.info('All tables dropped. Database reset successfully!'); } catch (error) { - console.error("Error resetting the database:", error); + console.error('Error resetting the database:', error); } finally { - // Close the connection await AppDataSource.destroy(); } }; diff --git a/dist/config/persistence/seeder.js b/dist/config/persistence/seeder.js index f889e83..46d498b 100644 --- a/dist/config/persistence/seeder.js +++ b/dist/config/persistence/seeder.js @@ -1,131 +1,16 @@ "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const permission_entity_1 = require("../../components/v1/platform/platformEntities/permission.entity"); -const userAccess_entity_1 = __importDefault(require("../../components/v1/auth/authEntities/userAccess.entity")); -const platform_entity_1 = __importStar(require("../../components/v1/platform/platformEntities/platform.entity")); -const user_entity_1 = __importDefault(require("../../components/v1/user/user.entity")); -const platformConstants_1 = __importDefault(require("../platformConstants")); -const errorHandler_1 = require("../../utils/errorHandler"); const logger_1 = __importDefault(require("../../utils/logger")); -const __1 = __importDefault(require("..")); -const platform_services_1 = __importDefault(require("../../components/v1/platform/platformServices/platform.services")); -const utils_1 = require("../../utils"); -const defaultRolesAndPermissions_1 = require("../defaultRolesAndPermissions"); -const platform_utils_1 = require("../../components/v1/platform/platform.utils"); -const free_email_1 = require("../../utils/free_email"); +/** + * Seeding is intentionally disabled. + * + * The previous seeding implementation referenced modules/constants that are not + * present in this repository snapshot, and blocked the server from booting. + */ const initiateSeeding = async () => { - const { seedData, platformConfig } = __1.default; - try { - logger_1.default.info('Check Seeding Data'); - const platformExists = await platform_services_1.default.findOneBy({ - name: platformConfig.name, - }); - if (platformExists) - return; - logger_1.default.info('Initiating Seeding ⚙️'); - logger_1.default.info('Seeding started ⚙️'); - await (0, utils_1.executeTransaction)(async (tx) => { - // Check if platform data exists - // Seed Platform - const platform = tx.create(platform_entity_1.default, { - name: platformConfig.name, - version: '1.0.0', - description: platformConfig?.description || '', - userTypes: platformConstants_1.default.userTypes, - supportedCountries: platformConstants_1.default.supportedCountries, - setups: platformConstants_1.default.kycSteps.map((setup) => ({ - type: setup, - status: 'active', - activeCountries: [...platformConstants_1.default.supportedCountries], - cost: 0, - })), - }); - logger_1.default.info('Seeding in progress (platform user done) ⚙️'); - // Seed User - const user = tx.create(user_entity_1.default, { - phonePrefix: seedData.phonePrefix, - firstName: seedData.firstName, - lastName: seedData.lastName, - email: seedData.email, - phone: seedData.phone, - isConfirmed: true, - countryCode: 'gh', - isSignupComplete: true, - userType: 'platform-admin', - }); - await tx.save(user); - logger_1.default.info('Seeding in progress (seed user done) ⚙️'); - const savedPermissions = await tx.save(permission_entity_1.PermissionEntity, defaultRolesAndPermissions_1.defaultPermissions); - const superAdminRole = tx.create(permission_entity_1.RoleEntity, { - name: 'super-admin', - permissions: [ - { - value: 'supreme', - description: 'one_star_general', - }, - ], - userType: 'platform-admin', - }); - await tx.save(superAdminRole); - const resolvedRoles = (0, platform_utils_1.resolveRolePermissions)(savedPermissions, defaultRolesAndPermissions_1.defaultRolesAndPermissions); - await tx.save(permission_entity_1.RoleEntity, resolvedRoles); - await tx.save(platform_entity_1.FreeDomainEntity, free_email_1.free_domains.map((domain) => ({ domain }))); - // Seed Access - const userAccess = tx.create(userAccess_entity_1.default, { - userId: user.id, - password: seedData.password, - Role: superAdminRole, - }); - await tx.save([platform, userAccess]); - }); - logger_1.default.info('Seeding in progress (seed access done) ⚙️'); - logger_1.default.info('Seeding complete ✅'); - return; - } - catch (err) { - // logger.info('Error occurred in seeding:', err); - const error = err; - throw new errorHandler_1.AppError({ - name: error.name, - message: error.message, - httpCode: 500, - }); - } + logger_1.default.info('Seeding disabled (no-op).'); }; exports.default = initiateSeeding; diff --git a/dist/index.js b/dist/index.js index 437b147..c5b0c48 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,4 +1,37 @@ "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -10,10 +43,8 @@ const helmet_1 = __importDefault(require("helmet")); const cors_1 = __importDefault(require("cors")); const fingerprint_middleware_1 = __importDefault(require("./appMiddlewares/fingerprint.middleware")); const appMiddlewares_1 = require("./appMiddlewares"); -const routes_v1_1 = __importDefault(require("./components/v1/routes.v1")); const errorHandler_1 = require("./utils/errorHandler"); const data_source_1 = __importDefault(require("./config/persistence/data-source")); -const seeder_1 = __importDefault(require("./config/persistence/seeder")); const logger_1 = __importDefault(require("./utils/logger")); const config_1 = __importDefault(require("./config")); const app = (0, express_1.default)(); @@ -24,12 +55,7 @@ const rateLimiter = (0, express_rate_limit_1.default)({ legacyHeaders: false, }); const initializeDb = () => { - data_source_1.default.initialize() - .then(() => { - logger_1.default.info('DB Connected Successfully!!!'); - (0, seeder_1.default)(); - }) - .catch(errorHandler_1.errorHandler.handleError); + return data_source_1.default.initialize(); }; const allowedOrigins = ['http://localhost:3000', 'http://localhost:3001']; const corsOptions = { @@ -59,8 +85,10 @@ const initializeMiddlewares = () => { .use(express_1.default.urlencoded({ limit: '50kb', extended: false })) .use(appMiddlewares_1.verifyAllowedMethods); }; -const initializeRoutes = () => { - app.use('/v1/', routes_v1_1.default); +const initializeRoutes = (routerV1, routerApiV1) => { + app.use('/v1/', routerV1); + if (routerApiV1) + app.use('/api/v1/', routerApiV1); // if (appConfigs.isProd || appConfigs.isStaging) { // app.use(Sentry.Handlers.errorHandler()); // } @@ -75,19 +103,27 @@ const initializeRoutes = () => { // Use the error handling middleware app.use(errorHandler_1.errorHandlingMiddleware); }; -try { - initializeDb(); - initializeMiddlewares(); - initializeRoutes(); -} -catch (error) { - errorHandler_1.errorHandler.handleError(error); -} -const port = config_1.default.port; -app.listen(port, () => { - logger_1.default.info('[+]Logging Service Started'); - logger_1.default.info(`[+] CLOUD Server Running ... on Port -> ${port}`); -}); +const startServer = async () => { + try { + logger_1.default.info('Connecting to DB...'); + await initializeDb(); + logger_1.default.info('DB Connected Successfully!!!'); + initializeMiddlewares(); + const { default: routerV1 } = await Promise.resolve().then(() => __importStar(require('./components/v1/routes.v1'))); + const { default: routerApiV1 } = await Promise.resolve().then(() => __importStar(require('./components/v1/routes.api.v1'))); + initializeRoutes(routerV1, routerApiV1); + const port = config_1.default.port; + app.listen(port, () => { + logger_1.default.info('[+]Logging Service Started'); + logger_1.default.info(`[+] CLOUD Server Running ... on Port -> ${port}`); + }); + } + catch (error) { + errorHandler_1.errorHandler.handleError(error); + process.exit(1); + } +}; +void startServer(); process.on('uncaughtException', async (err) => { try { logger_1.default.log('error', '❌❌❌ ➡ ⬇⬇⬇ An Error occured -> UNCAUGHT EXCEPTION ERROR ⬇⬇⬇'); diff --git a/dist/services/cairo/campaignFactory.client.js b/dist/services/cairo/campaignFactory.client.js new file mode 100644 index 0000000..ad34645 --- /dev/null +++ b/dist/services/cairo/campaignFactory.client.js @@ -0,0 +1,11 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createCairoCampaignClient = void 0; +const process_1 = require("process"); +const campaignFactory_mock_1 = require("./campaignFactory.mock"); +const campaignFactory_starknet_1 = require("./campaignFactory.starknet"); +const createCairoCampaignClient = () => { + const isMock = process_1.env.CAIRO_MOCK === "true" || process_1.env.CAIRO_MOCK === "1"; + return isMock ? (0, campaignFactory_mock_1.createMockCampaignClient)() : (0, campaignFactory_starknet_1.createStarknetCampaignClient)(); +}; +exports.createCairoCampaignClient = createCairoCampaignClient; diff --git a/dist/services/cairo/campaignFactory.mock.js b/dist/services/cairo/campaignFactory.mock.js new file mode 100644 index 0000000..9b97d02 --- /dev/null +++ b/dist/services/cairo/campaignFactory.mock.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createMockCampaignClient = void 0; +const createMockCampaignClient = () => { + return { + assertContractAccessible: async () => { }, + createCampaign: async ({ campaignRef }) => { + const tx = `0xmock_${Date.now().toString(16)}`; + return { transactionHash: tx, campaignId: `mock_${campaignRef}` }; + }, + }; +}; +exports.createMockCampaignClient = createMockCampaignClient; diff --git a/dist/services/cairo/campaignFactory.starknet.js b/dist/services/cairo/campaignFactory.starknet.js new file mode 100644 index 0000000..cef3995 --- /dev/null +++ b/dist/services/cairo/campaignFactory.starknet.js @@ -0,0 +1,71 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createStarknetCampaignClient = void 0; +const process_1 = require("process"); +const starknet_1 = require("starknet"); +const logger_1 = __importDefault(require("../../utils/logger")); +const retry_1 = require("./retry"); +const campaign_validation_1 = require("../../components/v1/campaign/campaign.validation"); +const getRequiredEnv = (key) => { + const value = process_1.env[key]; + if (!value) + throw new Error(`Missing required env var: ${key}`); + return value; +}; +const parseEventCampaignId = (receipt) => { + const eventKey = process_1.env.CAMPAIGN_CREATED_EVENT_KEY; + if (!eventKey) + return null; + const normalizedKey = eventKey.toLowerCase(); + const events = receipt?.events ?? []; + const match = events.find((e) => (e.keys ?? []).map((k) => k.toLowerCase()).includes(normalizedKey)); + if (!match) + return null; + const data0 = match?.data?.[0]; + return typeof data0 === "string" && data0.length ? data0 : null; +}; +const createStarknetCampaignClient = () => { + const rpcUrl = getRequiredEnv("CAIRO_RPC_URL"); + const accountAddress = getRequiredEnv("CAIRO_ACCOUNT_ADDRESS"); + const privateKey = getRequiredEnv("CAIRO_PRIVATE_KEY"); + const factoryAddress = getRequiredEnv("CAIRO_FACTORY_CONTRACT_ADDRESS"); + const provider = new starknet_1.RpcProvider({ nodeUrl: rpcUrl }); + const account = new starknet_1.Account({ provider, address: accountAddress, signer: privateKey }); + return { + assertContractAccessible: async (address) => { + await (0, retry_1.withRetry)(async () => { + await provider.getClassHashAt(address); + }); + }, + createCampaign: async ({ campaignRef, targetAmount, donationToken }) => { + await (0, retry_1.withRetry)(async () => { + await provider.getClassHashAt(factoryAddress); + }); + await (0, retry_1.withRetry)(async () => { + await provider.getClassHashAt(donationToken); + }); + const campaignRefFelt = starknet_1.shortString.encodeShortString(campaignRef); + const { low, high } = (0, campaign_validation_1.toU256Parts)(targetAmount); + const invocation = { + contractAddress: factoryAddress, + entrypoint: "create_campaign", + calldata: [campaignRefFelt, low, high, donationToken], + }; + const result = await (0, retry_1.withRetry)(async () => { + logger_1.default.info(`Submitting create_campaign tx for ref=${campaignRef}`); + return await account.execute(invocation); + }); + const transactionHash = result?.transaction_hash ?? result?.transactionHash; + if (!transactionHash) { + throw new Error("Contract call did not return a transaction hash"); + } + const receipt = await (0, retry_1.withRetry)(async () => await provider.waitForTransaction(transactionHash)); + const campaignId = parseEventCampaignId(receipt) ?? transactionHash; + return { transactionHash, campaignId }; + }, + }; +}; +exports.createStarknetCampaignClient = createStarknetCampaignClient; diff --git a/dist/services/cairo/retry.js b/dist/services/cairo/retry.js new file mode 100644 index 0000000..ac87b8e --- /dev/null +++ b/dist/services/cairo/retry.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetry = exports.sleep = void 0; +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +exports.sleep = sleep; +const withRetry = async (fn, options) => { + const retries = options?.retries ?? 3; + const minDelayMs = options?.minDelayMs ?? 250; + const factor = options?.factor ?? 2; + let attempt = 0; + let lastError; + while (attempt <= retries) { + try { + return await fn(); + } + catch (error) { + lastError = error; + if (attempt === retries) + break; + const backoff = Math.round(minDelayMs * Math.pow(factor, attempt)); + const jitter = Math.round(Math.random() * 100); + await (0, exports.sleep)(backoff + jitter); + attempt += 1; + } + } + throw lastError; +}; +exports.withRetry = withRetry; diff --git a/dist/utils/apiResponse.js b/dist/utils/apiResponse.js new file mode 100644 index 0000000..c853219 --- /dev/null +++ b/dist/utils/apiResponse.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.sendError = exports.sendSuccess = void 0; +const sendSuccess = (res, data, status = 200) => { + return res.status(status).json({ + success: true, + data, + }); +}; +exports.sendSuccess = sendSuccess; +const sendError = (res, status, error) => { + return res.status(status).json({ + success: false, + error: { + code: error.code, + message: error.message, + details: error.details ?? {}, + }, + }); +}; +exports.sendError = sendError; diff --git a/dist/utils/index.js b/dist/utils/index.js index da17213..9c2390e 100644 --- a/dist/utils/index.js +++ b/dist/utils/index.js @@ -1,7 +1,37 @@ "use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); Object.defineProperty(exports, "__esModule", { value: true }); exports.getUniqueNos = exports.getUniqueIds = exports.getIpAddressAndUserAgent = exports.decodeFromBase64 = exports.encodeToBase64 = exports.convertDateToTimeStamp = exports.getIpAddress = exports.generateBackupCodes = exports.generateRandomCode = exports.isValidDate = exports.redefinePhoneNo = exports.ulid = exports.uuid = exports.isValidUuid = void 0; exports.executeTransaction = executeTransaction; @@ -9,7 +39,6 @@ exports.transformObjectValues = transformObjectValues; const ulidx_1 = require("ulidx"); const crypto_1 = require("crypto"); const zod_1 = require("zod"); -const data_source_1 = __importDefault(require("../config/persistence/data-source")); const isValidUuid = (uuid) => { const result = zod_1.z.string().uuid().safeParse(uuid); return result.success; @@ -20,7 +49,8 @@ exports.isValidUuid = isValidUuid; * @param transactionCallback - A callback that performs the transaction logic. */ async function executeTransaction(transactionCallback) { - return data_source_1.default.transaction(async (transactionManager) => { + const { default: AppDataSource } = await Promise.resolve().then(() => __importStar(require('../config/persistence/data-source'))); + return AppDataSource.transaction(async (transactionManager) => { try { return await transactionCallback(transactionManager); } diff --git a/error.log b/error.log deleted file mode 100644 index fa47cc0..0000000 --- a/error.log +++ /dev/null @@ -1 +0,0 @@ -{"level":"error","message":"{\"name\":\"AggregateError\",\"message\":\"\",\"path1\":\"at internalConnectMultiple (node:net:1117:18)\",\"path2\":\"at afterConnectMultiple (node:net:1684:7)\"}","timestamp":"2025-07-02T00:00:04.171Z"} diff --git a/eslint.config.mjs b/eslint.config.mjs index 12871e1..755fe95 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,6 +4,14 @@ import tseslint from 'typescript-eslint'; /** @type {import('eslint').Linter.Config[]} */ export default [ + { + ignores: [ + 'node_modules/**', + 'dist/**', + 'coverage/**', + 'src/migrations/**', + ], + }, { files: ['**/*.{js,mjs,cjs,ts}'] }, { languageOptions: { globals: globals.browser } }, pluginJs.configs.recommended, diff --git a/package-lock.json b/package-lock.json index 7efc40e..c90ec0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "nodemailer": "^6.9.16", "pg": "^8.13.1", "reflect-metadata": "^0.2.2", + "starknet": "^9.4.2", "typeorm": "^0.3.20", "ulidx": "^2.4.1", "winston": "^3.17.0", @@ -33,6 +34,7 @@ "@types/jsonwebtoken": "^9.0.7", "@types/ms": "^0.7.34", "@types/node": "^22.9.0", + "c8": "^11.0.0", "eslint": "^9.14.0", "eslint-config-prettier": "^9.1.0", "globals": "^15.12.0", @@ -42,6 +44,22 @@ "typescript-eslint": "^8.13.0" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -793,6 +811,83 @@ "node": ">=12" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/curves": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", + "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.6.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", + "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz", + "integrity": "sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -841,12 +936,132 @@ "node": ">=14" } }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/starknet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/starknet/-/starknet-1.1.0.tgz", + "integrity": "sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.7.0", + "@noble/hashes": "~1.6.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sqltools/formatter": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", "license": "MIT" }, + "node_modules/@starknet-io/get-starknet-wallet-standard": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@starknet-io/get-starknet-wallet-standard/-/get-starknet-wallet-standard-5.0.0.tgz", + "integrity": "sha512-isDNGDlp16W24HE4IuweYXLDRZN0JbsDnazAieeKXE87Mn+jqhsjgTsMxcwWTjX7v906Bjz39FiDjGUddnr36g==", + "license": "MIT", + "dependencies": { + "@starknet-io/types-js": "^0.7.10", + "@wallet-standard/base": "^1.1.0", + "@wallet-standard/features": "^1.1.0", + "ox": "^0.4.4" + } + }, + "node_modules/@starknet-io/starknet-types-010": { + "name": "@starknet-io/types-js", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.10.0.tgz", + "integrity": "sha512-7ALSydz6pq3YIOpq5a7OkkxqwJciMc9Nlph0OGjhcC3xX0xH30XgizmziLyYVN10oO9+BJk8M9KbJjpzdbtRSw==", + "license": "MIT" + }, + "node_modules/@starknet-io/starknet-types-09": { + "name": "@starknet-io/types-js", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.9.2.tgz", + "integrity": "sha512-vWOc0FVSn+RmabozIEWcEny1I73nDGTvOrLYJsR1x7LGA3AZmqt4i/aW69o/3i2NN5CVP8Ok6G1ayRQJKye3Wg==", + "license": "MIT" + }, + "node_modules/@starknet-io/types-js": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@starknet-io/types-js/-/types-js-0.7.10.tgz", + "integrity": "sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w==", + "license": "MIT" + }, "node_modules/@types/bcryptjs": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", @@ -925,6 +1140,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1267,6 +1489,63 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@wallet-standard/base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wallet-standard/base/-/base-1.1.0.tgz", + "integrity": "sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + } + }, + "node_modules/@wallet-standard/features": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@wallet-standard/features/-/features-1.1.0.tgz", + "integrity": "sha512-hiEivWNztx73s+7iLxsuD1sOJ28xtRix58W7Xnz4XzzA/pF0+aicnWgjOdA10doVDEDZdUuZCIIqG96SFNlDUg==", + "license": "Apache-2.0", + "dependencies": { + "@wallet-standard/base": "^1.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/abi-wan-kanabi": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/abi-wan-kanabi/-/abi-wan-kanabi-2.2.4.tgz", + "integrity": "sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg==", + "license": "ISC", + "dependencies": { + "ansicolors": "^0.3.2", + "cardinal": "^2.1.1", + "fs-extra": "^10.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "generate": "dist/generate.js" + } + }, + "node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1347,6 +1626,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "license": "MIT" + }, "node_modules/ansis": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", @@ -1518,6 +1803,40 @@ "node": ">= 0.8" } }, + "node_modules/c8": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-11.0.0.tgz", + "integrity": "sha512-e/uRViGHSVIJv7zsaDKM7VRn2390TgHXqUSvYwPHBQaU6L7E9L0n9JbdkwdYPvshDT0KymBmmlwSpms3yBaMNg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^8.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": "20 || >=22" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1557,6 +1876,19 @@ "node": ">=6" } }, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "license": "MIT", + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1737,6 +2069,13 @@ "node": ">= 0.6" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cookie": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", @@ -2131,6 +2470,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -2186,6 +2538,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -2466,6 +2824,20 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2631,6 +3003,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2681,6 +3059,13 @@ "node": ">=16.0.0" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -2861,6 +3246,45 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -2910,6 +3334,18 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -3071,12 +3507,34 @@ "node": ">= 12.0.0" } }, + "node_modules/lossless-json": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lossless-json/-/lossless-json-4.3.0.tgz", + "integrity": "sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3184,10 +3642,10 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -3283,6 +3741,35 @@ "node": ">= 0.8.0" } }, + "node_modules/ox": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.4.4.tgz", + "integrity": "sha512-oJPEeCDs9iNiPs6J0rTx+Y0KGeCGyCAA3zo94yZhm8G5WpOxrwUtn2Ie/Y8IyARSqqY/j9JTKA3Fc1xs1DvFnw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3321,6 +3808,12 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3648,6 +4141,15 @@ "node": ">= 6" } }, + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "license": "MIT", + "dependencies": { + "esprima": "~4.0.0" + } + }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", @@ -3995,6 +4497,28 @@ "node": "*" } }, + "node_modules/starknet": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/starknet/-/starknet-9.4.2.tgz", + "integrity": "sha512-NFtg077DjddHUSh8sLZ5uB149LEF4NR90FuJ0oF7+zhce7l0oG9Ubqr3H4+jHm/ghHtV+yjlv1UhEoo4SBfILA==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.7.0", + "@noble/hashes": "~1.6.0", + "@scure/base": "~1.2.1", + "@scure/starknet": "1.1.0", + "@starknet-io/get-starknet-wallet-standard": "^5.0.0", + "@starknet-io/starknet-types-010": "npm:@starknet-io/types-js@0.10.0", + "@starknet-io/starknet-types-09": "npm:@starknet-io/types-js@~0.9.1", + "abi-wan-kanabi": "2.2.4", + "lossless-json": "^4.2.0", + "pako": "^2.0.4", + "ts-mixer": "^6.0.3" + }, + "engines": { + "node": ">=22" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -4135,6 +4659,105 @@ "node": ">=8" } }, + "node_modules/test-exclude": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz", + "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^13.0.6", + "minimatch": "^10.2.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -4185,6 +4808,12 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-mixer": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", + "license": "MIT" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -4347,7 +4976,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -4399,6 +5028,15 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4446,6 +5084,21 @@ "uuid": "dist/esm/bin/uuid" } }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 10e5fc7..2fb7010 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "dist/index.js", "scripts": { "typeorm": "tsx --env-file=.env node_modules/typeorm/cli.js -d src/config/persistence/data-source.ts", + "ensure-db": "tsx --env-file=.env src/scripts/ensure-db.ts", "generate-migration": "tsx src/scripts/generate-migration.ts", "run-migration": "yarn typeorm migration:run", "revert-migration": "tsx typeorm migration:revert", @@ -12,9 +13,26 @@ "build": "tsc -p tsconfig.build.json", "pre-start": "node --env-file=.env dist/index.js", "start": "node dist/index.js", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "c8 --check-coverage --lines 90 --statements 90 --functions 90 node --import tsx --test", "lint": "eslint --fix" }, + "c8": { + "include": [ + "src/components/v1/campaign/**/*.ts", + "src/services/cairo/retry.ts", + "src/services/cairo/campaignFactory.client.ts", + "src/services/cairo/campaignFactory.mock.ts", + "src/appMiddlewares/jwtAuth.api.ts", + "src/utils/apiResponse.ts" + ], + "exclude": [ + "src/services/cairo/campaignFactory.starknet.ts", + "src/config/**", + "src/utils/logger.ts", + "src/**/entity.ts", + "src/types/**" + ] + }, "keywords": [], "license": "ISC", "dependencies": { @@ -29,6 +47,7 @@ "nodemailer": "^6.9.16", "pg": "^8.13.1", "reflect-metadata": "^0.2.2", + "starknet": "^9.4.2", "typeorm": "^0.3.20", "ulidx": "^2.4.1", "winston": "^3.17.0", @@ -42,6 +61,7 @@ "@types/jsonwebtoken": "^9.0.7", "@types/ms": "^0.7.34", "@types/node": "^22.9.0", + "c8": "^11.0.0", "eslint": "^9.14.0", "eslint-config-prettier": "^9.1.0", "globals": "^15.12.0", diff --git a/src/__tests__/cairo.client.mock.test.ts b/src/__tests__/cairo.client.mock.test.ts new file mode 100644 index 0000000..09de462 --- /dev/null +++ b/src/__tests__/cairo.client.mock.test.ts @@ -0,0 +1,26 @@ +import test from "node:test" +import assert from "node:assert/strict" + +import { createCairoCampaignClient } from "../services/cairo/campaignFactory.client" + +test("createCairoCampaignClient returns mock client when CAIRO_MOCK=true", async () => { + const prev = process.env.CAIRO_MOCK + process.env.CAIRO_MOCK = "true" + + try { + const client = createCairoCampaignClient() + await client.assertContractAccessible("0x1") + const result = await client.createCampaign({ + campaignRef: "ABCDE", + targetAmount: "1", + donationToken: "0x1", + }) + + assert.ok(result.transactionHash.startsWith("0xmock_")) + assert.equal(result.campaignId, "mock_ABCDE") + } finally { + if (prev === undefined) delete process.env.CAIRO_MOCK + else process.env.CAIRO_MOCK = prev + } +}) + diff --git a/src/__tests__/campaign.service.test.ts b/src/__tests__/campaign.service.test.ts new file mode 100644 index 0000000..7a87beb --- /dev/null +++ b/src/__tests__/campaign.service.test.ts @@ -0,0 +1,140 @@ +import test from "node:test" +import assert from "node:assert/strict" + +import { CampaignService } from "../components/v1/campaign/campaign.service" +import type CampaignEntity from "../components/v1/campaign/campaign.entity" +import type AuditLogEntity from "../components/v1/audit/auditLog.entity" +import type WalletEntity from "../components/v1/wallet/wallet.entity" +import type UserEntity from "../components/v1/user/user.entity" +import type { CairoCampaignClient } from "../services/cairo/campaignFactory.client" + +type Repo = { + data: T[] + findOne: (_arg: any) => Promise + create: (_partial: Partial) => T + save: (_entity: any) => Promise + update: (_criteria: any, _partial: Partial) => Promise +} + +const makeRepo = >(match: (item: T, where: any) => boolean): Repo => { + return { + data: [], + async findOne(arg: any) { + const where = arg?.where ?? {} + return this.data.find((d) => match(d, where)) ?? null + }, + create(partial: Partial) { + return partial as T + }, + async save(entity: any) { + const e = entity as T + this.data.push(e) + return e + }, + async update(criteria: any, partial: Partial) { + const where = criteria ?? {} + const idx = this.data.findIndex((d) => match(d, where)) + if (idx >= 0) this.data[idx] = { ...(this.data[idx] as any), ...(partial as any) } + }, + } +} + +const mockCairo: CairoCampaignClient = { + assertContractAccessible: async () => {}, + createCampaign: async ({ campaignRef }) => ({ + transactionHash: "0xtx", + campaignId: `onchain_${campaignRef}`, + }), +} + +test("CampaignService rejects duplicate campaign_ref", async () => { + const campaigns = makeRepo((c, w) => c.campaignRef === w.campaignRef) + const audits = makeRepo(() => false) + const wallets = makeRepo(() => false) + const users = makeRepo(() => false) + + await campaigns.save({ campaignRef: "ABCDE" } as any) + + const service = new CampaignService(campaigns as any, audits as any, wallets as any, users as any, mockCairo) + + let err: any + try { + await service.createCampaign({ + userId: "u1", + walletAddress: "0x1", + campaignRef: "ABCDE", + targetAmount: "1", + donationToken: "0x2", + }) + } catch (e) { + err = e + } + + assert.ok(err) + assert.equal(err.code, "DUPLICATE_CAMPAIGN_REF") +}) + +test("CampaignService requires wallet and sufficient balance", async () => { + const campaigns = makeRepo(() => false) + const audits = makeRepo(() => false) + const wallets = makeRepo((w, where) => w.address === where.address) + const users = makeRepo((u, where) => u.id === where.id) + + const service = new CampaignService(campaigns as any, audits as any, wallets as any, users as any, mockCairo) + + let missingWalletErr: any + try { + await service.createCampaign({ + userId: "u1", + walletAddress: "0x1", + campaignRef: "ABCDE", + targetAmount: "1", + donationToken: "0x2", + }) + } catch (e) { + missingWalletErr = e + } + assert.ok(missingWalletErr) + assert.equal(missingWalletErr.code, "WALLET_NOT_FOUND") + + await wallets.save({ address: "0x1", balance: "0" } as any) + let insufficientErr: any + try { + await service.createCampaign({ + userId: "u1", + walletAddress: "0x1", + campaignRef: "ABCDE", + targetAmount: "1", + donationToken: "0x2", + }) + } catch (e) { + insufficientErr = e + } + assert.ok(insufficientErr) + assert.equal(insufficientErr.code, "INSUFFICIENT_BALANCE") +}) + +test("CampaignService persists campaign and audit log and increments user count", async () => { + const campaigns = makeRepo(() => false) + const audits = makeRepo(() => false) + const wallets = makeRepo((w, where) => w.address === where.address) + const users = makeRepo((u, where) => u.id === where.id) + + await wallets.save({ address: "0x1", balance: "0.1" } as any) + await users.save({ id: "u1", campaignCount: 0 } as any) + + const service = new CampaignService(campaigns as any, audits as any, wallets as any, users as any, mockCairo) + + const saved = await service.createCampaign({ + userId: "u1", + walletAddress: "0x1", + campaignRef: "ABCDE", + targetAmount: "123", + donationToken: "0x2", + }) + + assert.equal(saved.campaignId, "onchain_ABCDE") + assert.equal(campaigns.data.length, 1) + assert.equal(audits.data.length, 1) + assert.equal((users.data[0] as any).campaignCount, 1) +}) diff --git a/src/__tests__/campaign.validation.test.ts b/src/__tests__/campaign.validation.test.ts new file mode 100644 index 0000000..b7e4701 --- /dev/null +++ b/src/__tests__/campaign.validation.test.ts @@ -0,0 +1,58 @@ +import test from "node:test" +import assert from "node:assert/strict" + +import { createCampaignSchema, isPositiveU256String, isValidStarknetAddress, toU256Parts } from "../components/v1/campaign/campaign.validation" + +test("isPositiveU256String validates u256 integer strings", () => { + assert.equal(isPositiveU256String("0"), false) + assert.equal(isPositiveU256String("-1"), false) + assert.equal(isPositiveU256String("1"), true) + assert.equal(isPositiveU256String("10"), true) + assert.equal(isPositiveU256String(" 10 "), false) +}) + +test("isValidStarknetAddress validates hex contract addresses", () => { + assert.equal(isValidStarknetAddress("0x0"), true) + assert.equal(isValidStarknetAddress("0x123abc"), true) + assert.equal(isValidStarknetAddress("123"), false) + assert.equal(isValidStarknetAddress("0xZZ"), false) +}) + +test("toU256Parts splits numbers into low/high 128-bit limbs", () => { + const parts = toU256Parts("340282366920938463463374607431768211456") // 2^128 + assert.equal(parts.low, "0x0") + assert.equal(parts.high, "0x1") +}) + +test("createCampaignSchema enforces required constraints", () => { + assert.throws(() => createCampaignSchema.parse({}), /Required/) + + assert.throws( + () => + createCampaignSchema.parse({ + campaign_ref: " ", + target_amount: "1", + donation_token: "0x1", + }), + /campaign_ref/ + ) + + assert.throws( + () => + createCampaignSchema.parse({ + campaign_ref: "ABCDE", + target_amount: "0", + donation_token: "0x1", + }), + /target_amount/ + ) + + const ok = createCampaignSchema.parse({ + campaign_ref: "ABCDE", + target_amount: "123", + donation_token: "0x1", + }) + + assert.equal(ok.campaign_ref, "ABCDE") +}) + diff --git a/src/__tests__/retry.test.ts b/src/__tests__/retry.test.ts new file mode 100644 index 0000000..78348f8 --- /dev/null +++ b/src/__tests__/retry.test.ts @@ -0,0 +1,37 @@ +import test from "node:test" +import assert from "node:assert/strict" + +import { withRetry } from "../services/cairo/retry" + +test("withRetry retries and eventually succeeds", async () => { + let calls = 0 + + const result = await withRetry( + async () => { + calls += 1 + if (calls < 3) throw new Error("fail") + return "ok" + }, + { retries: 3, minDelayMs: 1, factor: 1 } + ) + + assert.equal(result, "ok") + assert.equal(calls, 3) +}) + +test("withRetry throws after exhausting retries", async () => { + let calls = 0 + await assert.rejects( + () => + withRetry( + async () => { + calls += 1 + throw new Error("fail") + }, + { retries: 2, minDelayMs: 1, factor: 1 } + ), + /fail/ + ) + assert.equal(calls, 3) // initial + 2 retries +}) + diff --git a/src/appMiddlewares/index.ts b/src/appMiddlewares/index.ts index caf6170..bb0c574 100644 --- a/src/appMiddlewares/index.ts +++ b/src/appMiddlewares/index.ts @@ -12,7 +12,7 @@ export const verifyAllowedMethods = ( 'POST, PUT, PATCH, GET, DELETE' ); - return res.status(403).json('Invalid header method'); + return res.sendStatus(204); } else return next(); } catch (err) { return next(err); diff --git a/src/appMiddlewares/jwtAuth.api.ts b/src/appMiddlewares/jwtAuth.api.ts new file mode 100644 index 0000000..12a2434 --- /dev/null +++ b/src/appMiddlewares/jwtAuth.api.ts @@ -0,0 +1,55 @@ +import type { NextFunction, Response } from "express" +import jwt from "jsonwebtoken" + +import type { IRequest } from "../types/global" +import appConfigs from "../config" +import { sendError } from "../utils/apiResponse" + +type JwtClaims = jwt.JwtPayload & { + sub?: string + userId?: string + id?: string + walletAddress?: string + address?: string + email?: string +} + +export const requireJwtAuthApi = (req: IRequest, res: Response, next: NextFunction) => { + const header = req.headers.authorization + const token = header?.startsWith("Bearer ") ? header.slice("Bearer ".length).trim() : "" + + if (!token) { + return sendError(res, 401, { + code: "AUTH_MISSING_TOKEN", + message: "Missing authentication token", + }) + } + + try { + const decoded = jwt.verify(token, appConfigs.authConfig.jwtSecret) as JwtClaims + + const userId = decoded.sub ?? decoded.userId ?? decoded.id + if (!userId) { + return sendError(res, 401, { + code: "AUTH_INVALID_TOKEN", + message: "Invalid authentication token", + }) + } + + req.auth = { + userId: String(userId), + walletAddress: decoded.walletAddress ?? decoded.address, + email: decoded.email, + claims: decoded, + } + + return next() + } catch (error) { + return sendError(res, 401, { + code: "AUTH_INVALID_TOKEN", + message: "Invalid authentication token", + details: error instanceof Error ? { name: error.name } : {}, + }) + } +} + diff --git a/src/components/v1/Donation/donation.validation.ts b/src/components/v1/Donation/donation.validation.ts index 2b58127..87845e5 100644 --- a/src/components/v1/Donation/donation.validation.ts +++ b/src/components/v1/Donation/donation.validation.ts @@ -1,6 +1,5 @@ import { z } from "zod" import { Network } from "../../../types/enums" -import { ValidationError } from "./path-to-validation-error" // reuse if needed const ethereumAddressRegex = /^0x[a-fA-F0-9]{40}$/ const decimalStringRegex = /^\d+(\.\d+)?$/ diff --git a/src/components/v1/audit/auditLog.entity.ts b/src/components/v1/audit/auditLog.entity.ts new file mode 100644 index 0000000..f02e2e7 --- /dev/null +++ b/src/components/v1/audit/auditLog.entity.ts @@ -0,0 +1,41 @@ +import { Entity, Column, PrimaryColumn, CreateDateColumn, Index, BeforeInsert } from "typeorm" +import { uuid } from "../../../utils" + +@Entity("audit_logs") +@Index("audit_logs_user_id_idx", ["userId"]) +@Index("audit_logs_entity_idx", ["entity", "entityId"]) +export class AuditLogEntity { + @PrimaryColumn("text", { name: "audit_id" }) + auditId: string + + @Column("text", { name: "user_id", nullable: false }) + userId: string + + @Column("text", { nullable: false }) + action: string + + @Column("text", { nullable: false }) + entity: string + + @Column("text", { name: "entity_id", nullable: false }) + entityId: string + + @Column("jsonb", { nullable: true }) + details: Record | null + + @CreateDateColumn({ + name: "created_at", + type: "timestamp", + precision: 3, + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date + + @BeforeInsert() + ensureId() { + if (!this.auditId) this.auditId = uuid() + } +} + +export default AuditLogEntity + diff --git a/src/components/v1/campaign/campaign.controller.ts b/src/components/v1/campaign/campaign.controller.ts new file mode 100644 index 0000000..c3fa89b --- /dev/null +++ b/src/components/v1/campaign/campaign.controller.ts @@ -0,0 +1,97 @@ +import type { Response } from "express" +import { ZodError } from "zod" + +import type { IRequest } from "../../../types/global" +import { sendError, sendSuccess } from "../../../utils/apiResponse" +import { createCampaignSchema } from "./campaign.validation" +import AppDataSource from "../../../config/persistence/data-source" +import CampaignEntity from "./campaign.entity" +import AuditLogEntity from "../audit/auditLog.entity" +import { CampaignService } from "./campaign.service" +import { createCairoCampaignClient } from "../../../services/cairo/campaignFactory.client" +import logger from "../../../utils/logger" +import WalletEntity from "../wallet/wallet.entity" +import UserEntity from "../user/user.entity" + +const mapCreateCampaignError = (error: unknown) => { + const code = typeof (error as any)?.code === "string" ? (error as any).code : "INTERNAL_ERROR" + + switch (code) { + case "DUPLICATE_CAMPAIGN_REF": + return { status: 409, code, message: "campaign_ref already exists" } + case "INSUFFICIENT_BALANCE": + return { status: 400, code, message: "Insufficient wallet balance for transaction fees" } + case "WALLET_NOT_FOUND": + return { status: 400, code, message: "Wallet not found" } + case "MISSING_WALLET_ADDRESS": + return { status: 400, code, message: "Token does not include a wallet address claim" } + default: + return { + status: 500, + code, + message: error instanceof Error ? error.message : "Internal server error", + } + } +} + +export const createCampaign = async (req: IRequest, res: Response) => { + if (!req.auth?.userId) { + return sendError(res, 401, { code: "AUTH_REQUIRED", message: "Authentication required" }) + } + + try { + const parsed = createCampaignSchema.parse(req.body) + + if (!AppDataSource.isInitialized) { + return sendError(res, 500, { code: "DB_NOT_READY", message: "Database not initialized" }) + } + + const service = new CampaignService( + AppDataSource.getRepository(CampaignEntity), + AppDataSource.getRepository(AuditLogEntity), + AppDataSource.getRepository(WalletEntity), + AppDataSource.getRepository(UserEntity), + createCairoCampaignClient() + ) + + const saved = await service.createCampaign({ + userId: req.auth.userId, + walletAddress: req.auth.walletAddress, + campaignRef: parsed.campaign_ref, + targetAmount: parsed.target_amount, + donationToken: parsed.donation_token, + }) + + return sendSuccess( + res, + { + campaign_id: saved.campaignId, + campaign_ref: saved.campaignRef, + target_amount: saved.targetAmount, + donation_token: saved.donationToken, + transaction_hash: saved.transactionHash, + created_at: saved.createdAt.toISOString(), + }, + 201 + ) + } catch (error) { + if (error instanceof ZodError) { + const issue = error.issues?.[0] + return sendError(res, 400, { + code: "VALIDATION_ERROR", + message: issue ? `${issue.path.join(".")} ${issue.message}` : "Invalid request", + details: { issues: error.issues }, + }) + } + + const mapped = mapCreateCampaignError(error) + logger.error( + JSON.stringify({ + name: (error as any)?.name, + code: mapped.code, + message: mapped.message, + }) + ) + return sendError(res, mapped.status, { code: mapped.code, message: mapped.message }) + } +} diff --git a/src/components/v1/campaign/campaign.entity.ts b/src/components/v1/campaign/campaign.entity.ts new file mode 100644 index 0000000..2f3dfbf --- /dev/null +++ b/src/components/v1/campaign/campaign.entity.ts @@ -0,0 +1,41 @@ +import { Entity, Column, PrimaryColumn, CreateDateColumn, Index, BeforeInsert } from "typeorm" +import { uuid } from "../../../utils" + +@Entity("campaigns") +@Index("campaigns_campaign_ref_key", ["campaignRef"], { unique: true }) +@Index("campaigns_user_id_idx", ["userId"]) +export class CampaignEntity { + @PrimaryColumn("text", { name: "campaign_id" }) + campaignId: string + + @Column("text", { name: "user_id", nullable: false }) + userId: string + + @Column("text", { name: "campaign_ref", nullable: false }) + campaignRef: string + + @Column("numeric", { name: "target_amount", precision: 78, scale: 0, nullable: false }) + targetAmount: string + + @Column("text", { name: "donation_token", nullable: false }) + donationToken: string + + @Column("text", { name: "transaction_hash", nullable: false }) + transactionHash: string + + @CreateDateColumn({ + name: "created_at", + type: "timestamp", + precision: 3, + default: () => "CURRENT_TIMESTAMP", + }) + createdAt: Date + + @BeforeInsert() + ensureId() { + if (!this.campaignId) this.campaignId = uuid() + } +} + +export default CampaignEntity + diff --git a/src/components/v1/campaign/campaign.routes.ts b/src/components/v1/campaign/campaign.routes.ts new file mode 100644 index 0000000..1f95419 --- /dev/null +++ b/src/components/v1/campaign/campaign.routes.ts @@ -0,0 +1,31 @@ +import { Router } from "express" +import rateLimit from "express-rate-limit" + +import { requireJwtAuthApi } from "../../../appMiddlewares/jwtAuth.api" +import type { IRequest } from "../../../types/global" +import { sendError } from "../../../utils/apiResponse" +import { createCampaign } from "./campaign.controller" + +const campaignsRateLimiter = rateLimit({ + windowMs: 60 * 60 * 1000, + limit: 5, + standardHeaders: "draft-6", + legacyHeaders: false, + keyGenerator: (req) => { + const r = req as IRequest + return r.auth?.userId ?? req.ip ?? "unknown" + }, + handler: (req, res) => { + return sendError(res, 429, { + code: "RATE_LIMITED", + message: "Too many campaigns created. Try again later.", + details: { limit: 5, windowSeconds: 3600 }, + }) + }, +}) + +const router = Router() + +router.post("/", requireJwtAuthApi, campaignsRateLimiter, createCampaign) + +export default router diff --git a/src/components/v1/campaign/campaign.service.ts b/src/components/v1/campaign/campaign.service.ts new file mode 100644 index 0000000..9bde28e --- /dev/null +++ b/src/components/v1/campaign/campaign.service.ts @@ -0,0 +1,98 @@ +import type { Repository } from "typeorm" + +import CampaignEntity from "./campaign.entity" +import type AuditLogEntity from "../audit/auditLog.entity" + +import WalletEntity from "../wallet/wallet.entity" +import UserEntity from "../user/user.entity" +import logger from "../../../utils/logger" +import type { CairoCampaignClient } from "../../../services/cairo/campaignFactory.client" + +export class CampaignService { + constructor( + private readonly campaignRepository: Repository, + private readonly auditRepository: Repository, + private readonly walletRepository: Repository, + private readonly userRepository: Repository, + private readonly cairoClient: CairoCampaignClient + ) {} + + async createCampaign(args: { + userId: string + walletAddress?: string + campaignRef: string + targetAmount: string + donationToken: string + }) { + const { userId, walletAddress, campaignRef, targetAmount, donationToken } = args + + const existing = await this.campaignRepository.findOne({ where: { campaignRef } }) + if (existing) { + const error = new Error("Duplicate campaign_ref") + ;(error as any).code = "DUPLICATE_CAMPAIGN_REF" + throw error + } + + if (!walletAddress) { + const error = new Error("Missing wallet address in token claims") + ;(error as any).code = "MISSING_WALLET_ADDRESS" + throw error + } + + const wallet = await this.walletRepository.findOne({ where: { address: walletAddress } }) + + if (!wallet) { + const error = new Error("Wallet not found") + ;(error as any).code = "WALLET_NOT_FOUND" + throw error + } + + const walletBalance = Number.parseFloat(wallet.balance ?? "0") + if (!Number.isFinite(walletBalance) || walletBalance <= 0) { + const error = new Error("Insufficient wallet balance for fees") + ;(error as any).code = "INSUFFICIENT_BALANCE" + throw error + } + + const { transactionHash, campaignId } = await this.cairoClient.createCampaign({ + campaignRef, + targetAmount, + donationToken, + }) + + const saved = await this.campaignRepository.save( + this.campaignRepository.create({ + campaignId, + userId, + campaignRef, + targetAmount, + donationToken, + transactionHash, + }) + ) + + await this.auditRepository.save( + this.auditRepository.create({ + userId, + action: "campaign.create", + entity: "campaign", + entityId: saved.campaignId, + details: { + campaignRef, + donationToken, + transactionHash, + }, + } as any) + ) + + const user = await this.userRepository.findOne({ where: { id: userId } }) + if (user) { + const nextCount = (user.campaignCount ?? 0) + 1 + await this.userRepository.update({ id: userId }, { campaignCount: nextCount }) + } else { + logger.info(`User ${userId} not found; skipped campaignCount update`) + } + + return saved + } +} diff --git a/src/components/v1/campaign/campaign.validation.ts b/src/components/v1/campaign/campaign.validation.ts new file mode 100644 index 0000000..1844d60 --- /dev/null +++ b/src/components/v1/campaign/campaign.validation.ts @@ -0,0 +1,46 @@ +import { z } from "zod" + +const MAX_U256 = (1n << 256n) - 1n + +export const isPositiveU256String = (value: string) => { + if (!/^\d+$/.test(value)) return false + const asBigInt = BigInt(value) + return asBigInt > 0n && asBigInt <= MAX_U256 +} + +export const isValidStarknetAddress = (value: string) => { + if (!/^0x[0-9a-fA-F]{1,64}$/.test(value)) return false + try { + const n = BigInt(value) + return n >= 0n && n < (1n << 251n) + } catch { + return false + } +} + +export const createCampaignSchema = z.object({ + campaign_ref: z + .string() + .trim() + .length(5, "campaign_ref must be exactly 5 characters long") + .refine((s) => s.trim().length === 5, "campaign_ref cannot be empty"), + target_amount: z + .string() + .trim() + .refine((s) => isPositiveU256String(s), "target_amount must be a positive u256 integer string"), + donation_token: z + .string() + .trim() + .refine((s) => isValidStarknetAddress(s), "donation_token must be a valid contract address"), +}) + +export type CreateCampaignInput = z.infer + +export const toU256Parts = (value: string) => { + const n = BigInt(value) + const lowMask = (1n << 128n) - 1n + const low = n & lowMask + const high = n >> 128n + return { low: `0x${low.toString(16)}`, high: `0x${high.toString(16)}` } +} + diff --git a/src/components/v1/distribution/distribution.controller.ts b/src/components/v1/distribution/distribution.controller.ts index 5e68d98..a044140 100644 --- a/src/components/v1/distribution/distribution.controller.ts +++ b/src/components/v1/distribution/distribution.controller.ts @@ -1,14 +1,22 @@ import type { Request, Response } from "express" import AppDataSource from "../../../config/persistence/data-source" import { DistributionEntity } from "./distribution.entity" -import { DistributionService } from "./Distribution.service" +import { DistributionService } from "./distribution.service" import type { ApiResponse, DistributionResponseDto, CreateDistributionDto } from "./distribution.dto" -const distributionRepository = AppDataSource.getRepository(DistributionEntity) -const distributionService = new DistributionService(distributionRepository) +const getDistributionService = () => { + if (!AppDataSource.isInitialized) { + throw new Error("Database not initialized") + } + + const distributionRepository = AppDataSource.getRepository(DistributionEntity) + return new DistributionService(distributionRepository) +} export const createDistribution = async (req: Request, res: Response): Promise => { try { + const distributionService = getDistributionService() + const validatedData = req.body as CreateDistributionDto const distribution = await distributionService.createDistribution(validatedData) @@ -33,3 +41,27 @@ export const createDistribution = async (req: Request, res: Response): Promise => { + try { + const distributionService = getDistributionService() + const distributions = await distributionService.listDistributions() + + const response: ApiResponse = { + data: distributions, + success: true, + message: "Distributions fetched successfully", + } + + res.status(200).json(response) + } catch (error) { + console.error("Error in listDistributions:", error) + + const errorResponse: ApiResponse = { + data: null, + success: false, + message: error instanceof Error ? error.message : "Internal server error", + } + + res.status(500).json(errorResponse) + } +} diff --git a/src/components/v1/distribution/distribution.service.ts b/src/components/v1/distribution/distribution.service.ts index 9459ba6..5aaecd1 100644 --- a/src/components/v1/distribution/distribution.service.ts +++ b/src/components/v1/distribution/distribution.service.ts @@ -20,6 +20,20 @@ export class DistributionService { } } + async listDistributions(limit = 50): Promise { + try { + const distributions = await this.distributionRepository.find({ + order: { createdAt: "DESC" as const }, + take: Math.min(Math.max(limit, 1), 200), + }) + + return distributions.map((d) => this.formatDistributionResponse(d)) + } catch (error) { + console.error("Error listing distributions:", error) + throw new Error("Failed to list distributions") + } + } + private prepareDistributionData(data: CreateDistributionDto): Partial { const distributionData: Partial = { userAddress: data.userAddress.toLowerCase(), diff --git a/src/components/v1/distribution/distrubtion.routes.ts b/src/components/v1/distribution/distrubtion.routes.ts index 96cc192..a9a26e3 100644 --- a/src/components/v1/distribution/distrubtion.routes.ts +++ b/src/components/v1/distribution/distrubtion.routes.ts @@ -3,10 +3,12 @@ import policyMiddleware from "../../../appMiddlewares/policy.middleware" import { createDistributionSchema } from "./distribution.validation" import { createDistribution, + listDistributions, } from "./distribution.controller" const distributionRouter = new EnhancedRouter() -distributionRouter.post("/distributions", policyMiddleware(createDistributionSchema), createDistribution) +distributionRouter.get("/", listDistributions) +distributionRouter.post("/", policyMiddleware(createDistributionSchema), createDistribution) export default distributionRouter.getRouter() diff --git a/src/components/v1/platform/platform.utils.ts b/src/components/v1/platform/platform.utils.ts index 0ef7bb9..3b7d8e3 100644 --- a/src/components/v1/platform/platform.utils.ts +++ b/src/components/v1/platform/platform.utils.ts @@ -1,4 +1,4 @@ -import { NextFunction } from 'express'; +import { NextFunction, Response } from 'express'; import { IRequest } from '../../../types/global'; import { InvalidRequestError } from '../../../utils/errorHandler'; diff --git a/src/components/v1/platform/platformControllers/permission.controller.ts b/src/components/v1/platform/platformControllers/permission.controller.ts index 0134d3e..63b7ede 100644 --- a/src/components/v1/platform/platformControllers/permission.controller.ts +++ b/src/components/v1/platform/platformControllers/permission.controller.ts @@ -25,7 +25,6 @@ import platformConstants from '../../../../config/platformConstants'; import AppDataSource from '../../../../config/persistence/data-source'; import { mapPermissions, transformPermissions } from '../platform.utils'; import permissionRepository from '../platformServices/permission.services'; -import userAccessRepository from '../../auth/authServices/useraccess.services'; export const addPermissions = async (req: IRequest, res: Response) => { const { permissions }: z.infer = req.body; @@ -227,16 +226,6 @@ export const deleteRole = async (req: IRequest, res: Response) => { throw new NotFoundError(responseMessages.dataNotExist('Role')); } - const userAccessExists = await userAccessRepository.findOne({ - where: { Role: { id: +roleId } }, - }); - - if (userAccessExists) { - throw new InvalidRequestError( - 'This role cannot be deleted, it has an existing user(s).' - ); - } - await roleRepository.delete({ id: +roleId }); return handleResponse(res, { diff --git a/src/components/v1/routes.api.v1.ts b/src/components/v1/routes.api.v1.ts new file mode 100644 index 0000000..ee09f65 --- /dev/null +++ b/src/components/v1/routes.api.v1.ts @@ -0,0 +1,10 @@ +import EnhancedRouter from "../../utils/enhancedRouter" + +import campaignRoutes from "./campaign/campaign.routes" + +const router = new EnhancedRouter() + +router.use("/campaigns", campaignRoutes) + +export default router.getRouter() + diff --git a/src/components/v1/routes.v1.ts b/src/components/v1/routes.v1.ts index 83a9bea..8f847cf 100644 --- a/src/components/v1/routes.v1.ts +++ b/src/components/v1/routes.v1.ts @@ -1,14 +1,10 @@ import EnhancedRouter from '../../utils/enhancedRouter'; -import platformRoutes from './platform/platform.routes'; -import walletRoutes from './wallet/wallet.routes'; import distributionRoutes from "./distribution/distrubtion.routes" const routerV1 = new EnhancedRouter(); -routerV1.use('/platform', platformRoutes); -routerV1.use('/wallets', walletRoutes); routerV1.use("/distributions", distributionRoutes) export default routerV1.getRouter(); diff --git a/src/components/v1/user/user.entity.ts b/src/components/v1/user/user.entity.ts index a100cc1..21f21e3 100644 --- a/src/components/v1/user/user.entity.ts +++ b/src/components/v1/user/user.entity.ts @@ -13,6 +13,9 @@ export class UserEntity { @Column("text", { nullable: false, unique: true }) email: string + @Column("integer", { name: "campaign_count", default: 0 }) + campaignCount: number + @CreateDateColumn({ name: "created_at", type: "timestamp", diff --git a/src/components/v1/wallet/wallet.entity.ts b/src/components/v1/wallet/wallet.entity.ts index cf24414..539b6b4 100644 --- a/src/components/v1/wallet/wallet.entity.ts +++ b/src/components/v1/wallet/wallet.entity.ts @@ -2,7 +2,7 @@ import { Entity, Column, PrimaryColumn, CreateDateColumn, UpdateDateColumn, Inde import { uuid } from "../../../utils" import { Network } from "../../../types/enums" -@Entity("Wallet") +@Entity("wallet") @Index("Wallet_address_idx", ["address"]) @Index("Wallet_network_idx", ["network"]) export class WalletEntity { diff --git a/src/config/persistence/data-source.ts b/src/config/persistence/data-source.ts index f228faa..160c940 100644 --- a/src/config/persistence/data-source.ts +++ b/src/config/persistence/data-source.ts @@ -1,60 +1,82 @@ -import "reflect-metadata" -import { DataSource } from "typeorm" +import 'reflect-metadata'; +import { DataSource } from 'typeorm'; -import appConfigs from ".." -import logger from "../../utils/logger" -import PlatformEntity from "../../components/v1/platform/platformEntities/platform.entity" +import appConfigs from '..'; +import logger from '../../utils/logger'; +import PlatformEntity from '../../components/v1/platform/platformEntities/platform.entity'; -import { PermissionEntity, RoleEntity } from "../../components/v1/platform/platformEntities/permission.entity" -import WalletEntity from "../../components/v1/wallet/wallet.entity" -import DistributionEntity from "../../components/v1/distribution/distribution.entity" -import FeeConfigEntity from "../../components/v1/feeConfig/feeConfig.entity" -import UserEntity from "src/components/v1/user/user.entity" +import { + PermissionEntity, + RoleEntity, +} from '../../components/v1/platform/platformEntities/permission.entity'; +import WalletEntity from '../../components/v1/wallet/wallet.entity'; +import DistributionEntity from '../../components/v1/distribution/distribution.entity'; +import FeeConfigEntity from '../../components/v1/feeConfig/feeConfig.entity'; +import UserEntity from '../../components/v1/user/user.entity'; +import CampaignEntity from '../../components/v1/campaign/campaign.entity'; +import AuditLogEntity from '../../components/v1/audit/auditLog.entity'; + +const { dbConfigs } = appConfigs; + +const missingDbConfigKeys = [ + ['DATABASE_HOST', dbConfigs.DATABASE_HOST], + ['DATABASE_PORT', dbConfigs.DATABASE_PORT], + ['DATABASE_USERNAME', dbConfigs.DATABASE_USERNAME], + ['DATABASE_PASSWORD', dbConfigs.DATABASE_PASSWORD], + ['DATABASE_NAME', dbConfigs.DATABASE_NAME], +] + .filter(([, value]) => !value) + .map(([key]) => key); + +if (missingDbConfigKeys.length) { + throw new Error( + `Missing required database env vars: ${missingDbConfigKeys.join(', ')}` + ); +} -const { dbConfigs } = appConfigs +const databasePort = Number(dbConfigs.DATABASE_PORT ?? 5432); const AppDataSource = new DataSource({ - host: dbConfigs.DATABASE_HOST, - port: +dbConfigs.DATABASE_PORT!, - username: dbConfigs.DATABASE_USERNAME, - password: dbConfigs.DATABASE_PASSWORD, - database: dbConfigs.DATABASE_NAME, - type: "postgres", - synchronize: false, - logging: true, - entities: [ - RoleEntity, - PlatformEntity, - PermissionEntity, - WalletEntity, - DistributionEntity, - FeeConfigEntity, - UserEntity, - ], - migrations: ["src/migrations/*.js"], - ssl: { - rejectUnauthorized: false, - }, -}) + host: dbConfigs.DATABASE_HOST, + port: Number.isNaN(databasePort) ? 5432 : databasePort, + username: dbConfigs.DATABASE_USERNAME, + password: String(dbConfigs.DATABASE_PASSWORD ?? ''), + database: dbConfigs.DATABASE_NAME, + type: 'postgres', + connectTimeoutMS: 5000, + synchronize: false, + logging: appConfigs.isDev || appConfigs.isLocalDev || appConfigs.isTestMode, + entities: [ + RoleEntity, + PlatformEntity, + PermissionEntity, + WalletEntity, + DistributionEntity, + FeeConfigEntity, + UserEntity, + CampaignEntity, + AuditLogEntity, + ], + migrations: ['src/migrations/*.js'], + ...(appConfigs.isProd || appConfigs.isStaging + ? { ssl: { rejectUnauthorized: false } } + : {}), +}); export const resetDatabase = async () => { - try { - // Connect to the database - await AppDataSource.initialize() - - const queryRunner = AppDataSource.createQueryRunner() - - // Drop all tables - logger.info("Dropping all tables...") - await queryRunner.clearDatabase() - - logger.info("All tables dropped. Database reset successfully!") - } catch (error) { - console.error("Error resetting the database:", error) - } finally { - // Close the connection - await AppDataSource.destroy() - } -} + try { + await AppDataSource.initialize(); + + const queryRunner = AppDataSource.createQueryRunner(); + + logger.info('Dropping all tables...'); + await queryRunner.clearDatabase(); + logger.info('All tables dropped. Database reset successfully!'); + } catch (error) { + console.error('Error resetting the database:', error); + } finally { + await AppDataSource.destroy(); + } +}; -export default AppDataSource +export default AppDataSource; diff --git a/src/config/persistence/seeder.ts b/src/config/persistence/seeder.ts index 7ed3fea..edbb8d3 100644 --- a/src/config/persistence/seeder.ts +++ b/src/config/persistence/seeder.ts @@ -1,135 +1,13 @@ -import { - PermissionEntity, - RoleEntity, -} from '../../components/v1/platform/platformEntities/permission.entity'; -import UserAccessEntity from '../../components/v1/auth/authEntities/userAccess.entity'; -import PlatformEntity, { - FreeDomainEntity, -} from '../../components/v1/platform/platformEntities/platform.entity'; -import UserEntity from '../../components/v1/user/user.entity'; -import platformConstants from '../platformConstants'; -import { AppError } from '../../utils/errorHandler'; import logger from '../../utils/logger'; -import appConfigs from '..'; -import platformRepository from '../../components/v1/platform/platformServices/platform.services'; -import { executeTransaction } from '../../utils'; -import { - defaultPermissions, - defaultRolesAndPermissions, -} from '../defaultRolesAndPermissions'; -import { resolveRolePermissions } from '../../components/v1/platform/platform.utils'; -import { free_domains } from '../../utils/free_email'; +/** + * Seeding is intentionally disabled. + * + * The previous seeding implementation referenced modules/constants that are not + * present in this repository snapshot, and blocked the server from booting. + */ const initiateSeeding = async () => { - const { seedData, platformConfig } = appConfigs; - try { - logger.info('Check Seeding Data'); - - const platformExists = await platformRepository.findOneBy({ - name: platformConfig.name, - }); - - if (platformExists) return; - - logger.info('Initiating Seeding ⚙️'); - logger.info('Seeding started ⚙️'); - - await executeTransaction(async (tx) => { - // Check if platform data exists - - // Seed Platform - const platform = tx.create(PlatformEntity, { - name: platformConfig.name!, - version: '1.0.0', - description: platformConfig?.description || '', - userTypes: - platformConstants.userTypes as unknown as PlatformEntity['userTypes'], - supportedCountries: - platformConstants.supportedCountries as unknown as PlatformEntity['supportedCountries'], - - setups: platformConstants.kycSteps.map((setup) => ({ - type: setup, - status: 'active' as (typeof platformConstants.setupStatus)[number], - activeCountries: [...platformConstants.supportedCountries], - cost: 0, - })), - }); - - logger.info('Seeding in progress (platform user done) ⚙️'); - - // Seed User - const user = tx.create(UserEntity, { - phonePrefix: seedData.phonePrefix!, - firstName: seedData.firstName!, - lastName: seedData.lastName!, - email: seedData.email!, - phone: seedData.phone!, - isConfirmed: true, - countryCode: 'gh', - isSignupComplete: true, - userType: 'platform-admin', - }); - - await tx.save(user); - - logger.info('Seeding in progress (seed user done) ⚙️'); - - const savedPermissions = await tx.save( - PermissionEntity, - defaultPermissions as unknown as PermissionEntity - ); - - const superAdminRole = tx.create(RoleEntity, { - name: 'super-admin', - permissions: [ - { - value: 'supreme', - description: 'one_star_general', - }, - ], - userType: 'platform-admin', - }); - - await tx.save(superAdminRole); - - const resolvedRoles = resolveRolePermissions( - savedPermissions as unknown as PermissionEntity[], - defaultRolesAndPermissions as RoleEntity[] - ); - - await tx.save(RoleEntity, resolvedRoles as unknown as RoleEntity); - - await tx.save( - FreeDomainEntity, - free_domains.map((domain) => ({ domain })) - ); - - // Seed Access - - const userAccess = tx.create(UserAccessEntity, { - userId: user.id, - password: seedData.password!, - Role: superAdminRole, - }); - - await tx.save([platform, userAccess]); - }); - - logger.info('Seeding in progress (seed access done) ⚙️'); - - logger.info('Seeding complete ✅'); - - return; - } catch (err) { - // logger.info('Error occurred in seeding:', err); - - const error = err as Error; - throw new AppError({ - name: error.name, - message: error.message, - httpCode: 500, - }); - } + logger.info('Seeding disabled (no-op).'); }; export default initiateSeeding; diff --git a/src/index.ts b/src/index.ts index 6545f5b..f4fa56b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,12 @@ import 'reflect-metadata'; import expressRateLimiter from 'express-rate-limit'; -import express from 'express'; +import express, { type Router } from 'express'; import helmet from 'helmet'; import cors from 'cors'; import fingerprintMiddleware from './appMiddlewares/fingerprint.middleware'; import { verifyAllowedMethods } from './appMiddlewares'; -import routerV1 from './components/v1/routes.v1'; import { AppError, @@ -15,7 +14,6 @@ import { errorHandlingMiddleware, } from './utils/errorHandler'; import AppDataSource from './config/persistence/data-source'; -import initiateSeeding from './config/persistence/seeder'; import logger from './utils/logger'; import appConfigs from './config'; @@ -29,12 +27,7 @@ const rateLimiter = expressRateLimiter({ }); const initializeDb = () => { - AppDataSource.initialize() - .then(() => { - logger.info('DB Connected Successfully!!!'); - initiateSeeding(); - }) - .catch(errorHandler.handleError); + return AppDataSource.initialize(); }; const allowedOrigins = ['http://localhost:3000', 'http://localhost:3001']; @@ -74,8 +67,9 @@ const initializeMiddlewares = () => { .use(verifyAllowedMethods); }; -const initializeRoutes = () => { +const initializeRoutes = (routerV1: Router, routerApiV1?: Router) => { app.use('/v1/', routerV1); + if (routerApiV1) app.use('/api/v1/', routerApiV1); // if (appConfigs.isProd || appConfigs.isStaging) { // app.use(Sentry.Handlers.errorHandler()); @@ -99,20 +93,32 @@ const initializeRoutes = () => { app.use(errorHandlingMiddleware); }; -try { - initializeDb(); - initializeMiddlewares(); - initializeRoutes(); -} catch (error) { - errorHandler.handleError(error as AppError); -} +const startServer = async () => { + try { + logger.info('Connecting to DB...'); + await initializeDb(); + logger.info('DB Connected Successfully!!!'); -const port = appConfigs.port; + initializeMiddlewares(); -app.listen(port, () => { - logger.info('[+]Logging Service Started'); - logger.info(`[+] CLOUD Server Running ... on Port -> ${port}`); -}); + const { default: routerV1 } = await import('./components/v1/routes.v1'); + const { default: routerApiV1 } = await import( + './components/v1/routes.api.v1' + ); + initializeRoutes(routerV1, routerApiV1); + + const port = appConfigs.port; + app.listen(port, () => { + logger.info('[+]Logging Service Started'); + logger.info(`[+] CLOUD Server Running ... on Port -> ${port}`); + }); + } catch (error) { + errorHandler.handleError(error as AppError); + process.exit(1); + } +}; + +void startServer(); process.on('uncaughtException', async (err) => { try { diff --git a/src/migrations/CreateCampaignAndAudit1760000000001.js b/src/migrations/CreateCampaignAndAudit1760000000001.js new file mode 100644 index 0000000..f884be0 --- /dev/null +++ b/src/migrations/CreateCampaignAndAudit1760000000001.js @@ -0,0 +1,63 @@ +const { MigrationInterface } = require("typeorm") + +module.exports = class CreateCampaignAndAudit1760000000001 { + name = "CreateCampaignAndAudit1760000000001" + + async up(queryRunner) { + await queryRunner.query(` + ALTER TABLE "User" + ADD COLUMN IF NOT EXISTS "campaign_count" integer NOT NULL DEFAULT 0 + `) + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS "campaigns" ( + "campaign_id" text NOT NULL, + "user_id" text NOT NULL, + "campaign_ref" text NOT NULL, + "target_amount" numeric(78,0) NOT NULL, + "donation_token" text NOT NULL, + "transaction_hash" text NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "PK_campaigns_campaign_id" PRIMARY KEY ("campaign_id"), + CONSTRAINT "UQ_campaigns_campaign_ref" UNIQUE ("campaign_ref") + ) + `) + + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS "campaigns_user_id_idx" ON "campaigns" ("user_id") + `) + + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS "audit_logs" ( + "audit_id" text NOT NULL, + "user_id" text NOT NULL, + "action" text NOT NULL, + "entity" text NOT NULL, + "entity_id" text NOT NULL, + "details" jsonb, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "PK_audit_logs_audit_id" PRIMARY KEY ("audit_id") + ) + `) + + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS "audit_logs_user_id_idx" ON "audit_logs" ("user_id") + `) + + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS "audit_logs_entity_idx" ON "audit_logs" ("entity", "entity_id") + `) + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX IF EXISTS "audit_logs_entity_idx"`) + await queryRunner.query(`DROP INDEX IF EXISTS "audit_logs_user_id_idx"`) + await queryRunner.query(`DROP TABLE IF EXISTS "audit_logs"`) + + await queryRunner.query(`DROP INDEX IF EXISTS "campaigns_user_id_idx"`) + await queryRunner.query(`DROP TABLE IF EXISTS "campaigns"`) + + await queryRunner.query(`ALTER TABLE "User" DROP COLUMN IF EXISTS "campaign_count"`) + } +} + diff --git a/src/migrations/CreateCoreEntities.js b/src/migrations/CreateCoreEntities.js index 3d22c96..636d6f1 100644 --- a/src/migrations/CreateCoreEntities.js +++ b/src/migrations/CreateCoreEntities.js @@ -69,18 +69,30 @@ module.exports = class CreateCoreEntities1703000000000 { ) `) - // Update Wallet table + // Ensure Wallet table exists (this repo snapshot does not include an earlier wallet migration) await queryRunner.query(` - ALTER TABLE "wallet" ADD COLUMN "network" "network" NOT NULL DEFAULT 'mainnet' + CREATE TABLE IF NOT EXISTS "wallet" ( + "id" text NOT NULL, + "address" text NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "UQ_wallet_address" UNIQUE ("address"), + CONSTRAINT "PK_wallet_id" PRIMARY KEY ("id") + ) + `) + + // Update Wallet table (backwards compatible) + await queryRunner.query(` + ALTER TABLE "wallet" ADD COLUMN IF NOT EXISTS "network" "network" NOT NULL DEFAULT 'mainnet' `) await queryRunner.query(` - ALTER TABLE "wallet" ADD COLUMN "chain_id" text NOT NULL DEFAULT '' + ALTER TABLE "wallet" ADD COLUMN IF NOT EXISTS "chain_id" text NOT NULL DEFAULT '' `) await queryRunner.query(` - ALTER TABLE "wallet" ADD COLUMN "chain_name" text NOT NULL DEFAULT '' + ALTER TABLE "wallet" ADD COLUMN IF NOT EXISTS "chain_name" text NOT NULL DEFAULT '' `) await queryRunner.query(` - ALTER TABLE "wallet" ADD COLUMN "balance" decimal(65,30) DEFAULT '0' + ALTER TABLE "wallet" ADD COLUMN IF NOT EXISTS "balance" decimal(65,30) DEFAULT '0' `) // Create indexes diff --git a/src/services/cairo/campaignFactory.client.ts b/src/services/cairo/campaignFactory.client.ts new file mode 100644 index 0000000..b4169f9 --- /dev/null +++ b/src/services/cairo/campaignFactory.client.ts @@ -0,0 +1,26 @@ +import { env } from "process" + +import { createMockCampaignClient } from "./campaignFactory.mock" +import { createStarknetCampaignClient } from "./campaignFactory.starknet" + +export type CreateCampaignOnChainArgs = { + campaignRef: string + targetAmount: string + donationToken: string +} + +export type CreateCampaignOnChainResult = { + transactionHash: string + campaignId: string +} + +export type CairoCampaignClient = { + assertContractAccessible: (_address: string) => Promise + createCampaign: (_args: CreateCampaignOnChainArgs) => Promise +} + +export const createCairoCampaignClient = (): CairoCampaignClient => { + const isMock = env.CAIRO_MOCK === "true" || env.CAIRO_MOCK === "1" + return isMock ? createMockCampaignClient() : createStarknetCampaignClient() +} + diff --git a/src/services/cairo/campaignFactory.mock.ts b/src/services/cairo/campaignFactory.mock.ts new file mode 100644 index 0000000..2986bc6 --- /dev/null +++ b/src/services/cairo/campaignFactory.mock.ts @@ -0,0 +1,12 @@ +import type { CairoCampaignClient } from "./campaignFactory.client" + +export const createMockCampaignClient = (): CairoCampaignClient => { + return { + assertContractAccessible: async () => {}, + createCampaign: async ({ campaignRef }) => { + const tx = `0xmock_${Date.now().toString(16)}` + return { transactionHash: tx, campaignId: `mock_${campaignRef}` } + }, + } +} + diff --git a/src/services/cairo/campaignFactory.starknet.ts b/src/services/cairo/campaignFactory.starknet.ts new file mode 100644 index 0000000..bbc38db --- /dev/null +++ b/src/services/cairo/campaignFactory.starknet.ts @@ -0,0 +1,77 @@ +import { env } from "process" +import { Account, RpcProvider, shortString } from "starknet" + +import logger from "../../utils/logger" +import { withRetry } from "./retry" +import { toU256Parts } from "../../components/v1/campaign/campaign.validation" +import type { CairoCampaignClient } from "./campaignFactory.client" + +const getRequiredEnv = (key: string) => { + const value = env[key] + if (!value) throw new Error(`Missing required env var: ${key}`) + return value +} + +const parseEventCampaignId = (receipt: any): string | null => { + const eventKey = env.CAMPAIGN_CREATED_EVENT_KEY + if (!eventKey) return null + + const normalizedKey = eventKey.toLowerCase() + const events: any[] = receipt?.events ?? [] + const match = events.find((e) => (e.keys ?? []).map((k: string) => k.toLowerCase()).includes(normalizedKey)) + if (!match) return null + + const data0 = match?.data?.[0] + return typeof data0 === "string" && data0.length ? data0 : null +} + +export const createStarknetCampaignClient = (): CairoCampaignClient => { + const rpcUrl = getRequiredEnv("CAIRO_RPC_URL") + const accountAddress = getRequiredEnv("CAIRO_ACCOUNT_ADDRESS") + const privateKey = getRequiredEnv("CAIRO_PRIVATE_KEY") + const factoryAddress = getRequiredEnv("CAIRO_FACTORY_CONTRACT_ADDRESS") + + const provider = new RpcProvider({ nodeUrl: rpcUrl }) + const account = new Account({ provider, address: accountAddress, signer: privateKey }) + + return { + assertContractAccessible: async (address: string) => { + await withRetry(async () => { + await provider.getClassHashAt(address) + }) + }, + createCampaign: async ({ campaignRef, targetAmount, donationToken }) => { + await withRetry(async () => { + await provider.getClassHashAt(factoryAddress) + }) + await withRetry(async () => { + await provider.getClassHashAt(donationToken) + }) + + const campaignRefFelt = shortString.encodeShortString(campaignRef) + const { low, high } = toU256Parts(targetAmount) + + const invocation = { + contractAddress: factoryAddress, + entrypoint: "create_campaign", + calldata: [campaignRefFelt, low, high, donationToken], + } + + const result = await withRetry(async () => { + logger.info(`Submitting create_campaign tx for ref=${campaignRef}`) + return await account.execute(invocation as any) + }) + + const transactionHash = (result as any)?.transaction_hash ?? (result as any)?.transactionHash + if (!transactionHash) { + throw new Error("Contract call did not return a transaction hash") + } + + const receipt = await withRetry(async () => await provider.waitForTransaction(transactionHash)) + const campaignId = parseEventCampaignId(receipt) ?? transactionHash + + return { transactionHash, campaignId } + }, + } +} + diff --git a/src/services/cairo/retry.ts b/src/services/cairo/retry.ts new file mode 100644 index 0000000..c54b0da --- /dev/null +++ b/src/services/cairo/retry.ts @@ -0,0 +1,33 @@ +export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) + +export const withRetry = async ( + fn: () => Promise, + options?: { + retries?: number + minDelayMs?: number + factor?: number + } +) => { + const retries = options?.retries ?? 3 + const minDelayMs = options?.minDelayMs ?? 250 + const factor = options?.factor ?? 2 + + let attempt = 0 + let lastError: unknown + while (attempt <= retries) { + try { + return await fn() + } catch (error) { + lastError = error + if (attempt === retries) break + + const backoff = Math.round(minDelayMs * Math.pow(factor, attempt)) + const jitter = Math.round(Math.random() * 100) + await sleep(backoff + jitter) + attempt += 1 + } + } + + throw lastError +} + diff --git a/src/types/global.ts b/src/types/global.ts index 78c69d3..d06b2b6 100644 --- a/src/types/global.ts +++ b/src/types/global.ts @@ -86,6 +86,12 @@ export interface IRequest extends Request { // file?: Request['file']; permissions?: string[]; paginationData?: IPaginationData; + auth?: { + userId: string; + walletAddress?: string; + email?: string; + claims?: Record; + }; } export interface AppErrorAttributes { diff --git a/src/utils/apiResponse.ts b/src/utils/apiResponse.ts new file mode 100644 index 0000000..349089d --- /dev/null +++ b/src/utils/apiResponse.ts @@ -0,0 +1,26 @@ +import type { Response } from "express" + +export type ApiErrorPayload = { + code: string + message: string + details?: Record +} + +export const sendSuccess = (res: Response, data: T, status = 200) => { + return res.status(status).json({ + success: true, + data, + }) +} + +export const sendError = (res: Response, status: number, error: ApiErrorPayload) => { + return res.status(status).json({ + success: false, + error: { + code: error.code, + message: error.message, + details: error.details ?? {}, + }, + }) +} + diff --git a/src/utils/index.ts b/src/utils/index.ts index 4efbc76..78a67aa 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,8 +1,8 @@ import { ulid as ulidx } from 'ulidx'; import { randomUUID } from 'crypto'; import { z } from 'zod'; +import type { EntityManager } from 'typeorm'; -import AppDataSource from '../config/persistence/data-source'; import { IRequest } from '../types/global'; export const isValidUuid = (uuid: string): boolean => { @@ -16,9 +16,13 @@ export const isValidUuid = (uuid: string): boolean => { */ export async function executeTransaction( transactionCallback: ( - _transactionManager: typeof AppDataSource.manager + _transactionManager: EntityManager ) => Promise ): Promise { + const { default: AppDataSource } = await import( + '../config/persistence/data-source' + ); + return AppDataSource.transaction(async (transactionManager) => { try { return await transactionCallback(transactionManager); diff --git a/tsconfig.build.tsbuildinfo b/tsconfig.build.tsbuildinfo deleted file mode 100644 index 9136afe..0000000 --- a/tsconfig.build.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/reflect-metadata/index.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/buffer/index.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/dom-events.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/mime/index.d.ts","./node_modules/@types/send/index.d.ts","./node_modules/@types/qs/index.d.ts","./node_modules/@types/range-parser/index.d.ts","./node_modules/@types/express-serve-static-core/index.d.ts","./node_modules/@types/http-errors/index.d.ts","./node_modules/@types/serve-static/index.d.ts","./node_modules/@types/connect/index.d.ts","./node_modules/@types/body-parser/index.d.ts","./node_modules/@types/express/index.d.ts","./node_modules/express-rate-limit/dist/index.d.ts","./node_modules/helmet/index.d.cts","./node_modules/@types/cors/index.d.ts","./src/config/platformconstants.ts","./node_modules/typeorm/metadata/types/relationtypes.d.ts","./node_modules/typeorm/metadata/types/deferrabletype.d.ts","./node_modules/typeorm/metadata/types/ondeletetype.d.ts","./node_modules/typeorm/metadata/types/onupdatetype.d.ts","./node_modules/typeorm/decorator/options/relationoptions.d.ts","./node_modules/typeorm/metadata/types/propertytypeinfunction.d.ts","./node_modules/typeorm/common/objecttype.d.ts","./node_modules/typeorm/common/entitytarget.d.ts","./node_modules/typeorm/metadata/types/relationtypeinfunction.d.ts","./node_modules/typeorm/metadata-args/relationmetadataargs.d.ts","./node_modules/typeorm/driver/types/columntypes.d.ts","./node_modules/typeorm/decorator/options/valuetransformer.d.ts","./node_modules/typeorm/decorator/options/columncommonoptions.d.ts","./node_modules/typeorm/decorator/options/columnoptions.d.ts","./node_modules/typeorm/metadata-args/types/columnmode.d.ts","./node_modules/typeorm/metadata-args/columnmetadataargs.d.ts","./node_modules/typeorm/common/objectliteral.d.ts","./node_modules/typeorm/schema-builder/options/tablecolumnoptions.d.ts","./node_modules/typeorm/schema-builder/table/tablecolumn.d.ts","./node_modules/typeorm/schema-builder/options/viewoptions.d.ts","./node_modules/typeorm/schema-builder/view/view.d.ts","./node_modules/typeorm/naming-strategy/namingstrategyinterface.d.ts","./node_modules/typeorm/metadata/foreignkeymetadata.d.ts","./node_modules/typeorm/metadata/relationmetadata.d.ts","./node_modules/typeorm/metadata-args/embeddedmetadataargs.d.ts","./node_modules/typeorm/metadata-args/relationidmetadataargs.d.ts","./node_modules/typeorm/metadata/relationidmetadata.d.ts","./node_modules/typeorm/metadata/relationcountmetadata.d.ts","./node_modules/typeorm/metadata/types/eventlistenertypes.d.ts","./node_modules/typeorm/metadata-args/entitylistenermetadataargs.d.ts","./node_modules/typeorm/metadata/entitylistenermetadata.d.ts","./node_modules/typeorm/metadata-args/uniquemetadataargs.d.ts","./node_modules/typeorm/metadata/uniquemetadata.d.ts","./node_modules/typeorm/metadata/embeddedmetadata.d.ts","./node_modules/typeorm/metadata/columnmetadata.d.ts","./node_modules/typeorm/driver/types/ctecapabilities.d.ts","./node_modules/typeorm/driver/types/mappedcolumntypes.d.ts","./node_modules/typeorm/driver/query.d.ts","./node_modules/typeorm/driver/sqlinmemory.d.ts","./node_modules/typeorm/schema-builder/schemabuilder.d.ts","./node_modules/typeorm/driver/types/datatypedefaults.d.ts","./node_modules/typeorm/entity-schema/entityschemaindexoptions.d.ts","./node_modules/typeorm/driver/types/geojsontypes.d.ts","./node_modules/typeorm/decorator/options/spatialcolumnoptions.d.ts","./node_modules/typeorm/decorator/options/foreignkeyoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemacolumnforeignkeyoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemacolumnoptions.d.ts","./node_modules/typeorm/decorator/options/joincolumnoptions.d.ts","./node_modules/typeorm/decorator/options/jointablemultiplecolumnsoptions.d.ts","./node_modules/typeorm/decorator/options/jointableoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemarelationoptions.d.ts","./node_modules/typeorm/find-options/orderbycondition.d.ts","./node_modules/typeorm/metadata/types/tabletypes.d.ts","./node_modules/typeorm/entity-schema/entityschemauniqueoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemacheckoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemaexclusionoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemainheritanceoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemarelationidoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemaforeignkeyoptions.d.ts","./node_modules/typeorm/entity-schema/entityschemaoptions.d.ts","./node_modules/typeorm/entity-schema/entityschema.d.ts","./node_modules/typeorm/logger/logger.d.ts","./node_modules/typeorm/logger/loggeroptions.d.ts","./node_modules/typeorm/driver/types/databasetype.d.ts","./node_modules/typeorm/cache/queryresultcacheoptions.d.ts","./node_modules/typeorm/cache/queryresultcache.d.ts","./node_modules/typeorm/common/mixedlist.d.ts","./node_modules/typeorm/data-source/basedatasourceoptions.d.ts","./node_modules/typeorm/driver/types/replicationmode.d.ts","./node_modules/typeorm/schema-builder/options/tableforeignkeyoptions.d.ts","./node_modules/typeorm/schema-builder/table/tableforeignkey.d.ts","./node_modules/typeorm/driver/types/upserttype.d.ts","./node_modules/typeorm/driver/driver.d.ts","./node_modules/typeorm/find-options/joinoptions.d.ts","./node_modules/typeorm/find-options/findoperatortype.d.ts","./node_modules/typeorm/find-options/findoperator.d.ts","./node_modules/typeorm/driver/mongodb/bson.typings.d.ts","./node_modules/typeorm/platform/platformtools.d.ts","./node_modules/typeorm/driver/mongodb/typings.d.ts","./node_modules/typeorm/find-options/equaloperator.d.ts","./node_modules/typeorm/find-options/findoptionswhere.d.ts","./node_modules/typeorm/find-options/findoptionsselect.d.ts","./node_modules/typeorm/find-options/findoptionsrelations.d.ts","./node_modules/typeorm/find-options/findoptionsorder.d.ts","./node_modules/typeorm/find-options/findoneoptions.d.ts","./node_modules/typeorm/find-options/findmanyoptions.d.ts","./node_modules/typeorm/common/deeppartial.d.ts","./node_modules/typeorm/repository/saveoptions.d.ts","./node_modules/typeorm/repository/removeoptions.d.ts","./node_modules/typeorm/find-options/mongodb/mongofindoneoptions.d.ts","./node_modules/typeorm/find-options/mongodb/mongofindmanyoptions.d.ts","./node_modules/typeorm/schema-builder/options/tableuniqueoptions.d.ts","./node_modules/typeorm/schema-builder/table/tableunique.d.ts","./node_modules/typeorm/subscriber/broadcasterresult.d.ts","./node_modules/typeorm/subscriber/event/transactioncommitevent.d.ts","./node_modules/typeorm/subscriber/event/transactionrollbackevent.d.ts","./node_modules/typeorm/subscriber/event/transactionstartevent.d.ts","./node_modules/typeorm/subscriber/event/updateevent.d.ts","./node_modules/typeorm/subscriber/event/removeevent.d.ts","./node_modules/typeorm/subscriber/event/insertevent.d.ts","./node_modules/typeorm/subscriber/event/loadevent.d.ts","./node_modules/typeorm/subscriber/event/softremoveevent.d.ts","./node_modules/typeorm/subscriber/event/recoverevent.d.ts","./node_modules/typeorm/subscriber/event/queryevent.d.ts","./node_modules/typeorm/subscriber/entitysubscriberinterface.d.ts","./node_modules/typeorm/subscriber/broadcaster.d.ts","./node_modules/typeorm/schema-builder/options/tablecheckoptions.d.ts","./node_modules/typeorm/metadata-args/checkmetadataargs.d.ts","./node_modules/typeorm/metadata/checkmetadata.d.ts","./node_modules/typeorm/schema-builder/table/tablecheck.d.ts","./node_modules/typeorm/schema-builder/options/tableexclusionoptions.d.ts","./node_modules/typeorm/metadata-args/exclusionmetadataargs.d.ts","./node_modules/typeorm/metadata/exclusionmetadata.d.ts","./node_modules/typeorm/schema-builder/table/tableexclusion.d.ts","./node_modules/typeorm/driver/mongodb/mongoqueryrunner.d.ts","./node_modules/typeorm/query-builder/querypartialentity.d.ts","./node_modules/typeorm/query-runner/queryresult.d.ts","./node_modules/typeorm/query-builder/result/insertresult.d.ts","./node_modules/typeorm/query-builder/result/updateresult.d.ts","./node_modules/typeorm/query-builder/result/deleteresult.d.ts","./node_modules/typeorm/entity-manager/mongoentitymanager.d.ts","./node_modules/typeorm/repository/mongorepository.d.ts","./node_modules/typeorm/find-options/findtreeoptions.d.ts","./node_modules/typeorm/repository/treerepository.d.ts","./node_modules/typeorm/query-builder/transformer/plainobjecttonewentitytransformer.d.ts","./node_modules/typeorm/driver/types/isolationlevel.d.ts","./node_modules/typeorm/query-builder/whereexpressionbuilder.d.ts","./node_modules/typeorm/query-builder/brackets.d.ts","./node_modules/typeorm/query-builder/insertorupdateoptions.d.ts","./node_modules/typeorm/repository/upsertoptions.d.ts","./node_modules/typeorm/common/pickkeysbytype.d.ts","./node_modules/typeorm/entity-manager/entitymanager.d.ts","./node_modules/typeorm/repository/repository.d.ts","./node_modules/typeorm/migration/migrationinterface.d.ts","./node_modules/typeorm/migration/migration.d.ts","./node_modules/typeorm/driver/cockroachdb/cockroachconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/cockroachdb/cockroachconnectionoptions.d.ts","./node_modules/typeorm/driver/mysql/mysqlconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/mysql/mysqlconnectionoptions.d.ts","./node_modules/typeorm/driver/postgres/postgresconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/postgres/postgresconnectionoptions.d.ts","./node_modules/typeorm/driver/sqlite/sqliteconnectionoptions.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/defaultauthentication.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/azureactivedirectoryaccesstokenauthentication.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/azureactivedirectorydefaultauthentication.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/azureactivedirectorymsiappserviceauthentication.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/azureactivedirectorymsivmauthentication.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/azureactivedirectorypasswordauthentication.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/azureactivedirectoryserviceprincipalsecret.d.ts","./node_modules/typeorm/driver/sqlserver/authentication/ntlmauthentication.d.ts","./node_modules/typeorm/driver/sqlserver/sqlserverconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/sqlserver/sqlserverconnectionoptions.d.ts","./node_modules/typeorm/driver/oracle/oracleconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/oracle/oracleconnectionoptions.d.ts","./node_modules/typeorm/driver/mongodb/mongoconnectionoptions.d.ts","./node_modules/typeorm/driver/cordova/cordovaconnectionoptions.d.ts","./node_modules/typeorm/driver/sqljs/sqljsconnectionoptions.d.ts","./node_modules/typeorm/driver/react-native/reactnativeconnectionoptions.d.ts","./node_modules/typeorm/driver/nativescript/nativescriptconnectionoptions.d.ts","./node_modules/typeorm/driver/expo/expoconnectionoptions.d.ts","./node_modules/typeorm/driver/aurora-mysql/auroramysqlconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/aurora-mysql/auroramysqlconnectionoptions.d.ts","./node_modules/typeorm/driver/sap/sapconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/sap/sapconnectionoptions.d.ts","./node_modules/typeorm/driver/aurora-postgres/aurorapostgresconnectionoptions.d.ts","./node_modules/typeorm/driver/better-sqlite3/bettersqlite3connectionoptions.d.ts","./node_modules/typeorm/driver/capacitor/capacitorconnectionoptions.d.ts","./node_modules/typeorm/connection/baseconnectionoptions.d.ts","./node_modules/typeorm/driver/spanner/spannerconnectioncredentialsoptions.d.ts","./node_modules/typeorm/driver/spanner/spannerconnectionoptions.d.ts","./node_modules/typeorm/data-source/datasourceoptions.d.ts","./node_modules/typeorm/entity-manager/sqljsentitymanager.d.ts","./node_modules/typeorm/query-builder/relationloader.d.ts","./node_modules/typeorm/query-builder/relationidloader.d.ts","./node_modules/typeorm/data-source/datasource.d.ts","./node_modules/typeorm/metadata-args/tablemetadataargs.d.ts","./node_modules/typeorm/metadata/types/treetypes.d.ts","./node_modules/typeorm/metadata/types/closuretreeoptions.d.ts","./node_modules/typeorm/metadata-args/treemetadataargs.d.ts","./node_modules/typeorm/metadata/entitymetadata.d.ts","./node_modules/typeorm/metadata-args/indexmetadataargs.d.ts","./node_modules/typeorm/metadata/indexmetadata.d.ts","./node_modules/typeorm/schema-builder/options/tableindexoptions.d.ts","./node_modules/typeorm/schema-builder/table/tableindex.d.ts","./node_modules/typeorm/schema-builder/options/tableoptions.d.ts","./node_modules/typeorm/schema-builder/table/table.d.ts","./node_modules/typeorm/query-runner/queryrunner.d.ts","./node_modules/typeorm/query-builder/querybuildercte.d.ts","./node_modules/typeorm/query-builder/alias.d.ts","./node_modules/typeorm/query-builder/joinattribute.d.ts","./node_modules/typeorm/query-builder/relation-id/relationidattribute.d.ts","./node_modules/typeorm/query-builder/relation-count/relationcountattribute.d.ts","./node_modules/typeorm/query-builder/selectquery.d.ts","./node_modules/typeorm/query-builder/selectquerybuilderoption.d.ts","./node_modules/typeorm/query-builder/whereclause.d.ts","./node_modules/typeorm/query-builder/queryexpressionmap.d.ts","./node_modules/typeorm/query-builder/updatequerybuilder.d.ts","./node_modules/typeorm/query-builder/deletequerybuilder.d.ts","./node_modules/typeorm/query-builder/softdeletequerybuilder.d.ts","./node_modules/typeorm/query-builder/insertquerybuilder.d.ts","./node_modules/typeorm/query-builder/relationquerybuilder.d.ts","./node_modules/typeorm/query-builder/notbrackets.d.ts","./node_modules/typeorm/query-builder/querybuilder.d.ts","./node_modules/typeorm/query-builder/selectquerybuilder.d.ts","./node_modules/typeorm/metadata-args/relationcountmetadataargs.d.ts","./node_modules/typeorm/metadata-args/namingstrategymetadataargs.d.ts","./node_modules/typeorm/metadata-args/joincolumnmetadataargs.d.ts","./node_modules/typeorm/metadata-args/jointablemetadataargs.d.ts","./node_modules/typeorm/metadata-args/entitysubscribermetadataargs.d.ts","./node_modules/typeorm/metadata-args/inheritancemetadataargs.d.ts","./node_modules/typeorm/metadata-args/discriminatorvaluemetadataargs.d.ts","./node_modules/typeorm/metadata-args/entityrepositorymetadataargs.d.ts","./node_modules/typeorm/metadata-args/transactionentitymetadataargs.d.ts","./node_modules/typeorm/metadata-args/transactionrepositorymetadataargs.d.ts","./node_modules/typeorm/metadata-args/generatedmetadataargs.d.ts","./node_modules/typeorm/metadata-args/foreignkeymetadataargs.d.ts","./node_modules/typeorm/metadata-args/metadataargsstorage.d.ts","./node_modules/typeorm/connection/connectionmanager.d.ts","./node_modules/typeorm/globals.d.ts","./node_modules/typeorm/container.d.ts","./node_modules/typeorm/common/relationtype.d.ts","./node_modules/typeorm/error/typeormerror.d.ts","./node_modules/typeorm/error/cannotreflectmethodparametertypeerror.d.ts","./node_modules/typeorm/error/alreadyhasactiveconnectionerror.d.ts","./node_modules/typeorm/persistence/subjectchangemap.d.ts","./node_modules/typeorm/persistence/subject.d.ts","./node_modules/typeorm/error/subjectwithoutidentifiererror.d.ts","./node_modules/typeorm/error/cannotconnectalreadyconnectederror.d.ts","./node_modules/typeorm/error/locknotsupportedongivendrivererror.d.ts","./node_modules/typeorm/error/connectionisnotseterror.d.ts","./node_modules/typeorm/error/cannotcreateentityidmaperror.d.ts","./node_modules/typeorm/error/metadataalreadyexistserror.d.ts","./node_modules/typeorm/error/cannotdetermineentityerror.d.ts","./node_modules/typeorm/error/updatevaluesmissingerror.d.ts","./node_modules/typeorm/error/treerepositorynotsupportederror.d.ts","./node_modules/typeorm/error/customrepositorynotfounderror.d.ts","./node_modules/typeorm/error/transactionnotstartederror.d.ts","./node_modules/typeorm/error/transactionalreadystartederror.d.ts","./node_modules/typeorm/error/entitynotfounderror.d.ts","./node_modules/typeorm/error/entitymetadatanotfounderror.d.ts","./node_modules/typeorm/error/mustbeentityerror.d.ts","./node_modules/typeorm/error/optimisticlockversionmismatcherror.d.ts","./node_modules/typeorm/error/limitonupdatenotsupportederror.d.ts","./node_modules/typeorm/error/primarycolumncannotbenullableerror.d.ts","./node_modules/typeorm/error/customrepositorycannotinheritrepositoryerror.d.ts","./node_modules/typeorm/error/queryrunnerprovideralreadyreleasederror.d.ts","./node_modules/typeorm/error/cannotattachtreechildrenentityerror.d.ts","./node_modules/typeorm/error/customrepositorydoesnothaveentityerror.d.ts","./node_modules/typeorm/error/missingdeletedatecolumnerror.d.ts","./node_modules/typeorm/error/noconnectionforrepositoryerror.d.ts","./node_modules/typeorm/error/circularrelationserror.d.ts","./node_modules/typeorm/error/returningstatementnotsupportederror.d.ts","./node_modules/typeorm/error/usingjointableisnotallowederror.d.ts","./node_modules/typeorm/error/missingjoincolumnerror.d.ts","./node_modules/typeorm/error/missingprimarycolumnerror.d.ts","./node_modules/typeorm/error/entitypropertynotfounderror.d.ts","./node_modules/typeorm/error/missingdrivererror.d.ts","./node_modules/typeorm/error/driverpackagenotinstallederror.d.ts","./node_modules/typeorm/error/cannotgetentitymanagernotconnectederror.d.ts","./node_modules/typeorm/error/connectionnotfounderror.d.ts","./node_modules/typeorm/error/noversionorupdatedatecolumnerror.d.ts","./node_modules/typeorm/error/insertvaluesmissingerror.d.ts","./node_modules/typeorm/error/optimisticlockcannotbeusederror.d.ts","./node_modules/typeorm/error/metadatawithsuchnamealreadyexistserror.d.ts","./node_modules/typeorm/error/driveroptionnotseterror.d.ts","./node_modules/typeorm/error/findrelationsnotfounderror.d.ts","./node_modules/typeorm/error/pessimisticlocktransactionrequirederror.d.ts","./node_modules/typeorm/error/repositorynottreeerror.d.ts","./node_modules/typeorm/error/datatypenotsupportederror.d.ts","./node_modules/typeorm/error/initializedrelationerror.d.ts","./node_modules/typeorm/error/missingjointableerror.d.ts","./node_modules/typeorm/error/queryfailederror.d.ts","./node_modules/typeorm/error/noneedtoreleaseentitymanagererror.d.ts","./node_modules/typeorm/error/usingjoincolumnonlyononesideallowederror.d.ts","./node_modules/typeorm/error/usingjointableonlyononesideallowederror.d.ts","./node_modules/typeorm/error/subjectremovedandupdatederror.d.ts","./node_modules/typeorm/error/persistedentitynotfounderror.d.ts","./node_modules/typeorm/error/usingjoincolumnisnotallowederror.d.ts","./node_modules/typeorm/error/columntypeundefinederror.d.ts","./node_modules/typeorm/error/queryrunneralreadyreleasederror.d.ts","./node_modules/typeorm/error/offsetwithoutlimitnotsupportederror.d.ts","./node_modules/typeorm/error/cannotexecutenotconnectederror.d.ts","./node_modules/typeorm/error/noconnectionoptionerror.d.ts","./node_modules/typeorm/error/forbiddentransactionmodeoverrideerror.d.ts","./node_modules/typeorm/error/index.d.ts","./node_modules/typeorm/decorator/options/columnwithlengthoptions.d.ts","./node_modules/typeorm/decorator/options/columnnumericoptions.d.ts","./node_modules/typeorm/decorator/options/columnenumoptions.d.ts","./node_modules/typeorm/decorator/options/columnembeddedoptions.d.ts","./node_modules/typeorm/decorator/options/columnhstoreoptions.d.ts","./node_modules/typeorm/decorator/options/columnwithwidthoptions.d.ts","./node_modules/typeorm/decorator/columns/column.d.ts","./node_modules/typeorm/decorator/columns/createdatecolumn.d.ts","./node_modules/typeorm/decorator/columns/deletedatecolumn.d.ts","./node_modules/typeorm/decorator/options/primarygeneratedcolumnnumericoptions.d.ts","./node_modules/typeorm/decorator/options/primarygeneratedcolumnuuidoptions.d.ts","./node_modules/typeorm/decorator/options/primarygeneratedcolumnidentityoptions.d.ts","./node_modules/typeorm/decorator/columns/primarygeneratedcolumn.d.ts","./node_modules/typeorm/decorator/columns/primarycolumn.d.ts","./node_modules/typeorm/decorator/columns/updatedatecolumn.d.ts","./node_modules/typeorm/decorator/columns/versioncolumn.d.ts","./node_modules/typeorm/decorator/options/virtualcolumnoptions.d.ts","./node_modules/typeorm/decorator/columns/virtualcolumn.d.ts","./node_modules/typeorm/decorator/options/viewcolumnoptions.d.ts","./node_modules/typeorm/decorator/columns/viewcolumn.d.ts","./node_modules/typeorm/decorator/columns/objectidcolumn.d.ts","./node_modules/typeorm/decorator/listeners/afterinsert.d.ts","./node_modules/typeorm/decorator/listeners/afterload.d.ts","./node_modules/typeorm/decorator/listeners/afterremove.d.ts","./node_modules/typeorm/decorator/listeners/aftersoftremove.d.ts","./node_modules/typeorm/decorator/listeners/afterrecover.d.ts","./node_modules/typeorm/decorator/listeners/afterupdate.d.ts","./node_modules/typeorm/decorator/listeners/beforeinsert.d.ts","./node_modules/typeorm/decorator/listeners/beforeremove.d.ts","./node_modules/typeorm/decorator/listeners/beforesoftremove.d.ts","./node_modules/typeorm/decorator/listeners/beforerecover.d.ts","./node_modules/typeorm/decorator/listeners/beforeupdate.d.ts","./node_modules/typeorm/decorator/listeners/eventsubscriber.d.ts","./node_modules/typeorm/decorator/options/indexoptions.d.ts","./node_modules/typeorm/decorator/options/entityoptions.d.ts","./node_modules/typeorm/decorator/relations/joincolumn.d.ts","./node_modules/typeorm/decorator/relations/jointable.d.ts","./node_modules/typeorm/decorator/relations/manytomany.d.ts","./node_modules/typeorm/decorator/relations/manytoone.d.ts","./node_modules/typeorm/decorator/relations/onetomany.d.ts","./node_modules/typeorm/decorator/relations/onetoone.d.ts","./node_modules/typeorm/decorator/relations/relationcount.d.ts","./node_modules/typeorm/decorator/relations/relationid.d.ts","./node_modules/typeorm/decorator/entity/entity.d.ts","./node_modules/typeorm/decorator/entity/childentity.d.ts","./node_modules/typeorm/decorator/entity/tableinheritance.d.ts","./node_modules/typeorm/decorator/options/viewentityoptions.d.ts","./node_modules/typeorm/decorator/entity-view/viewentity.d.ts","./node_modules/typeorm/decorator/tree/treelevelcolumn.d.ts","./node_modules/typeorm/decorator/tree/treeparent.d.ts","./node_modules/typeorm/decorator/tree/treechildren.d.ts","./node_modules/typeorm/decorator/tree/tree.d.ts","./node_modules/typeorm/decorator/index.d.ts","./node_modules/typeorm/decorator/foreignkey.d.ts","./node_modules/typeorm/decorator/options/uniqueoptions.d.ts","./node_modules/typeorm/decorator/unique.d.ts","./node_modules/typeorm/decorator/check.d.ts","./node_modules/typeorm/decorator/exclusion.d.ts","./node_modules/typeorm/decorator/generated.d.ts","./node_modules/typeorm/decorator/entityrepository.d.ts","./node_modules/typeorm/find-options/operator/and.d.ts","./node_modules/typeorm/find-options/operator/or.d.ts","./node_modules/typeorm/find-options/operator/any.d.ts","./node_modules/typeorm/find-options/operator/arraycontainedby.d.ts","./node_modules/typeorm/find-options/operator/arraycontains.d.ts","./node_modules/typeorm/find-options/operator/arrayoverlap.d.ts","./node_modules/typeorm/find-options/operator/between.d.ts","./node_modules/typeorm/find-options/operator/equal.d.ts","./node_modules/typeorm/find-options/operator/in.d.ts","./node_modules/typeorm/find-options/operator/isnull.d.ts","./node_modules/typeorm/find-options/operator/lessthan.d.ts","./node_modules/typeorm/find-options/operator/lessthanorequal.d.ts","./node_modules/typeorm/find-options/operator/ilike.d.ts","./node_modules/typeorm/find-options/operator/like.d.ts","./node_modules/typeorm/find-options/operator/morethan.d.ts","./node_modules/typeorm/find-options/operator/morethanorequal.d.ts","./node_modules/typeorm/find-options/operator/not.d.ts","./node_modules/typeorm/find-options/operator/raw.d.ts","./node_modules/typeorm/find-options/operator/jsoncontains.d.ts","./node_modules/typeorm/find-options/findoptionsutils.d.ts","./node_modules/typeorm/logger/abstractlogger.d.ts","./node_modules/typeorm/logger/advancedconsolelogger.d.ts","./node_modules/typeorm/logger/formattedconsolelogger.d.ts","./node_modules/typeorm/logger/simpleconsolelogger.d.ts","./node_modules/typeorm/logger/filelogger.d.ts","./node_modules/typeorm/repository/abstractrepository.d.ts","./node_modules/typeorm/data-source/index.d.ts","./node_modules/typeorm/repository/baseentity.d.ts","./node_modules/typeorm/driver/sqlserver/mssqlparameter.d.ts","./node_modules/typeorm/connection/connectionoptionsreader.d.ts","./node_modules/typeorm/connection/connectionoptions.d.ts","./node_modules/typeorm/connection/connection.d.ts","./node_modules/typeorm/migration/migrationexecutor.d.ts","./node_modules/typeorm/naming-strategy/defaultnamingstrategy.d.ts","./node_modules/typeorm/naming-strategy/legacyoraclenamingstrategy.d.ts","./node_modules/typeorm/entity-schema/entityschemaembeddedcolumnoptions.d.ts","./node_modules/typeorm/schema-builder/rdbmsschemabuilder.d.ts","./node_modules/typeorm/util/instancechecker.d.ts","./node_modules/typeorm/repository/findtreesoptions.d.ts","./node_modules/typeorm/util/treerepositoryutils.d.ts","./node_modules/typeorm/index.d.ts","./node_modules/ulidx/dist/types.d.ts","./node_modules/ulidx/dist/ulid.d.ts","./node_modules/ulidx/dist/constants.d.ts","./node_modules/ulidx/dist/index.d.ts","./node_modules/zod/dist/types/v3/helpers/typealiases.d.ts","./node_modules/zod/dist/types/v3/helpers/util.d.ts","./node_modules/zod/dist/types/v3/zoderror.d.ts","./node_modules/zod/dist/types/v3/locales/en.d.ts","./node_modules/zod/dist/types/v3/errors.d.ts","./node_modules/zod/dist/types/v3/helpers/parseutil.d.ts","./node_modules/zod/dist/types/v3/helpers/enumutil.d.ts","./node_modules/zod/dist/types/v3/helpers/errorutil.d.ts","./node_modules/zod/dist/types/v3/helpers/partialutil.d.ts","./node_modules/zod/dist/types/v3/standard-schema.d.ts","./node_modules/zod/dist/types/v3/types.d.ts","./node_modules/zod/dist/types/v3/external.d.ts","./node_modules/zod/dist/types/v3/index.d.ts","./node_modules/zod/dist/types/index.d.ts","./src/config/index.ts","./node_modules/@types/triple-beam/index.d.ts","./node_modules/logform/index.d.ts","./node_modules/winston-transport/index.d.ts","./node_modules/winston/lib/winston/config/index.d.ts","./node_modules/winston/lib/winston/transports/index.d.ts","./node_modules/winston/index.d.ts","./src/utils/logger.ts","./src/components/v1/platform/platformentities/platform.entity.ts","./src/components/v1/platform/platformentities/permission.entity.ts","./src/types/enums.ts","./src/components/v1/distribution/distribution.entity.ts","./src/components/v1/feeconfig/feeconfig.entity.ts","./src/components/v1/user/user.entity.ts","./src/config/persistence/data-source.ts","./src/utils/index.ts","./src/components/v1/wallet/wallet.entity.ts","./src/types/global.ts","./src/appmiddlewares/fingerprint.middleware.ts","./src/appmiddlewares/index.ts","./src/utils/enhancedrouter.ts","./src/appmiddlewares/policy.middleware.ts","./src/utils/helper.ts","./src/types/general-policy.ts","./src/utils/responsemessages.ts","./src/utils/errorhandler.ts","./src/components/v1/platform/platformservices/platform.services.ts","./src/components/v1/platform/platform.utils.ts","./src/components/v1/platform/platform.validations.ts","./src/components/v1/platform/platformservices/role.services.ts","./src/components/v1/platform/platformservices/permission.services.ts","./src/components/v1/platform/platformcontrollers/permission.controller.ts","./src/components/v1/platform/platform.routes.ts","./src/components/v1/wallet/wallet.services.ts","./src/components/v1/wallet/wallet.controller.ts","./src/components/v1/wallet/wallet.routes.ts","./src/components/v1/routes.v1.ts","./src/config/persistence/seeder.ts","./src/index.ts","./src/components/v1/wallet/wallet.utils.ts","./src/components/v1/wallet/wallet.validations.ts","./node_modules/@types/bcryptjs/index.d.ts","./node_modules/@types/estree/index.d.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@types/ms/index.d.ts","./node_modules/@types/jsonwebtoken/index.d.ts","../../../node_modules/@types/webidl-conversions/index.d.ts","../../../node_modules/@types/whatwg-url/index.d.ts"],"fileIdsList":[[64,107],[64,107,122,157,165],[64,107,122,157],[64,107,119,122,157,159,160,161],[64,107,160,162,164,166],[64,107,112,157,620],[64,104,107],[64,106,107],[107],[64,107,112,142],[64,107,108,113,119,120,127,139,150],[64,107,108,109,119,127],[59,60,61,64,107],[64,107,110,151],[64,107,111,112,120,128],[64,107,112,139,147],[64,107,113,115,119,127],[64,106,107,114],[64,107,115,116],[64,107,117,119],[64,106,107,119],[64,107,119,120,121,139,150],[64,107,119,120,121,134,139,142],[64,102,107],[64,102,107,115,119,122,127,139,150],[64,107,119,120,122,123,127,139,147,150],[64,107,122,124,139,147,150],[62,63,64,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156],[64,107,119,125],[64,107,126,150],[64,107,115,119,127,139],[64,107,128],[64,107,129],[64,106,107,130],[64,104,105,106,107,108,109,110,111,112,113,114,115,116,117,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156],[64,107,132],[64,107,133],[64,107,119,134,135],[64,107,134,136,151,153],[64,107,119,139,140,142],[64,107,141,142],[64,107,139,140],[64,107,142],[64,107,143],[64,104,107,139],[64,107,119,145,146],[64,107,145,146],[64,107,112,127,139,147],[64,107,148],[64,107,127,149],[64,107,122,133,150],[64,107,112,151],[64,107,139,152],[64,107,126,153],[64,107,154],[64,107,119,121,130,139,142,150,153,155],[64,107,139,156],[64,107,120,139,157,158],[64,107,122,157,159,163],[64,107,167],[64,107,122],[64,107,577],[64,107,236,358],[64,107,178,557],[64,107,239],[64,107,346],[64,107,342,346],[64,107,342],[64,107,193,232,233,234,235,237,238,346],[64,107,178,179,188,193,233,237,240,244,276,292,293,295,297,303,304,305,306,342,343,344,345,351,358,375],[64,107,308,310,312,313,323,325,326,327,328,329,330,331,333,335,336,337,338,341],[64,107,182,184,185,215,457,458,459,460,461,462],[64,107,185],[64,107,182,185],[64,107,466,467,468],[64,107,475],[64,107,182,473],[64,107,503],[64,107,491],[64,107,232],[64,107,178,216],[64,107,490],[64,107,183],[64,107,182,183,184],[64,107,223],[64,107,173,174,175],[64,107,219],[64,107,182],[64,107,214],[64,107,173],[64,107,557],[64,107,182,183],[64,107,220,221],[64,107,176,178],[64,107,375],[64,107,348,349],[64,107,174],[64,107,511],[64,107,239,332],[64,107,147],[64,107,239,240,307],[64,107,174,175,182,188,190,192,206,207,208,211,212,239,240,242,243,351,357,358],[64,107,239,250],[64,107,190,192,210,240,242,249,250,264,277,281,285,292,346,355,357,358],[64,107,115,127,147,248,249],[64,107,239,240,309],[64,107,239,324],[64,107,239,240,311],[64,107,239,334],[64,107,240,339,340],[64,107,209],[64,107,314,315,316,317,318,319,320,321],[64,107,239,240,322],[64,107,178,179,188,250,252,256,257,258,259,260,287,289,290,291,293,295,296,297,301,302,304,346,358,375],[64,107,179,188,206,250,253,257,261,262,286,287,289,290,291,303,346,351],[64,107,303,346,358],[64,107,231],[64,107,179,216],[64,107,182,183,215,217],[64,107,213,218,222,223,224,225,226,227,228,229,230,557],[64,107,172,173,174,175,179,219,220,221],[64,107,393],[64,107,351,393],[64,107,182,206,235,393],[64,107,179,393],[64,107,306,393],[64,107,393,394,395,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455],[64,107,195,393],[64,107,195,351,393],[64,107,393,397],[64,107,244,393],[64,107,247],[64,107,256],[64,107,245,252,253,254,255],[64,107,183,188,246],[64,107,250],[64,107,188,256,257,294,351,375],[64,107,247,250,251],[64,107,261],[64,107,188,256],[64,107,247,251],[64,107,188,247],[64,107,178,179,188,292,293,295,303,304,342,343,346,375,388,389],[58,64,107,176,178,179,182,183,185,188,189,190,191,192,193,213,214,218,219,221,222,223,231,232,233,234,235,238,240,241,242,244,245,246,247,250,251,252,253,254,255,256,257,258,259,260,263,264,266,267,268,269,270,271,272,273,274,275,276,278,281,282,285,288,289,290,291,292,293,294,295,298,299,303,304,305,306,342,346,351,354,355,356,357,358,368,369,371,372,373,374,375,389,390,391,392,456,463,464,465,469,470,471,472,474,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,504,505,506,507,508,509,510,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,544,545,546,547,548,549,550,551,552,554,556],[64,107,233,234,358],[64,107,233,358,537],[64,107,233,234,358,537],[64,107,358],[64,107,233],[64,107,185,186],[64,107,200],[64,107,179],[64,107,173,174,175,177,180],[64,107,378],[64,107,181,187,196,197,201,203,279,283,347,350,352,376,377,378,379,380,381,382,383,384,385,386,387],[64,107,172,176,177,180],[64,107,223,224,557],[64,107,193,279,351],[64,107,182,183,187,188,195,205,346,351],[64,107,195,196,198,199,202,204,206,346,351,353],[64,107,188,200,201,205,351],[64,107,188,194,195,198,199,202,204,205,206,223,224,280,284,346,347,348,349,350,353,557],[64,107,193,283,351],[64,107,173,174,175,193,206,351],[64,107,193,205,206,351,352],[64,107,195,351,375,376],[64,107,188,195,197,351,375],[64,107,172,173,174,175,177,181,188,194,205,206,351],[64,107,206],[64,107,173,193,203,205,206,351],[64,107,305],[64,107,306,346,358],[64,107,193,357],[64,107,193,550],[64,107,192,357],[64,107,188,195,206,351,396],[64,107,195,206,397],[64,107,119,120,139,235],[64,107,351],[64,107,298],[64,107,179,188,291,298,299,346,358,374],[64,107,188,243,299],[64,107,179,188,206,287,289,300,374],[64,107,195,346,351,360,367],[64,107,299],[64,107,179,188,206,244,287,299,346,351,358,359,360,366,367,368,369,370,371,372,373,375],[64,107,188,195,206,223,243,346,351,359,360,361,362,363,364,365,366,374],[64,107,188],[64,107,195,351,367,375],[64,107,188,195,346,358,375],[64,107,188,374],[64,107,288],[64,107,188,288],[64,107,179,188,195,223,249,252,253,254,255,257,298,299,351,358,364,365,367,374],[64,107,179,188,223,290,298,299,346,358,374],[64,107,188,351],[64,107,188,223,287,290,298,299,346,358,374],[64,107,188,299],[64,107,188,190,192,210,240,242,249,264,277,281,285,288,297,303,346,355,357],[64,107,178,188,295,303,304,375],[64,107,179,250,252,256,257,258,259,260,287,289,290,291,301,302,304,375,543],[64,107,188,250,256,257,261,262,292,304,358,375],[64,107,179,188,250,252,256,257,258,259,260,287,289,290,291,301,302,303,358,375,557],[64,107,188,294,304,375],[64,107,243,300],[64,107,189,241,263,278,282,354],[64,107,189,206,210,211,346,351,358],[64,107,210],[64,107,190,242,244,264,281,285,351,355,356],[64,107,278,280],[64,107,189],[64,107,282,284],[64,107,194,241,244],[64,107,353,354],[64,107,204,263],[64,107,191,557],[64,107,188,195,206,265,276,351,358],[64,107,266,267,268,269,270,271,272,273,274,275],[64,107,188,303,346,351,358],[64,107,303,346,351,358],[64,107,270],[64,107,188,195,206,303,346,351,358],[64,107,190,192,206,209,232,242,247,251,264,281,285,292,299,343,351,355,357,368,369,370,371,372,373,375,397,543,544,545,553],[64,107,303,351,555],[64,107,558,559,560],[64,107,558],[64,74,78,107,150],[64,74,107,139,150],[64,69,107],[64,71,74,107,147,150],[64,107,127,147],[64,107,157],[64,69,107,157],[64,71,74,107,127,150],[64,66,67,70,73,107,119,139,150],[64,74,81,107],[64,66,72,107],[64,74,95,96,107],[64,70,74,107,142,150,157],[64,95,107,157],[64,68,69,107,157],[64,74,107],[64,68,69,70,71,72,73,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,96,97,98,99,100,101,107],[64,74,89,107],[64,74,81,82,107],[64,72,74,82,83,107],[64,73,107],[64,66,69,74,107],[64,74,78,82,83,107],[64,78,107],[64,72,74,77,107,150],[64,66,71,74,81,107],[64,107,139],[64,69,74,95,107,155,157],[64,107,139,157,578],[64,107,139,157,578,579,580,581],[64,107,122,157,579],[64,107,574],[64,107,564,565],[64,107,562,563,564,566,567,572],[64,107,563,564],[64,107,572],[64,107,573],[64,107,564],[64,107,562,563,564,567,568,569,570,571],[64,107,562,563,574],[64,107,112,167,591,593],[64,107,167,575,593],[64,107,557,586,591],[64,107,596,597,604,607],[64,107,167,585,593,601,602],[64,107,171,575,599,603],[64,107,167,171,557,575,585,590,591,593,598,600,601,603,604,605,606],[64,107,171,557],[64,107,171,557,576,593],[64,107,557,585,590],[64,107,557,584,590],[64,107,596,608,611],[64,107,557,591],[64,107,167,593,598,609],[64,107,596,610],[64,107,557,590,592],[64,107,131],[58,64,107,557,576,583,584,585,587,588,589,592],[64,107,171,576,583,584,585,589,591,601,602,603],[58,64,107,167,168,169,170,576,583,590,594,595,601,612,613],[64,107,575,598],[64,107,167,171,584,585,592],[64,107,167,576,583,593,600],[64,107,167,575,599],[64,107,112,561,575,590,593],[64,107,576,582]],"fileInfos":[{"version":"69684132aeb9b5642cbcd9e22dff7818ff0ee1aa831728af0ecf97d3364d5546","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"936e80ad36a2ee83fc3caf008e7c4c5afe45b3cf3d5c24408f039c1d47bdc1df","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"fef8cfad2e2dc5f5b3d97a6f4f2e92848eb1b88e897bb7318cef0e2820bceaab","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"b5ce7a470bc3628408429040c4e3a53a27755022a32fd05e2cb694e7015386c7","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d6d51a5118d000ed3bfe6e1dd1335bebfff3fef23cd2af2f84a24d30f90cc90","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"a79e62f1e20467e11a904399b8b18b18c0c6eea6b50c1168bf215356d5bebfaf","affectsGlobalScope":true,"impliedFormat":1},{"version":"d802f0e6b5188646d307f070d83512e8eb94651858de8a82d1e47f60fb6da4e2","affectsGlobalScope":true,"impliedFormat":1},{"version":"4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"a12d953aa755b14ac1d28ecdc1e184f3285b01d6d1e58abc11bf1826bc9d80e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"a38efe83ff77c34e0f418a806a01ca3910c02ee7d64212a59d59bca6c2c38fa1","impliedFormat":1},{"version":"7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","impliedFormat":1},{"version":"3fe4022ba1e738034e38ad9afacbf0f1f16b458ed516326f5bf9e4a31e9be1dc","impliedFormat":1},{"version":"a957197054b074bcdf5555d26286e8461680c7c878040d0f4e2d5509a7524944","affectsGlobalScope":true,"impliedFormat":1},{"version":"4314c7a11517e221f7296b46547dbc4df047115b182f544d072bdccffa57fc72","impliedFormat":1},{"version":"e9b97d69510658d2f4199b7d384326b7c4053b9e6645f5c19e1c2a54ede427fc","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"f478f6f5902dc144c0d6d7bdc919c5177cac4d17a8ca8653c2daf6d7dc94317f","affectsGlobalScope":true,"impliedFormat":1},{"version":"19d5f8d3930e9f99aa2c36258bf95abbe5adf7e889e6181872d1cdba7c9a7dd5","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"0a6b3ad6e19dd0fe347a54cbfd8c8bd5091951a2f97b2f17e0af011bfde05482","impliedFormat":1},{"version":"0a37a4672f163d7fe46a414923d0ef1b0526dcd2d2d3d01c65afe6da03bf2495","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"71450bbc2d82821d24ca05699a533e72758964e9852062c53b30f31c36978ab8","affectsGlobalScope":true,"impliedFormat":1},{"version":"88bc59b32d0d5b4e5d9632ac38edea23454057e643684c3c0b94511296f2998c","affectsGlobalScope":true,"impliedFormat":1},{"version":"25d130083f833251b5b4c2794890831b1b8ce2ead24089f724181576cf9d7279","impliedFormat":1},{"version":"ffe66ee5c9c47017aca2136e95d51235c10e6790753215608bff1e712ff54ec6","impliedFormat":1},{"version":"206a70e72af3e24688397b81304358526ce70d020e4c2606c4acfd1fa1e81fb2","impliedFormat":1},{"version":"017caf5d2a8ef581cf94f678af6ce7415e06956317946315560f1487b9a56167","impliedFormat":1},{"version":"528b62e4272e3ddfb50e8eed9e359dedea0a4d171c3eb8f337f4892aac37b24b","impliedFormat":1},{"version":"d71535813e39c23baa113bc4a29a0e187b87d1105ccc8c5a6ebaca38d9a9bff2","impliedFormat":1},{"version":"aa9e37a18f4a50ea4bb5f118d03d144cc779b778e0e3fe60ee80c3add19e613b","affectsGlobalScope":true,"impliedFormat":1},{"version":"f72bc8fe16da67e4e3268599295797b202b95e54bd215a03f97e925dd1502a36","impliedFormat":1},{"version":"b1b6ee0d012aeebe11d776a155d8979730440082797695fc8e2a5c326285678f","impliedFormat":1},{"version":"45875bcae57270aeb3ebc73a5e3fb4c7b9d91d6b045f107c1d8513c28ece71c0","impliedFormat":1},{"version":"915e18c559321c0afaa8d34674d3eb77e1ded12c3e85bf2a9891ec48b07a1ca5","affectsGlobalScope":true,"impliedFormat":1},{"version":"636302a00dfd1f9fe6e8e91e4e9350c6518dcc8d51a474e4fc3a9ba07135100b","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"8145e07aad6da5f23f2fcd8c8e4c5c13fb26ee986a79d03b0829b8fce152d8b2","impliedFormat":1},{"version":"e1120271ebbc9952fdc7b2dd3e145560e52e06956345e6fdf91d70ca4886464f","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"e1ce1d622f1e561f6cdf246372ead3bbc07ce0342024d0e9c7caf3136f712698","impliedFormat":1},{"version":"199c8269497136f3a0f4da1d1d90ab033f899f070e0dd801946f2a241c8abba2","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"125d792ec6c0c0f657d758055c494301cc5fdb327d9d9d5960b3f129aff76093","impliedFormat":1},{"version":"27e4532aaaa1665d0dd19023321e4dc12a35a741d6b8e1ca3517fcc2544e0efe","affectsGlobalScope":true,"impliedFormat":1},{"version":"2754d8221d77c7b382096651925eb476f1066b3348da4b73fe71ced7801edada","impliedFormat":1},{"version":"6266d94fb9165d42716e45f3a537ca9f59c07b1dfa8394a659acf139134807db","affectsGlobalScope":true,"impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"98ffdf93dfdd206516971d28e3e473f417a5cfd41172e46b4ce45008f640588e","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"7d6ff413e198d25639f9f01f16673e7df4e4bd2875a42455afd4ecc02ef156da","affectsGlobalScope":true,"impliedFormat":1},{"version":"a7692a54334fd08960cd0c610ff509c2caa93998e0dcefa54021489bcc67c22d","affectsGlobalScope":true,"impliedFormat":1},{"version":"0f7e00beefa952297cde4638b2124d2d9a1eed401960db18edcadaa8500c78eb","impliedFormat":1},{"version":"3a051941721a7f905544732b0eb819c8d88333a96576b13af08b82c4f17581e4","impliedFormat":1},{"version":"ac5ed35e649cdd8143131964336ab9076937fa91802ec760b3ea63b59175c10a","impliedFormat":1},{"version":"1e25f8d0a8573cafd5b5a16af22d26ab872068a693b9dbccd3f72846ab373655","affectsGlobalScope":true,"impliedFormat":1},{"version":"3797dd6f4ea3dc15f356f8cdd3128bfa18122213b38a80d6c1f05d8e13cbdad8","impliedFormat":1},{"version":"ad90122e1cb599b3bc06a11710eb5489101be678f2920f2322b0ac3e195af78d","impliedFormat":1},{"version":"d3f2d715f57df3f04bf7b16dde01dec10366f64fce44503c92b8f78f614c1769","impliedFormat":1},{"version":"b78cd10245a90e27e62d0558564f5d9a16576294eee724a59ae21b91f9269e4a","impliedFormat":1},{"version":"baac9896d29bcc55391d769e408ff400d61273d832dd500f21de766205255acb","impliedFormat":1},{"version":"2f5747b1508ccf83fad0c251ba1e5da2f5a30b78b09ffa1cfaf633045160afed","impliedFormat":1},{"version":"a45c25e77c911c1f2a04cade78f6f42b4d7d896a3882d4e226efd3a3fcd5f2c4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b71c603a539078a5e3a039b20f2b0a0d1708967530cf97dec8850a9ca45baa2b","impliedFormat":1},{"version":"0e13570a7e86c6d83dd92e81758a930f63747483e2cd34ef36fcdb47d1f9726a","impliedFormat":1},{"version":"104c67f0da1bdf0d94865419247e20eded83ce7f9911a1aa75fc675c077ca66e","impliedFormat":1},{"version":"cc0d0b339f31ce0ab3b7a5b714d8e578ce698f1e13d7f8c60bfb766baeb1d35c","impliedFormat":1},{"version":"5c45abf1e13e4463eacfd5dedda06855da8748a6a6cb3334f582b52e219acc04","impliedFormat":1},{"version":"f06a1129cf403bdeb1d8bcdf3bfae3bea34a3a351261a3e0656180d2cd187bd0","impliedFormat":99},{"version":"cf473bbae6d7a09b45be12a2578e8de12bfaadf6ac947ac2224a378fe3ae6d9f","impliedFormat":1},{"version":"25be1eb939c9c63242c7a45446edb20c40541da967f43f1aa6a00ed53c0552db","impliedFormat":1},"acdfddb257c75cd6ebd85e13a934d76ebffa1091afcc7d155a2620fe68149214",{"version":"2e2bc02af7b535d267be8cecbc5831466dd71c5af294401821791b26cb363c47","impliedFormat":1},{"version":"986affe0f60331f20df7d708ee097056b0973d85422ec2ce754af19c1fa4e4b1","impliedFormat":1},{"version":"8f06c2807459f1958b297f4ad09c6612d7dbd7997c9ccfc6ea384f7538e0cea8","impliedFormat":1},{"version":"a7de30cd043d7299bfe9daaca3732b086e734341587c3e923b01f3fd74d31126","impliedFormat":1},{"version":"78f7fad319e4ac305ffe8e03027423279b53a8af4db305096aa75d446b1ec7af","impliedFormat":1},{"version":"3bf58923a1d27819745bdad52bca1bdced9fef12cc0c7f8a3fd5f4e0206b684a","impliedFormat":1},{"version":"8fc11f102df58f03d36fcbf0da3efa37c177f5f18f534c76179ceef0c3a672cd","impliedFormat":1},{"version":"e6935ab0f64a886e778c12a54ed6e9075ce7e7f44723ff0d52020a654b025a09","impliedFormat":1},{"version":"9829af7653a29f1b85d3dd688a6c6256087c0b737b85d84b630e7f93fd420faf","impliedFormat":1},{"version":"3d9d985d41e536fcf79fc95082925c2f1ae5ade75814ad2bd70c0944747f7ac4","impliedFormat":1},{"version":"3fadad55baa2e46f03a61a6f72de5b1f6c9991ce86096c72a555c0b75397ee82","impliedFormat":1},{"version":"b0e6f1b1569779cf567317c2265d67460d1d3b4de4e79126533109d87dc16d50","impliedFormat":1},{"version":"18cb8be1326ffa4158abd8d84c9b0a189c0f52201f12f7af2d2af830c077f2bf","impliedFormat":1},{"version":"b08fc2b6ccd4d3db42af01b3c6390fc1e30dc1d95496d9a8ee5f9319c2e4883f","impliedFormat":1},{"version":"0de68916e23c1e3df800f9f61cdd7c506ceb0656fcbc245ee9974aad26786781","impliedFormat":1},{"version":"80c538ee6a62249e77ba3de07efb23d4a7ca8946499c065261bf5079f1cd3cf0","impliedFormat":1},{"version":"ad4277862bdcbe1cf5c1e0d43b39770e1ccc033da92f5b9ff75ca8c3a03a569b","impliedFormat":1},{"version":"46a86c47400a564df04a1604fcac41cb599ebbada392527a1462c9dfe4713d78","impliedFormat":1},{"version":"f342dcb96ad26855757929a9f6632704b7013f65786573d4fdcd4da09f475923","impliedFormat":1},{"version":"dcd467dc444953a537502d9e140d4f2dc13010664d4216cc8e6977b3c5c3efa3","impliedFormat":1},{"version":"ca476924dfa6120b807a14e0a8aea7b061b8bdaa7eecdb303d7957c769102e96","impliedFormat":1},{"version":"848fe622fac070f8af9255e5d63fe829e3da079cae30be48fb6deb5dbf2c27c6","impliedFormat":1},{"version":"f3bb275073b5db8931c042d347fdce888775436a4774836221af57fdccec32ff","impliedFormat":1},{"version":"03cb8cb2f8ef002a5cac9b8c9a0c02e5fd09de128b9769c5b920a6cbfc080087","impliedFormat":1},{"version":"3e5ebc3a6a938a03a361f4cdb9a26c9f5a1bac82b46273e11d5d37cd8eccc918","impliedFormat":1},{"version":"a0a7800e71c504c21f3051a29f0f6f948f0b8296c9ebffeb67033822aabf92e0","impliedFormat":1},{"version":"6a219f12b3e853398d51192736707e320699a355052687bad4729784649ff519","impliedFormat":1},{"version":"4294a84634c56529e67301a3258448019e41c101de6b9646ea41c0ecdc70df92","impliedFormat":1},{"version":"80fc027e10234b809a9a40086114a8154657dcb8478d58c85ef850592d352870","impliedFormat":1},{"version":"27f24ba43083d406b372e9eff72dbc378afa0503dac1c1dd32499cc92fc9cb22","impliedFormat":1},{"version":"12594611a054ca7fe69962f690a4e79922d563b4b434716eb855d63a9d11a78f","impliedFormat":1},{"version":"1440eca2d8bc47ebdbc5a901b369de1b7b39c3297e5b4ac9631899f49ea9740b","impliedFormat":1},{"version":"fc9897fbada879bda954603ea204c6e5df913262a90ad848b5efaab182b58033","impliedFormat":1},{"version":"93443b2da120bea58eb48bd7da86559d4cf868dc2d581eebf9b48b51ba1e8894","impliedFormat":1},{"version":"04eb09529c51d058d0cc686cf0b0e4927068f84904ea2b844038e4f863dd4291","impliedFormat":1},{"version":"c2956026078814be6dc01515213aeb1eb816e81715085952bbc97b7c81fe3f6d","impliedFormat":1},{"version":"ac3a69c529ab256532825b08902aec65d0d88c66963e39ae19a3d214953aedc5","impliedFormat":1},{"version":"fe29108f3ddf7030c3d573c5226ebe03213170b3beca5200ca7cb33755184017","impliedFormat":1},{"version":"04d5bfb0a0eecd66c0b3f522477bf69065a9703be8300fbea5566a0fc4a97b9d","impliedFormat":1},{"version":"d5e3e13faca961679bed01d80bc38b3336e7de598ebf9b03ec7d31081af735ad","impliedFormat":1},{"version":"de05a488fb501de32c1ec0af2a6ddfe0fdef46935b9f4ffb3922d355b15da674","impliedFormat":1},{"version":"9f00f2bc49f0c10275a52cb4f9e2991860d8b7b0922bfab6eafe14178377aa72","impliedFormat":1},{"version":"7bd94408358caf1794ad24546ca0aa56f9be6be2d3245d0972fcb924b84a81fd","impliedFormat":1},{"version":"0e7c3660d1df392b6f6ae7fa697f0629ae4404e5b7bac05dd81136247aff32d5","impliedFormat":1},{"version":"b0b3636502dc0c50295f67747968f202f7b775eac5016329606d1bc2888d5dd9","impliedFormat":1},{"version":"f9ede7ea553dc197fd5d2604f62cda1be1aea50024ed73237d9e3144f0c93608","impliedFormat":1},{"version":"a449c582300e77b4b1b0ae262784bf12d0037756d5059db18881f251b205d480","impliedFormat":1},{"version":"c6688fd4c2a8a24c9b80da3660a7a06b93ed37d12d84f3ba4aa071ffc125e75f","impliedFormat":1},{"version":"20efc25890a0b2f09e4d224afaaf84917baa77b1aee60d9dfd11ff8078d73f93","impliedFormat":1},{"version":"d00b48096854d711cee688e7ff1ca796c1bf0d27ca509633c2a98b85cc23d47d","impliedFormat":1},{"version":"30f116226d0e53c6cbbdbc967479d5c8036935f771b2af51987c2e8d4cc7fc6a","impliedFormat":1},{"version":"8be98ffc3c54fb40b220796b796388f8ade50c8ba813a811bffccf98006566d5","impliedFormat":1},{"version":"4e82eed3c1b5084132708ce030f8ec90b69e4b7bb844dcaacd808045ae24c0e2","impliedFormat":1},{"version":"eae8c7cbcb175b997ce8e76cd6e770eca5dba07228f6cb4a44e1b0a11eb87685","impliedFormat":1},{"version":"b3ded8e50b3cdf548d7c8d3b3b5b2105932b04a2f08b392564f4bc499407e4e5","impliedFormat":1},{"version":"4ed2d8fb4c598719985b8fbef65f7de9c3f5ae6a233fc0fe20bd00193c490908","impliedFormat":1},{"version":"6da51da9b74383988b89e17298ceca510357f63830f78b40f72afe4d5a9cee3e","impliedFormat":1},{"version":"512a079a1a3de2492c80aa599e173b2ea8cc6afb2800e3e99f14330b34155fe1","impliedFormat":1},{"version":"f281f20b801830f2f94b2bc0b18aba01d4fb50c2f4a847ffcadff39de31c8b80","impliedFormat":1},{"version":"7ec2518429f33f4722c88cc7328fa98219d7df9990ee1fc11600122a927d39e3","impliedFormat":1},{"version":"8e3842ba15690ab4b340893a4552a8c3670b8f347fbb835afe14be98891eef10","impliedFormat":1},{"version":"e7b9673dcd3d1825dbd70ad1d1f848d68189afc302ecdafc6eb30cbe7bd420b5","impliedFormat":1},{"version":"15911b87a2ad4b65b30c445802d55fa6186c66068603113042e8c3dfa4a35e2a","impliedFormat":1},{"version":"a9dc7b8d06b1f69d219f61fa3f7ac621e6e3a8d5a430e800cd7d1a755cc058c3","impliedFormat":1},{"version":"f8c496656cb5fd737931b4d6c60bd72a97c48f37c07dcb74a593dd24ac3f684a","impliedFormat":1},{"version":"f2cf1d33c458ac091983e5dac1613f264d48a69b281e43c5b055321320082358","impliedFormat":1},{"version":"0fa43815d4b05eafe97c056dae73c313f23a9f00b559f1e942d042c7a04db93c","impliedFormat":1},{"version":"6b9eb11700f5e66dae6141f7d8ea595d2cdb2572cb7c0d732ea180b824a215da","impliedFormat":1},{"version":"a02db6aabaa291a85cf52b0c3f02a75301b80be856db63d44af4feea2179f37b","impliedFormat":1},{"version":"e1e94e41f47a4496566a9f40e815687a2eca1e7b7910b67704813cf61248b869","impliedFormat":1},{"version":"557ba6713b2a6fefd943399d5fb6c64e315dc461e9e05eaa6300fdbeeda5d0a1","impliedFormat":1},{"version":"1f7eeb69504ad94d16f4731f707d2af879adc7487dc35b146e2d86825bb779b4","impliedFormat":1},{"version":"c1b5c480e4d38377c82f9f517c12014d3d4475c0e607c4845e0836e0e89bbf7d","impliedFormat":1},{"version":"1a014a8365354f37ea245349a4361d3b46589be7921fe7f1dbf408cc0f084bab","impliedFormat":1},{"version":"87fc4a324b9fa5c9b93a13b5ae1b55ea390929ec1b0450afebff9620921a9cc1","impliedFormat":1},{"version":"73c0b8df0e282e26a53820f53502847a043bd77a9cda78782207d5349842fba2","impliedFormat":1},{"version":"5bae6e8aeb6486bc8503767978e4960e25ce1ea16b7e89c1ea4eed1c3ab62788","impliedFormat":1},{"version":"67a2b1d1789a15eef7b12c95793662da1added6bc8e0a784463cc88a24648818","impliedFormat":1},{"version":"4fe5c47cde584a33872b90fb4ded7e136d246e3d1d11661229000475cde9ccff","impliedFormat":1},{"version":"d6db974317fd9ff66a923555464850dcf87976054a7adacf09d53323f64686d1","impliedFormat":1},{"version":"79f4812dffe8f933c12c341d68eee731cb6dd7f2a4bb20097c411560c97a6263","impliedFormat":1},{"version":"c446e8f3bd5b16e121252e05ba7696524ca95ec3f819c12fb8c37e7836744769","impliedFormat":1},{"version":"23386bb0bcb20fcb367149f22f5c6468b53f1987e86fd25de875ffb769e4d241","impliedFormat":1},{"version":"3913806467307a4bd874b105ac3e79ac261ab986fbdce7f0feea26cbcee95765","impliedFormat":1},{"version":"a9417a980a4300048d179d0295e5b7dd76e4db7b566344779ee576cbd084b3c4","impliedFormat":1},{"version":"b96760c030c41fa078b35ea05fc3e7e4d2a81710a8329271d42b6abc110d5dbe","impliedFormat":1},{"version":"ef8ff23609cec5eb95e2beb98132ad90c0c5075415b50228b12f89ffaf981a4a","impliedFormat":1},{"version":"1154ed167b954ffb24a95ec3b11b1519a597024e7fda1df63c144962bc523aaf","impliedFormat":1},{"version":"174a3381f98fc78c451528cb1aa1baaa37a51852ec6fa90d42efd876301537c1","impliedFormat":1},{"version":"2c0de27d99a9331cfac8bc5c6bbd174e0593628bf3df268faa6c4188962a9549","impliedFormat":1},{"version":"1a17bcbc124a098987f7b1adbbcd412f8372ecb37e352b1c50165dac439eee5e","impliedFormat":1},{"version":"0ef49170735d9e5902f55b72465accadd0db93cae52544e3c469cbc8fbdbf654","impliedFormat":1},{"version":"f68a30e88dfa7d12d8dd4609bc9d5226a31d260bf3526de5554feed3f0bf0cb6","impliedFormat":1},{"version":"d8acc6f92c85e784acbbc72036156a4c1168a18cba5390c7d363040479c39396","impliedFormat":1},{"version":"1fffef141820a0556f60aa6050eccb17dbcdc29ecd8a17ee4366573fd9c96ce3","impliedFormat":1},{"version":"d2598c755c11170e3b5f85cd0c237033e783fd4896070c06c35b2246879612b8","impliedFormat":1},{"version":"8d2044a28963c6c85a2cf4e334eb49bb6f3dd0c0dfe316233148a9be74510a0e","impliedFormat":1},{"version":"2660eb7dba5976c2dcbea02ec146b1f27109e7bee323392db584f8c78a6477dd","impliedFormat":1},{"version":"54a4f21be5428d7bff9240efb4e8cae3cb771cad37f46911978e013ff7289238","impliedFormat":1},{"version":"10837df0382365c2544fb75cb9a8f6e481e68c64915362941b4ea4468fd0ef61","impliedFormat":1},{"version":"cc4483c79688bd3f69c11cb3299a07d5dcf87646c35b869c77cde553c42893cf","impliedFormat":1},{"version":"faf76eeb5dd5d4d1e37c6eb875d114fa97297c2b50b10e25066fed09e325a77a","impliedFormat":1},{"version":"b741703daf465b44177ef31cc637bde5cd5345e6c048d5807108e6e868182b01","impliedFormat":1},{"version":"9c3e59360437a3e2a22f7f1032559a4c24aba697365b62fb4816b7c8c66035b8","impliedFormat":1},{"version":"393446ab3f0dd3449ad6fd4c8abd0c82b711c514b9e8dfbf75222bbc48eb0cb6","impliedFormat":1},{"version":"ea02a962453ec628e886a6c5d0fc03bf4da9dfa38e1f8d42e65e07b2651edd85","impliedFormat":1},{"version":"5eb09226bfa1928721a438e37c004647fc19d8d1f4817bddcc350e57fb32935f","impliedFormat":1},{"version":"5994ed389d7fc28c03dad647ecb62e5349160bde443b0c7a54e0e10d6368bcbd","impliedFormat":1},{"version":"e1ff7df643e1aa1dbf1863113a913358844ed66f1af452e774834b0008e578b2","impliedFormat":1},{"version":"c5114285d0283d05e09cd959e605a4f76e5816c2fbe712241993fd66496083e5","impliedFormat":1},{"version":"2752e949c871f2cbd146efa21ebc34e4693c0ac8020401f90a45d4e150682181","impliedFormat":1},{"version":"c349cea980e28566998972522156daac849af8a9e4a9d59074845e319b975f5d","impliedFormat":1},{"version":"0370682454d1d243b75a7c7031bc8589531a472e927b67854c1b53b55ee496ea","impliedFormat":1},{"version":"cf6b4dbb5a1ac9ece24761c3a08682029851b292b67113a93b5e2bfd2e64e49d","impliedFormat":1},{"version":"c478eeebfab3c6b9886de171c82d46c999d06ab35e187119645f2df6a1e38577","impliedFormat":1},{"version":"cb2fea712720bb7951d7e5d63db8670bf4a400d3e0fb197bceb6ef44efe36ec3","impliedFormat":1},{"version":"1b4fcfc691980d63a730d47d5309d9f85cdddc18a4c83f6e3af20936d103e3ff","impliedFormat":1},{"version":"ef19d5fe42541f8b529bccd10f488d12caefa3b57a0deb1ed6143219cba716b4","impliedFormat":1},{"version":"84b5e6269d7cf53008a479eeb533ef09d025eafb4febe3729301b8d4daf37ff2","impliedFormat":1},{"version":"04196b5d9edd60b9648daa329c3355d7c95f33b7e520e7835eb21002174a8b8c","impliedFormat":1},{"version":"f9f6a3cd16546a9c55e6a1b225a85099a08bc402c6ce6b1aad1a317b49efef24","impliedFormat":1},{"version":"9e665aea79b702fd612ffb7ac741e4160d35d8d696a789129ebcbaea003beb3d","impliedFormat":1},{"version":"c8eeffebe6c2c6800f73aa59d1436d4dadbad7f3ddda02a831ffa66114c3122d","impliedFormat":1},{"version":"caf3f141f93cbf527ad18ecce326311d70342fe1e16ce93e5ce8d6bcdf02bd48","impliedFormat":1},{"version":"4283d88023e6e9645626475e392565464eae99068f17e324cfc40a27d10fe94f","impliedFormat":1},{"version":"51e3b73dea24e2a9638345fb7a2a7ef5d3aa2e7a285ad6bd446b45fab826def1","impliedFormat":1},{"version":"77c4c9f71f3736ed179043a72c4fad9832023855804fbe5261a956428b26a7a6","impliedFormat":1},{"version":"7232467057ec57666b884924f84fd21cd3a79cc826430c312e61a5bc5758f879","impliedFormat":1},{"version":"624f5dbfd76f2d77f20ace318e8cb918608a296106e55587fb443ef3030c595d","impliedFormat":1},{"version":"c78bb1275f640e4902ad5c3383ab4f54f73322a59c95924ab671125ba9546294","impliedFormat":1},{"version":"1cb0838371e8213ce116a1497bb86bcf01a11a755b77587980ee7cfb2d625ece","impliedFormat":1},{"version":"f5d29fd7099274774c203d94d8c0238770ab411b922b978be15a2c3ec8ab845c","impliedFormat":1},{"version":"6d99b5b226a65890ce27796e086d58c6351f601757c1e9f217a69e944d05e7e6","impliedFormat":1},{"version":"10b322f5bc001bec9bf08513c978c120adb0abe3c82793b11bdaf75873426c05","impliedFormat":1},{"version":"51b4efdc8dc92bc6ae2c44d4edad265decad70e8577d5653fc7f85200cbf6c6e","impliedFormat":1},{"version":"c3fa40ac56aa2598d9133c90b115eeb39bbad56c6dfca350dc8435b8b107fe26","impliedFormat":1},{"version":"cc542183b68b048a8cf64eb6231b3d0852f7f4d0191d4637c9d1d4c3f44b83b5","impliedFormat":1},{"version":"669acddcc842a2fcc012770ac377a38d353e041ff7ea926454d3c7559c1c4f83","impliedFormat":1},{"version":"c6fd975d319a70d6ba90bf38c34ac8efebe531214038fe561a27f89f2203f78e","impliedFormat":1},{"version":"a818204639081cf07d80885b88aff5120e5a4135211162f5e08cfc00ef3bf5b6","impliedFormat":1},{"version":"c194ca06da86829b836bb188dffc05543bbea3cbda797667c7a7cade2f907646","impliedFormat":1},{"version":"6df6afb0424a7c7581ee98a9333d30e893b943d0a4709b88f18c252ddc3101b4","impliedFormat":1},{"version":"59c2cbf84c22fae87f4f506f36a7258a72b931b602115067dfd6008ee526f8c0","impliedFormat":1},{"version":"1e09cd1bc6b6baa0733e1e799c4533105ea79cbb109937c71e8c870e14693216","impliedFormat":1},{"version":"0b60cfcd94fa9bd9fa58176650c7e4c72f99b9d30a50d0b55aa08b510276af96","impliedFormat":1},{"version":"ba25681012e5117866a2456dd3557e24aa5a946ed641126aa4469880db526883","impliedFormat":1},{"version":"2b1e058a8c3944890c7ce7c712ecfd0f2645420ee67537ac031d7afe6feda6e0","impliedFormat":1},{"version":"175dbcd1f226eebd93fd9628e9180fb537bb1171489b33db7b388ef0f4e73b37","impliedFormat":1},{"version":"69ec6331ee3a7cd6bade5d5f683f1705c1041ff77432aa18c50d2097e61f93db","impliedFormat":1},{"version":"06f34a0f2151b619314fc8a54e4352a40fd5606bda50623c326c3be365cc1ef9","impliedFormat":1},{"version":"6c6dcb49af3d72d823334f74a554b2f9917e3a59b3219934b7ae9e6b03a3e8b4","impliedFormat":1},{"version":"f094c7eb360c69adaf277ef5bc24d7ce7d6d7043f357a557ecd9b345532588d5","impliedFormat":1},{"version":"3d24aec533fe2f035b0675ba1c0e55e8680a714fff2a517e0fb388279476701c","impliedFormat":1},{"version":"224e2edff4c1e67d9c5179aa70e31d0dc7dd4ea5a9e80ffde121df9e5254eef2","impliedFormat":1},{"version":"acbad5d10b2edef7dbec73c0af84dd46206065346016287ffc4abfe9456b2250","impliedFormat":1},{"version":"70a3659d557bb683091f9d318762a330a3acb3954f5e89e5134d24c9272192f1","impliedFormat":1},{"version":"d9fe2c804f7db2f19e4323601278b748dc2984798f265c37cd37bb84e6c88ab8","impliedFormat":1},{"version":"3525647a73ae2124fa8f353f0a078b44ff1ee6f82958c2bb507de61575f12fff","impliedFormat":1},{"version":"d7238315cbd18ebeed93f41ad756a0ed9759824b9b158c3d7a1e0b71682d8966","impliedFormat":1},{"version":"eeba7376ce9721610d3282a4159f3c60154b7b3877fb251f7b3211b085cfdc18","impliedFormat":1},{"version":"643efb9d7747ee1dd50ff5bd4b7a87351157e55988c7d2f90ffbdf124f063931","impliedFormat":1},{"version":"788c870cac6b39980a5cc41bf610b1873952ecdd339b781f0687d42682ffc5dc","impliedFormat":1},{"version":"d51a2e050c8a131b13ec9330a0869e5ac75b9ac4ebde52d5f474e819510b5263","impliedFormat":1},{"version":"3544b854dccadff219b992b2e5dadfbd7a8e0b9815d6d56006775a17e6500568","impliedFormat":1},{"version":"6c034655fa83236bd779cacfc1d5b469d6e2150a1993e66ecca92376a8b2c6a7","impliedFormat":1},{"version":"6bd6933efe9d6263d9f1a534a28a8f88b1e4c331b95d85d39350cf02eca8dce0","impliedFormat":1},{"version":"658cf468a05b2b591fcd5455a76d9927face59ac4a21b4965982b3c234f5d289","impliedFormat":1},{"version":"6bf893d1b824bde22ee5880c0c760c1dd0a5163c38d22311441a3341b6965d2d","impliedFormat":1},{"version":"579d9d3c25058b854a6f7cc6368a473efcaa0740f45db13cb508761d35fc0156","impliedFormat":1},{"version":"2e0e76b30d5cff617354422d49f38205bd0eb5ca9ad6f4c1eebf34856e3886c7","impliedFormat":1},{"version":"28b415e70f9da0346545b7d2bcf361844a8e5778bd6b45bc1a2859f99700ff5b","impliedFormat":1},{"version":"a905f2f6785e3971bd97c42191394209d97f2aefb11841f7353dd9789821fa8c","impliedFormat":1},{"version":"e099c5ebddf80ae7285d380c7dd3b5d49c1347346ced51ae121b846833a8d102","impliedFormat":1},{"version":"aec91730b9f4d83758b4a45596317d34d6ecdbe9330a44629f53af47641b96ee","impliedFormat":1},{"version":"2321197343254570a8d4c868572059bfdfb683cf9d4099b6d4694250dac69471","impliedFormat":1},{"version":"18a3be03c31356b60ea1090bcc905d99e4983ca911cc70b34ad0b9b4d4e050c3","impliedFormat":1},{"version":"738ddac5ab5b61d70d3466f3906d6b3c83c8786e922c6e726a6597296181ae87","impliedFormat":1},{"version":"90d202ace592f7b51b131a5890ec93e4df774c8677a485391c280cef0ea53f48","impliedFormat":1},{"version":"b34e1861949a545916696ef40f4a7fe71793661e72dd4db5e04cacc60ef23f7a","impliedFormat":1},{"version":"9833a67663f960dc2d1908a19365ddde55c0651235596ac60d7078a9be6f6e56","impliedFormat":1},{"version":"2bcb8920601b80911430979b6db4a58a7908a31334e74e4e22b75c65edce3587","impliedFormat":1},{"version":"c3186dc74d62d0fb6fba29841ccbf995614992526c37fac5c082d0f28b351e54","impliedFormat":1},{"version":"2306daed18f7f59542a99857a678ef818058eefa30c2a556af123a1cf53889cd","impliedFormat":1},{"version":"b41ed9285a09710807ce2c423e038dfe538e46e9183c0c05aadc27bfb9ae256a","impliedFormat":1},{"version":"56b9f9de03f28eb5922750a213d3f47b21a4f00a48c7c9b89bf1733623873d3a","impliedFormat":1},{"version":"2bdd736078e445858cb1d9df809ff3a2f00445d78664dd70b6794fb2156bdd53","impliedFormat":1},{"version":"d8851222fa6348f7f805a72d535d6c1143a6f3b8001afcf2719ce9152ee47346","impliedFormat":1},{"version":"74ffa4541a56571f379060acaf9ab86da6c889dfe1f588425807e0117e62bba5","impliedFormat":1},{"version":"cf4dc15ca9dc6c0995dd2a9264e5ec37d09d9d551c85f395034e812abdf60a99","impliedFormat":1},{"version":"73e8b003f39c7ce46d2811749dab1dd1b309235fd5c277bd672c30a98b5cf90f","impliedFormat":1},{"version":"4cb49e79595c6413fcb01af55a8a574705bf385bd2ec5cf8b777778952e2914a","impliedFormat":1},{"version":"d6b44382b2670f38c8473e7c16b6e8a9bfa546b396b920afc4c53410eeb22abf","impliedFormat":1},{"version":"3b5c6f451b7ad87e3fcd2008d3a6cb69bd33803e541e9c0fe35754201389158f","impliedFormat":1},{"version":"8329556a2e85e3c3ff3dff43141790ff624b0f5138cedec5bb793164cf8b088f","impliedFormat":1},{"version":"4c889ce7e61ca7f3b7733e0d2be80b3af373e080c922e04639aa25f22963ae63","impliedFormat":1},{"version":"2239a8cd90c48e0b5c075e51099e7e3b4fc3d4741e4d9cc4410d2544d4216946","impliedFormat":1},{"version":"f5aa57712223d7438799be67b0c4a0e5ac3841f6397b5e692673944374f58a83","impliedFormat":1},{"version":"774c37f8faed74c238915868ccc36d0afedfbafb1d2329d6a230966457f57cbd","impliedFormat":1},{"version":"bc41b711477270e8d6f1110d57863284d084b089a22592c7c09df8d4cc3d1d20","impliedFormat":1},{"version":"0c792fe4e5f383b4f085a0033553fb84ed9322b7923fd59d4575aa43135e050d","impliedFormat":1},{"version":"228ed3721f42cc25bfebceef33754ce4766414d975ff71d012f01f141dbe3549","impliedFormat":1},{"version":"08985cdb65bbfe3c70d0037794a3d0f0a5613f55c278c77277a7acc17205db57","impliedFormat":1},{"version":"22bdefb6b2107006ab203073218566443a52ab65eb5e4e8e86c3d38efe776588","impliedFormat":1},{"version":"63f65f58a6f195d5f3529eacfa7a15382e3051a9aa186422e87d48252957ed42","impliedFormat":1},{"version":"c86fea295c21ea01c93410eba2ec6e4f918b97d0c3bf9f1bb1960eabe417e7eb","impliedFormat":1},{"version":"05d41b3e7789381ff4d7f06d8739bf54cc8e75b835cb28f22e59c1d212e48ff3","impliedFormat":1},{"version":"6fbcfc270125b77808679b682663c7c6ad36518f5a528c5f7258bcd635096770","impliedFormat":1},{"version":"9d3bd4ee558de42e9d8434f7293b404c4b7a09b344e77c36bbe959696328d594","impliedFormat":1},{"version":"f63be9b46a22ee5894316cf71a4ba7581809dd98cf046109060a1214ee9e2977","impliedFormat":1},{"version":"dd3cc41b5764c9435b7cae3cc830be4ee6071f41a607188e43aa1edeba4fbb3e","impliedFormat":1},{"version":"b2dbb9485701a1d8250d9a35b74afd41b9a403c32484ed40ed195e8aa369ae70","impliedFormat":1},{"version":"5aa7565991c306061181bd0148c458bcce3472d912e2af6a98a0a54904cd84fc","impliedFormat":1},{"version":"9629e70ae80485928a562adb978890c53c7be47c3b3624dbb82641e1da48fd2f","impliedFormat":1},{"version":"c33d86e1d4753d035c4ea8d0fdb2377043bc894e4227be3ceabc8e6a5411ab2e","impliedFormat":1},{"version":"f9ec74382c95cbc85804daf0e9dabed56511a6dfb72f8a2868aa46a0b9b5eafc","impliedFormat":1},{"version":"1ff7a67731e575e9f31837883ddfc6bfcef4a09630267e433bc5aea65ad2ced4","impliedFormat":1},{"version":"0c4f6b6eb73b0fa4d27ce6eef6c2f1e7bd93d953b941e486b55d5d4b22883350","impliedFormat":1},{"version":"af9692ce3b9db8b94dcfbaa672cb6a87472f8c909b83b5aeea043d6e53e8b107","impliedFormat":1},{"version":"782f2628a998fd03f4ccbe9884da532b8c9be645077556e235149ca9e6bd8c7d","impliedFormat":1},{"version":"269b7db8b769d5677f8d5d219e74ea2390b72ea2c65676b307e172e8f605a74a","impliedFormat":1},{"version":"ae731d469fae328ba73d6928e4466b72e3966f92f14cd1a711f9a489c6f93839","impliedFormat":1},{"version":"90878ed33999d4ff8da72bd2ca3efb1cde76d81940767adc8c229a70eb9332b2","impliedFormat":1},{"version":"d7236656e70e3a7005dba52aa27b2c989ba676aff1cab0863795ac6185f8d54f","impliedFormat":1},{"version":"e327901e9f31d1ad13928a95d95604ee4917d72ad96092da65612879d89aba42","impliedFormat":1},{"version":"868914e3630910e58d4ad917f44b045d05303adc113931e4b197357f59c3e93e","impliedFormat":1},{"version":"7d59adb080be18e595f1ce421fc50facd0073672b8e67abac5665ba7376b29b9","impliedFormat":1},{"version":"275344839c4df9f991bcf5d99c98d61ef3ce3425421e63eeb4641f544cb76e25","impliedFormat":1},{"version":"c4f1cc0bd56665694e010a6096a1d31b689fa33a4dd2e3aa591c4e343dd5181c","impliedFormat":1},{"version":"81c3d9b4d90902aa6b3cbd22e4d956b6eb5c46c4ea2d42c8ff63201c3e9676da","impliedFormat":1},{"version":"5bfc3a4bd84a6f4b992b3d285193a8140c80bbb49d50a98c4f28ad14d10e0acc","impliedFormat":1},{"version":"a7cf6a2391061ca613649bc3497596f96c1e933f7b166fa9b6856022b68783ab","impliedFormat":1},{"version":"864c844c424536df0f6f745101d90d69dd14b36aa8bd6dde11268bb91e7de88e","impliedFormat":1},{"version":"c74a70a215bbd8b763610f195459193ab05c877b3654e74f6c8881848b9ddb7f","impliedFormat":1},{"version":"3fa94513af13055cd79ea0b70078521e4484e576f8973e0712db9aab2f5dd436","impliedFormat":1},{"version":"48ffc1a6b67d61110c44d786d520a0cba81bb89667c7cdc35d4157263bfb7175","impliedFormat":1},{"version":"7cb4007e1e7b6192af196dc1dacd29a0c3adc44df23190752bef6cbbc94b5e0b","impliedFormat":1},{"version":"3d409649b4e73004b7561219ce791874818239913cac47accc083fad58f4f985","impliedFormat":1},{"version":"051908114dee3ca6d0250aacb0a4a201e60f458085177d5eda1fc3cde2e570f3","impliedFormat":1},{"version":"3e8240b75f97eb4495679f6031fb02ad889a43017cae4b17d572324513559372","impliedFormat":1},{"version":"d82609394127fb33eed0b58e33f8a0f55b62b21c2b6c10f1d7348b4781e392cb","impliedFormat":1},{"version":"b0f8a6436fbaf3fb7b707e2551b3029650bfaeb51d4b98e089e9a104d5b559b5","impliedFormat":1},{"version":"eae0ac4f87d56dcf9fbcf9314540cc1447e7a206eee8371b44afa3e2911e520c","impliedFormat":1},{"version":"b585e7131070c77b28cc682f9b1be6710e5506c196a4b6b94c3028eb865de4a7","impliedFormat":1},{"version":"b92ac4cc40d551450a87f9154a8d088e31cff02c36e81db2976d9ff070ba9929","impliedFormat":1},{"version":"6f99b4a552fbdc6afd36d695201712901d9b3f009e340db8b8d1d3415f2776f5","impliedFormat":1},{"version":"43700e8832b12f82e6f519b56fae2695e93bb18dddb485ddea6583a0d1482992","impliedFormat":1},{"version":"e8165ea64af5de7f400d851aeea5703a3b8ac021c08bebc958859d341fa53387","impliedFormat":1},{"version":"6db546ea3ced87efda943e6016c2a748e150941a0704af013dfe535936e820e1","impliedFormat":1},{"version":"f521c4293b6d8f097e885be50c2fef97de3dd512ad26f978360bb70c766e7eae","impliedFormat":1},{"version":"a0666dfd499f319cc51a1e6d9722ed9c830b040801427bbdd2984b73f98d292a","impliedFormat":1},{"version":"a7d86611d7882643dd8c529d56d2e2b698afd3a13a5adc2d9e8157b57927c0da","impliedFormat":1},{"version":"7e4615c366c93399f288c7bfbaa00a1dc123578be9d8ac96b15d489efc3f4851","impliedFormat":1},{"version":"f2e6c87a2c322ee1473cb0bd776eb20ee7bff041bc56619e5d245134ab73e83d","impliedFormat":1},{"version":"ee89bc94431b2dfaf6a7e690f8d9a5473b9d61de4ddcb637217d11229fe5b69f","impliedFormat":1},{"version":"a19c1014936f60281156dd4798395ad4ab26b7578b5a6a062b344a3e924a4333","impliedFormat":1},{"version":"5608be84dd2ca55fc6d9b6da43f67194182f40af00291198b6487229403a98fe","impliedFormat":1},{"version":"4a800f1d740379122c473c18343058f4bd63c3dffdef4d0edba668caa9c75f54","impliedFormat":1},{"version":"8e6868a58ca21e92e09017440fdb42ebfe78361803be2c1e7f49883b7113fdc2","impliedFormat":1},{"version":"2fbb72a22faefa3c9ae0dfb2a7e83d7b3d82ec625a74a8800a9da973511b0672","impliedFormat":1},{"version":"3e8c1a811bad9e5cd313c3d90c39a99867befa746098cdad81a9578ac3392541","impliedFormat":1},{"version":"d88f78b4e272864f414d98e5ed0996cd09f7a3bb01c5b7528320386f7383153d","impliedFormat":1},{"version":"0b9c34da2c6f0170e6a357112b91f2351712c5a537b76e42adfee9a91308b122","impliedFormat":1},{"version":"47adac87ec85a52ed2562cb4a3b441383551727ed802e471aa05c12e7cc7e27e","impliedFormat":1},{"version":"d1cacf181763c5d0960986f6d0abd1a36fc58fc06a707c9f5060b6b5526179ca","impliedFormat":1},{"version":"92610d503212366ff87801c2b9dc2d1bccfa427f175261a5c11331bc3588bb3f","impliedFormat":1},{"version":"805e2737ce5d94d7da549ed51dfa2e27c2f06114b19573687e9bde355a20f0ff","impliedFormat":1},{"version":"a37b576e17cf09938090a0e7feaec52d5091a1d2bbd73d7335d350e5f0e8be95","impliedFormat":1},{"version":"98971aa63683469692fef990fcba8b7ba3bae3077de26ac4be3e1545d09874b8","impliedFormat":1},{"version":"c6d36fa611917b6177e9c103a2719a61421044fb81cdd0accd19eba08d1b54de","impliedFormat":1},{"version":"088592cf2e218b99b02a5029ed8d1a763a3856cd25e012cfbb536b7494f08971","impliedFormat":1},{"version":"5eb39c56462b29c90cb373676a9a9a179f348a8684b85990367b3bbc6be5a6e9","impliedFormat":1},{"version":"52252b11bcbfaeb4c04dc9ec92ea3f1481684eee62c0c913e8ff1421dc0807e5","impliedFormat":1},{"version":"731d07940d9b4313122e6cc58829ea57dcc5748003df9a0cad7eb444b0644685","impliedFormat":1},{"version":"b3ead4874138ce39966238b97f758fdb06f56a14df3f5e538d77596195ece0b5","impliedFormat":1},{"version":"032b40b5529f2ecce0524974dbec04e9c674278ae39760b2ee0d7fce1bb0b165","impliedFormat":1},{"version":"c25736b0cb086cd2afa4206c11959cb8141cea9700f95a766ad37c2712b7772b","impliedFormat":1},{"version":"033c269cd9631b3f56bb69a9f912c1f0d6f83cf2cff4d436ee1c98f6e655e3b5","impliedFormat":1},{"version":"bd6d692a4a950abbfabe29131420abe804e7f3cc187c3c451f9811e9cf4408ce","impliedFormat":1},{"version":"a9b6411417d4bffd9a89c41dc9dedda7d39fb4fa378eaa0ab55ec9ea1a94eb6a","impliedFormat":1},{"version":"1329e7cd7aca4d223ef5a088d82bc3f6f302ce70581c8d3823a050ea155eec3b","impliedFormat":1},{"version":"09248c76437c5b1efce189b4050c398f76a9385135af75c5fb46308b0d1432e0","impliedFormat":1},{"version":"b8df115bf7b30cceeb4550c0be507082b9930ee6268539a1a1aaffb0791cc299","impliedFormat":1},{"version":"dde00f41a2d2b1e70df6df8ac33de7cb3a658956212c7bee326245cc01c990c2","impliedFormat":1},{"version":"115d092e2748990ff0f67f376f47e9a45a2f21f7c7784102419c14b32c4362d1","impliedFormat":1},{"version":"4ba068163c800094cd81b237f86f22c3a33c23cf2a70b9252aca373cfdf59677","impliedFormat":1},{"version":"5cd5a999e218c635ea6c3e0d64da34a0f112757e793f29bc097fd18b5267f427","impliedFormat":1},{"version":"cc14b99b4e1bbedab2e3fbf058ed95231d8ced691f0645f2a206c32464f1bd7b","impliedFormat":1},{"version":"e6db934da4b03c1f4f1da6f4165a981ec004e9e7d956c585775326b392d4d886","impliedFormat":1},{"version":"53e65282ab040a9f535f4ad2e3c8d8346034d8d69941370886d17055874b348d","impliedFormat":1},{"version":"6ecb85c8cbb289fe72e1d302684e659cc01ef76ae8e0ad01e8b2203706af1d56","impliedFormat":1},{"version":"35ab64ba795a16668247552da22f2efe1c5fbc5bc775392c534747be7f91df04","impliedFormat":1},{"version":"34283015304de5df8d6e3740b9bca58e40513ec6333b3fb0a3fa3aa4c43b856b","impliedFormat":1},{"version":"4a397c8a3d1cccf28751bcca469d57faeb637e76b74f6826e76ad66a3c57c7b8","impliedFormat":1},{"version":"34c1bb0d4cf216f2acb3d013ad2c79f906fe89ce829e23a899029dfa738f97e0","impliedFormat":1},{"version":"b70b5b3d14d125d6dcc16a9ac43cafe8801f644954ac36cb2918723f9cbbd4fe","impliedFormat":1},{"version":"b50f05738b1e82cbb7318eb35a7aaf25036f5585b75bbf4377cfa2bad15c40bf","impliedFormat":1},{"version":"c682cb23f38a786bb37901b3f64727bd3c6210292f5bb36f3b11b63fbe2b23ee","impliedFormat":1},{"version":"d6592cf10dc7797d138af32800d53ff4707fdcd6e053812ce701404f5f533351","impliedFormat":1},{"version":"997f6604cd3d35281083706aa2862e8181ed1929a6cbb004c087557d6c7f23c4","impliedFormat":1},{"version":"9584dd669a3bf285e079502ebbb683e7da0bf7f7c1eb3d63f6ef929350667541","impliedFormat":1},{"version":"41a10e2db052a8bf53ed4d933d9b4f5caa30bdaee5a9d978af95f6641ce44860","impliedFormat":1},{"version":"1dd236a02d5974092780f456750107a3158124002de00ca17342f3a4819e297b","impliedFormat":1},{"version":"652e51858bafd77e1abcc4d4e9d5e48cc4426c3dd2910021abd8cc664961e135","impliedFormat":1},{"version":"8c5c602045ffdfebeffc7a71cd2bf201fe147a371274b5fcbded765a92f2af78","impliedFormat":1},{"version":"6392ce794eef6f9b57818264bb0eeb24a46cf923f7695a957c15d3d087fbb6cc","impliedFormat":1},{"version":"b10f123e8100aa98723c133af16f1226a6360ec5b6990a0fe82b165d289549db","impliedFormat":1},{"version":"93d20368cdb5fff7f7398bfc9b2b474b2a2d5867277a0631a33b7db7fd53d5b4","impliedFormat":1},{"version":"b1e69b9834104482fabf7fba40e86a282ee10e0600ffd75123622f4610b0ef9e","impliedFormat":1},{"version":"ad5bb6c450cb574289db945ff82be103ed5d0ad8ee8c76164cee7999c695ae01","impliedFormat":1},{"version":"217761e8a5482b3ad20588a801521c2f5f9f7fb2fbb416d4eff3aff9b57f8471","impliedFormat":1},{"version":"7ad780687331f05998c62277d73b6f15ee3e8045b0187a515ffc49c0ad993606","impliedFormat":1},{"version":"e9aa5ccb42e118f5418721d2ac8c0ebdebeb9502007db9b4c1b7c9b8d493013e","impliedFormat":1},{"version":"d300868212b3cc4d13228f5dc2e9880d5959dc742c0c55be2fc43bcda8504c8f","impliedFormat":1},{"version":"0c55daad827669843bd2401f1ddd163b74d9f922680b08ae6e162ceb6c11b078","impliedFormat":1},{"version":"fe45a9bc654dfd1550c9466c0dad9c8017f2626476ed9d25c65ddfc1943f6b74","impliedFormat":1},{"version":"03abcbc7b5b68887525be71a194dd7f9f68276b5fb5b8989abae9a91585ddc33","impliedFormat":1},{"version":"5055e86e689cfe39104ab71298757e5aac839c2ea9d1f12299e76fa79303d47d","impliedFormat":1},{"version":"42266c387025558423c19d624f671352aac3e449c23906cb636f9ae317b72d7e","impliedFormat":1},{"version":"e578a36b3683d233e045a85c9adb0f10e83d2b48f777b9c05fbc363ccc6bdd34","impliedFormat":1},{"version":"0235d0ba0c7b64244d4703b7d6cabd88ba809abeb01da0c13e9ed111bf5e7059","impliedFormat":1},{"version":"9b21e8a79f4213c1cf29f3c408f85a622f9eb6f4902549ccb9a2c00717a0b220","impliedFormat":1},{"version":"d556e498591413e254793f9d64d3108b369a97bd50f9dd4015b5552888e975ef","impliedFormat":1},{"version":"e2c652c7a45072e408c1749908ca39528d3a9a0eb6634a8999b8cf0e35ef20c8","impliedFormat":1},{"version":"ec08224b320739d26aaf61cead7f1e0f82e6581df0216f6fe048aa6f5042cb8c","impliedFormat":1},{"version":"4eadaa271acca9bd20fc6ac1ea5e4bf9ab6698b8ccf3ec07c33df4970f8130f1","impliedFormat":1},{"version":"3238d2eee64423c8d41972c88673b0327d8b40174a78ea346bcd10954a8f3373","impliedFormat":1},{"version":"8f773ddff9070d725dd23f5cf6c8e62bd86984a57b5d5e3fc7583010b48cd8ac","impliedFormat":1},{"version":"5ecd8fdeb6c87db9c320eefbfa9ea27efccbdce853ed38d5ba58e2da482edf1f","impliedFormat":1},{"version":"19a4d116285e7d77e91411966930761a2204ce2d20915afdb12652681a4a88d7","impliedFormat":1},{"version":"c30ca82112586c5dae7477d7e82cc91a7e0d1e658c581f9ec3df07c4485bba84","impliedFormat":1},{"version":"68fca1813d17ee736f41124ccc958d0364cdef79ad1222951bfacc36b2630a58","impliedFormat":1},{"version":"7813329e568df1d42e5a6c52312b1a7c69700e35a561cf085158c345be155b22","impliedFormat":1},{"version":"561067dc7b6b7635277d3cad0a0e11f698d377063dd2c15dfac43ef78847eef4","impliedFormat":1},{"version":"438247e782a8a9b9abdce618e963667cf95157cc6d3f5194a452d3c7d9e9655c","impliedFormat":1},{"version":"253f79802f33f405c1807f33efa7d78e0a26143ee694297d4f8e1477c7ed5e28","impliedFormat":1},{"version":"f1e8eca509487806fdf979349cfcdb6ffdeb20f11b7e95666c4309d12dcd9ba6","impliedFormat":1},{"version":"83724b26b711d85d6cfc9dd92fd5d666ffaae27fcfb1a0110401b98814ea26c0","impliedFormat":1},{"version":"869a27c929366c3c864013a991fd4c4c86af73eba25513e8ae915f814d3d349c","impliedFormat":1},{"version":"bfa105c32ed586b227188f7b568776d03202dc7aa4c3af2746579450c7d5e7f2","impliedFormat":1},{"version":"756e3f41a7f2501a34e1a070283c7f5550e200eeb43fed3c806e3f2edd924a75","impliedFormat":1},{"version":"59935cc13dcb7c3c7825e770a61e6696bfd11b65e3e47c28acc410dbdf8461c0","impliedFormat":1},{"version":"85e2808cc73ab3ac07774802b34a6ff0d7e1e46c26de7bc2dbe08e04b3340edb","impliedFormat":1},{"version":"f766e5cdea938e0c9d214533fd4501ab0ee23ab4efca9edba334fa02d2869f11","impliedFormat":1},{"version":"eb380820a3a1feda3a182a3d078da18e0d5b7da08ae531ce11133a84b479678c","impliedFormat":1},{"version":"7fba5cc3088ad9acada3daeff52dae0f2cac8d84d19508abd78af5924dc96bea","impliedFormat":1},{"version":"14176cfdbc3d1d633ad9b5daf044ab4c7d0d73be61ca2f14388800e21f0989cd","impliedFormat":1},{"version":"a24f510afe4d938d625a4b5a5374ac0478e56305e8743dd7d37d86d709754286","impliedFormat":1},{"version":"648acdbcbcd01b1a91e8b0ad390ed59fada685977f44b90e148b65bd8159dfe8","impliedFormat":1},{"version":"8309898ba0ac6f2856a94a11723d499091253a6d5df34ddebc6149d43480bfd2","impliedFormat":1},{"version":"a317ae0eb092da3fd799d1717a2da319a74abebe85e2914cb259222969f95705","impliedFormat":1},{"version":"36d76e2dbd5f5243bd566b018c589e2ba707e34b24ec7d285feb11ba6bf23fbe","impliedFormat":1},{"version":"f780879a2ca63dbb59b36f772bc28dccd2840f1377d8d632e8c978b99c26a45f","impliedFormat":1},{"version":"335c2e013b572967a9a282a70f9dded38631189b992381f1df50e966c7f315d6","impliedFormat":1},{"version":"8b7a519edbd0b7654491300d8e3cbd2cb3ef921003569ca39ebd33e77479bb99","impliedFormat":1},{"version":"c90f8038c75600e55db93d97bab73c0ab8fb618d75392d1d1ad32e2f6e9c7908","impliedFormat":1},{"version":"ca083f3bf68e813b5bded56ecbf177636aa75833eb86c7b40e3d75b8ce4c2f78","impliedFormat":1},{"version":"3c8bf00283ef468da8389119d3f5662c81106e302c8810f40ea86b1018df647e","impliedFormat":1},{"version":"67b248e4bac845c5139898b44cbd3e1213674bcc9831039701b5f0f957243a24","impliedFormat":1},{"version":"63d49516f359186f7b3e3115f2c829ed75c319b34022c97b56beead032a073b7","impliedFormat":1},{"version":"9f5f256c7b5cc4a98ef557ea9720f81e96319d569f731c897ddb4514936242b4","impliedFormat":1},{"version":"a20ded6c920f6e566537e93d69cbad79bc57d7e3ce85686003078cf88c1c9cfc","impliedFormat":1},{"version":"40b2d781df7b4a76d33454cb917c3883655ec1d8d05424b7a80d01610ad5082f","impliedFormat":1},{"version":"703ea2acd8b4741248897a5709cd46e22fcd9d13f01ff3481322a86505f0b77c","impliedFormat":1},{"version":"e09c56f8c446225e061b53cb2f95fcbbc8555483ab29165f6b0f39bc82c8d773","impliedFormat":1},{"version":"51ebaff0cba6b3adf43f13b57bb731d56946cabd06d14cf9dfc7c5eaa8f95770","impliedFormat":1},{"version":"a6a059446e66fbf5072eccce94eb5587cef2f99aa04d4bbd4ebe63d0a6592a4f","impliedFormat":1},{"version":"6e2533e27eba5ff02d6eed37e0a7eb69ae7982e0f72fd8f74c90ab201f061867","impliedFormat":1},{"version":"9c10dd3d85b7620ed3105b3f018125d0bb54198bf5847e39622afb22c651a1ad","impliedFormat":1},{"version":"58c62e415bf74b1423bf443587e33d7951a8bf19d7b03073f26e86d9b43ba9ea","impliedFormat":1},{"version":"dd6ec67ad168e92b8bf79ba975c6e0be8c60e403ba704d1c1b31a6059c12f967","impliedFormat":1},{"version":"bcaf468eea143f8e68ca40e5da58d640656b4f36697170c339042500be78ac5d","impliedFormat":1},{"version":"92de961d1db5fe075db8c0b6414a6eec430adaf9022465fe9d0a23f437aafcb3","impliedFormat":1},{"version":"7610ecdae59cea1a8db7580941ebc24d522d8ac1751ce718a6af22d41e1a1279","impliedFormat":1},{"version":"7355edff7686f91edbca25e0fe9d6c3359df2520d48d3dc6d857aa47047f8ddf","impliedFormat":1},{"version":"d49275f9098a8e7a5df7c55321b0242cef0bfdde51018b7b2709c4dc74917822","impliedFormat":1},{"version":"b25556c4111afad4cb174aa4674db2e5b23a6b191dc6a3e42c7c3417ea446a68","impliedFormat":1},{"version":"f9568a3a6c74013aee8b09d73ef04175596b51ce6f5d9dcd4885418170fe9306","impliedFormat":1},{"version":"bd3910ccd4fcd05ebd83fbfeb62f5a82a6674c85c6c0e4755c16298df7abe4d7","impliedFormat":1},{"version":"7c0541d0addc3007e5f5776023d5e6e44f96eae0684cdabe59ef04f2a294b116","impliedFormat":1},{"version":"70137204b720e4dd1b81260a70578f0f4f417c53837f8a13859b2f58e20d7150","impliedFormat":1},{"version":"b28b6875a761fd153ebf120fecb359660de80fd36e90c9b3d72a12318bd5d789","impliedFormat":1},{"version":"56d092bd6225f6e67d9acab3fd65ce0a4edb36cadba2f0370e67322e2f6f1bc8","impliedFormat":1},{"version":"a4709d5d466ad8dcf4ddccb905ad95348131df1616f964185be9739f96526bde","impliedFormat":1},{"version":"73b0fd6255f24e82be861f800a264f0175984062b6ccca3052578b03ed6f397b","impliedFormat":1},{"version":"4a3f7c6f02cb01eb7a9800548b41cfa03a57e476fc92a72869983f37efa8067a","impliedFormat":1},{"version":"fafd0ff1e1aa1ef702a4600d6ecdf561bb2e77cccfa61763ff7360b6e23c816e","impliedFormat":1},{"version":"0828dd60d0ea50ec9edc32fedd4ac30dc4af97fc1b3e3ce07c9640ce1103d92a","impliedFormat":99},{"version":"412e2558ef05b165e9ed93bacd719c38a99b3c481e6dd09642d83c8d90817241","impliedFormat":99},{"version":"31b49604ccfefe542191c41de8874b1893bf24a821007c215a4373a682f09556","impliedFormat":99},{"version":"a1ae64f5958e3f1c0c3879ff45fb40c7ef41f37214493a43296954ce27c6c4f1","impliedFormat":99},{"version":"d3cfde44f8089768ebb08098c96d01ca260b88bccf238d55eee93f1c620ff5a5","impliedFormat":1},{"version":"293eadad9dead44c6fd1db6de552663c33f215c55a1bfa2802a1bceed88ff0ec","impliedFormat":1},{"version":"54f6ec6ea75acea6eb23635617252d249145edbc7bcd9d53f2d70280d2aef953","impliedFormat":1},{"version":"c25ce98cca43a3bfa885862044be0d59557be4ecd06989b2001a83dcf69620fd","impliedFormat":1},{"version":"8e71e53b02c152a38af6aec45e288cc65bede077b92b9b43b3cb54a37978bb33","impliedFormat":1},{"version":"754a9396b14ca3a4241591afb4edc644b293ccc8a3397f49be4dfd520c08acb3","impliedFormat":1},{"version":"f672c876c1a04a223cf2023b3d91e8a52bb1544c576b81bf64a8fec82be9969c","impliedFormat":1},{"version":"e4b03ddcf8563b1c0aee782a185286ed85a255ce8a30df8453aade2188bbc904","impliedFormat":1},{"version":"de2316e90fc6d379d83002f04ad9698bc1e5285b4d52779778f454dd12ce9f44","impliedFormat":1},{"version":"25b3f581e12ede11e5739f57a86e8668fbc0124f6649506def306cad2c59d262","impliedFormat":1},{"version":"2da997a01a6aa5c5c09de5d28f0f4407b597c5e1aecfd32f1815809c532650a2","impliedFormat":1},{"version":"5d26d2e47e2352def36f89a3e8bf8581da22b7f857e07ef3114cd52cf4813445","impliedFormat":1},{"version":"3db2efd285e7328d8014b54a7fce3f4861ebcdc655df40517092ed0050983617","impliedFormat":1},{"version":"d5d39a24c759df40480a4bfc0daffd364489702fdbcbdfc1711cde34f8739995","impliedFormat":1},"7ae8623aed44d94d0f10af93d8edea2b1970dd0fecc3cf603679f6cb44a16adc",{"version":"908217c4f2244ec402b73533ebfcc46d6dcd34fc1c807ff403d7f98702abb3bc","impliedFormat":1},{"version":"1bc5991c91bf4be8b59db501ed284a34945d95abe9b7451d02ea001f7c5621a9","impliedFormat":1},{"version":"d8b8a5a6bf623239d5374ad4a7ff6f3b195ab5ee61293f59f1957e90d2a22809","impliedFormat":1},{"version":"35d283eca7dc0a0c7b099f5fbbf0678b87f3d837572cd5e539ba297ad9837e68","impliedFormat":1},{"version":"1c8384a195a2d931cf6e2b8f656acf558ca649a3f74922d86b95889f49a7f7c5","impliedFormat":1},{"version":"cd11655f57a3558dfcee05a6e78c026f9dfd30535eaf124439c5e88a5617359b","impliedFormat":1},"6515c9a21edd161c428d49aa1929aba7fa1e56848740fe37c292e446e5f62b40","118ec59b4f73330d30f57666efa20a4762723fc4e5a3ffe65517b49503a68b2c","7a54dbfa51bfb6a3313d7fb8722282abe480be1c528da3a7989554f287d76567","ad59ca58126aa71c1378dc1f3dee0db511bde2f879731584c6b2fde8f78ee329","8a8b4f88fd522ac7feda6f8059e7e86b56445cc7e7cc49bee0df0c9a982a2eac","f7d77a1f64b554b5a899132d581324e43df0f228ebbb50ebed3df81fb5910b91","aa010953767a12b3868638ae86300ef7e1ef8acc97c96b2e86a2c3e9d417f7a6","5f5448fe381f712d7e82acede031516227e4617881b07599b00daa33e5f1db55","cc5c1daf58ec73b6dee477c6a125cf7def78c4d9ae9c4472ef7305448ead54a4","4705c363e60275939210a51d55697c659ede09a663b401961e92b82d7cc60f33","8b800969cccb5ce560522bb14f1c6e8b55c4f7c65450178e002fab4f23a19195","b2e389c3156a39fec5e871b8f7c85834fc1e71abb73f93a2186bb80a83439b31","478b2f6e5436bdb929859e93dc6d97369e3ac9f241a091af68ca6b13a3763b3a","a2cfd1ffb4e837d5239b6f078fe116ecfd706d75e696cc972e07c434144f8f7b","d7a5688a4317f8f326cae2e70dfa140adc2c8798c2236d5d385e5778072f7161","f71138a8fc359b38c575749f714df77e00425f30531b3dcae7d7338a23eed010","6f0c87f802cf1611bc8cd7817e4f40c4f9587b0578c19f51e6c442bd0b61d8a9","4a05ba43f90f5be9485c1b167fbc442c6005cf5d09d9d953f50acf84ba9872a6","6f1842c65ff5e7e0920ddd6dddc7a8733350aa465ee3c74f4d938a27d9e66f9e","5e41ec0bd878f4a185e7ffc573c86711c4291da2b0b6dd112825dbf812d8da73","e10a909dbf1510bb375dd641e5cef025cacfb944d4c0a931b3c5240af19dfdeb","87e2afdbf78ac8dc731498bbcea02b86395b4ed146d36d2ce765e8a401db8d27","4d6a9802943facbb1518792b25f32353d0f4fc6211394a2aeb6ac7e1c7575a05","46fd3dae742cc23d5d23dd32d97a8a1fc43fecc00eec54d48bda44b63b4e3107","ca996fede90db437c208ac2e5512bcc9c423d57b1c93f9e6f2b74809d0cf4543","e19a44d897c77e1b0f0d3fb97f8117d453d622b4089406d219ae35d05b71d32e","368cf0f9255b180ecefa71b16c839a34632ab917add1527d1f4efb2f99ffb956","0d9d95ba2c9972118506a44be24316b4de9007790fa4975adb9be42941a02162","46f33fcb640d0ef712a60af8cdd5e24c071776256dc8c114390234b5202d93a1","03be392139faadd825305f6f31cf18c3a0464465a13005bd16076fb7d7ee41ab","7d8a8ca9f6b8d2d2ca34765da85a4d3901485aa36a0716ab74593df5f6ca767e","62fd226f341eb5f90bc43d0295c8df1b03d892c08a46e6260ff4f8daa854cc37","e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",{"version":"a3d3f704c5339a36da3ca8c62b29072f87e86c783b8452d235992142ec71aa2d","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"68cc8d6fcc2f270d7108f02f3ebc59480a54615be3e09a47e14527f349e9d53e","impliedFormat":1},{"version":"95da3c365e3d45709ad6e0b4daa5cdaf05e9076ba3c201e8f8081dd282c02f57","impliedFormat":1},{"version":"f2f23fe34b735887db1d5597714ae37a6ffae530cafd6908c9d79d485667c956","impliedFormat":1},{"version":"5bba0e6cd8375fd37047e99a080d1bd9a808c95ecb7f3043e3adc125196f6607","impliedFormat":1}],"root":[171,576,[583,616]],"options":{"allowSyntheticDefaultImports":true,"emitDecoratorMetadata":true,"esModuleInterop":true,"experimentalDecorators":true,"module":1,"noImplicitAny":true,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":true,"strictPropertyInitialization":false,"target":9},"referencedMap":[[617,1],[166,2],[165,3],[170,3],[618,1],[162,4],[167,5],[163,1],[619,1],[621,6],[158,1],[620,1],[104,7],[105,7],[106,8],[64,9],[107,10],[108,11],[109,12],[59,1],[62,13],[60,1],[61,1],[110,14],[111,15],[112,16],[113,17],[114,18],[115,19],[116,19],[118,1],[117,20],[119,21],[120,22],[121,23],[103,24],[63,1],[122,25],[123,26],[124,27],[157,28],[125,29],[126,30],[127,31],[128,32],[129,33],[130,34],[131,35],[132,36],[133,37],[134,38],[135,38],[136,39],[137,1],[138,1],[139,40],[141,41],[140,42],[142,43],[143,44],[144,45],[145,46],[146,47],[147,48],[148,49],[149,50],[150,51],[151,52],[152,53],[153,54],[154,55],[155,56],[156,57],[160,1],[161,1],[159,58],[164,59],[577,1],[65,1],[168,60],[169,61],[578,62],[58,1],[237,63],[236,1],[258,1],[179,64],[238,1],[188,1],[178,1],[302,1],[392,1],[339,65],[548,66],[389,67],[547,68],[546,68],[391,1],[239,69],[346,70],[342,71],[543,67],[513,1],[463,72],[464,73],[465,73],[477,73],[470,74],[469,75],[471,73],[472,73],[476,76],[474,77],[504,78],[501,1],[500,79],[502,73],[516,80],[514,1],[510,81],[515,1],[509,82],[478,1],[479,1],[482,1],[480,1],[481,1],[483,1],[484,1],[487,1],[485,1],[486,1],[488,1],[489,1],[184,83],[460,1],[459,1],[461,1],[458,1],[185,84],[457,1],[462,1],[491,85],[216,86],[490,1],[219,1],[220,87],[221,87],[468,88],[466,88],[467,1],[176,86],[215,89],[511,90],[183,1],[475,83],[503,91],[473,92],[492,87],[493,93],[494,94],[495,94],[496,94],[497,94],[498,95],[499,95],[508,96],[507,1],[505,1],[506,97],[512,98],[332,1],[333,99],[336,65],[337,65],[338,65],[307,100],[308,101],[327,65],[244,102],[331,65],[248,1],[326,103],[286,104],[250,105],[309,1],[310,106],[330,65],[324,1],[325,107],[311,100],[312,108],[209,1],[329,65],[334,1],[335,109],[340,1],[341,110],[210,111],[313,65],[328,65],[315,1],[316,1],[317,1],[318,1],[319,1],[320,1],[314,1],[321,1],[545,1],[322,112],[323,113],[182,1],[207,1],[235,1],[212,1],[214,1],[297,1],[208,88],[240,1],[243,1],[303,114],[292,115],[343,116],[232,117],[226,1],[217,118],[218,119],[552,80],[227,1],[230,118],[213,1],[228,73],[231,120],[229,95],[222,121],[225,90],[395,122],[418,122],[399,122],[402,123],[404,122],[453,122],[430,122],[394,122],[422,122],[450,122],[401,122],[431,122],[416,122],[419,122],[407,122],[440,124],[436,122],[429,122],[411,125],[410,125],[427,123],[437,122],[455,126],[456,127],[441,128],[433,122],[414,122],[400,122],[403,122],[435,122],[420,123],[428,122],[425,129],[442,129],[426,123],[412,122],[421,122],[454,122],[444,122],[432,122],[452,122],[434,122],[413,122],[448,122],[438,122],[415,122],[443,122],[451,122],[417,122],[439,125],[423,122],[447,130],[398,130],[409,122],[408,122],[406,131],[393,1],[405,122],[449,129],[445,129],[424,129],[446,129],[251,132],[257,133],[256,134],[247,135],[246,1],[255,136],[254,136],[253,136],[536,137],[252,138],[294,1],[245,1],[262,139],[261,140],[517,132],[519,132],[520,132],[521,132],[522,132],[523,132],[524,141],[529,132],[525,132],[526,132],[535,132],[527,132],[528,132],[530,132],[531,132],[532,132],[533,132],[518,132],[534,142],[223,1],[390,143],[557,144],[537,145],[538,146],[541,147],[539,146],[233,148],[234,149],[540,146],[279,1],[187,150],[382,1],[196,1],[201,151],[383,152],[380,1],[283,1],[387,153],[386,1],[352,1],[381,73],[378,1],[379,154],[388,155],[377,1],[376,95],[197,95],[181,156],[347,157],[384,1],[385,1],[350,96],[186,1],[203,90],[280,158],[206,159],[205,160],[202,161],[351,162],[284,163],[194,164],[353,165],[199,166],[198,167],[195,168],[349,169],[173,1],[200,1],[174,1],[175,1],[177,1],[180,152],[172,1],[224,1],[348,1],[204,170],[306,171],[549,172],[305,148],[550,173],[551,174],[193,175],[397,176],[396,177],[249,178],[360,179],[299,180],[369,181],[300,182],[371,183],[361,184],[373,185],[374,186],[359,1],[367,187],[287,188],[363,189],[362,189],[345,190],[344,190],[372,191],[291,192],[289,193],[290,193],[364,1],[375,194],[365,1],[370,195],[296,196],[368,197],[366,1],[298,198],[288,1],[358,199],[542,200],[544,201],[555,1],[293,202],[260,1],[304,203],[259,1],[295,204],[301,205],[278,1],[189,1],[282,1],[241,1],[354,1],[356,206],[263,1],[191,91],[553,207],[211,208],[357,209],[281,210],[190,211],[285,212],[242,213],[355,214],[264,215],[192,216],[277,217],[265,1],[276,218],[271,219],[272,220],[275,116],[274,221],[270,220],[273,221],[266,116],[267,116],[268,116],[269,222],[554,223],[556,224],[56,1],[57,1],[11,1],[10,1],[2,1],[12,1],[13,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[3,1],[20,1],[21,1],[4,1],[22,1],[26,1],[23,1],[24,1],[25,1],[27,1],[28,1],[29,1],[5,1],[30,1],[31,1],[32,1],[33,1],[6,1],[37,1],[34,1],[35,1],[36,1],[38,1],[7,1],[39,1],[44,1],[45,1],[40,1],[41,1],[42,1],[43,1],[8,1],[49,1],[46,1],[47,1],[48,1],[50,1],[9,1],[51,1],[52,1],[53,1],[55,1],[54,1],[1,1],[560,1],[561,225],[558,1],[559,226],[81,227],[91,228],[80,227],[101,229],[72,230],[71,231],[100,232],[94,233],[99,234],[74,235],[88,236],[73,237],[97,238],[69,239],[68,232],[98,240],[70,241],[75,242],[76,1],[79,242],[66,1],[102,243],[92,244],[83,245],[84,246],[86,247],[82,248],[85,249],[95,232],[77,250],[78,251],[87,252],[67,253],[90,244],[89,242],[93,1],[96,254],[579,255],[582,256],[580,232],[581,257],[575,258],[566,259],[573,260],[568,1],[569,1],[567,261],[570,262],[562,1],[563,1],[574,263],[565,264],[571,1],[572,265],[564,266],[594,267],[595,60],[597,268],[587,269],[588,269],[608,270],[603,271],[604,272],[607,273],[585,274],[584,275],[606,276],[602,277],[605,276],[612,278],[589,279],[610,280],[592,269],[611,281],[609,282],[615,1],[616,1],[576,283],[590,284],[613,285],[171,1],[614,286],[586,1],[599,287],[593,288],[596,60],[601,289],[598,290],[591,291],[583,292],[600,1],[622,1],[623,1]],"semanticDiagnosticsPerFile":[[607,[{"start":1041,"length":45,"messageText":"Cannot find module '../../auth/authServices/useraccess.services' or its corresponding type declarations.","category":1,"code":2307}]],[613,[{"start":150,"length":57,"messageText":"Cannot find module '../../components/v1/auth/authEntities/userAccess.entity' or its corresponding type declarations.","category":1,"code":2307},{"start":238,"length":16,"messageText":"Module '\"../../components/v1/platform/platformEntities/platform.entity\"' has no exported member 'FreeDomainEntity'. Did you mean to use 'import FreeDomainEntity from \"../../components/v1/platform/platformEntities/platform.entity\"' instead?","category":1,"code":2614},{"start":788,"length":31,"messageText":"Cannot find module '../defaultRolesAndPermissions' or its corresponding type declarations.","category":1,"code":2307},{"start":936,"length":24,"messageText":"Cannot find module '../../utils/free_email' or its corresponding type declarations.","category":1,"code":2307},{"start":1984,"length":8,"code":2339,"category":1,"messageText":"Property 'kycSteps' does not exist on type '{ userTypes: readonly [\"organization-user\", \"platform-user\"]; platformRoleTypes: readonly [\"super-admin\", \"admin\"]; emailStatus: readonly [\"delivered\", \"failed\", \"queued\", \"accepted\"]; emailType: readonly [\"automatic\", \"manual\"]; ... 5 more ...; supportedImgTypes: string[]; }'."},{"start":1998,"length":5,"messageText":"Parameter 'setup' implicitly has an 'any' type.","category":1,"code":7006},{"start":2110,"length":11,"code":2339,"category":1,"messageText":"Property 'setupStatus' does not exist on type '{ userTypes: readonly [\"organization-user\", \"platform-user\"]; platformRoleTypes: readonly [\"super-admin\", \"admin\"]; emailStatus: readonly [\"delivered\", \"failed\", \"queued\", \"accepted\"]; emailType: readonly [\"automatic\", \"manual\"]; ... 5 more ...; supportedImgTypes: string[]; }'."},{"start":3132,"length":6,"code":2769,"category":1,"messageText":{"messageText":"No overload matches this call.","category":1,"code":2769,"next":[{"messageText":"Overload 1 of 2, '(entityClass: EntityTarget, plainObject?: DeepPartial | undefined): RoleEntity', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Type '\"platform-admin\"' is not assignable to type 'DeepPartial<\"organization-user\" | \"platform-user\"> | undefined'.","category":1,"code":2322}]},{"messageText":"Overload 2 of 2, '(entityClass: EntityTarget, plainObjects?: DeepPartial[] | undefined): RoleEntity[]', gave the following error.","category":1,"code":2772,"next":[{"messageText":"Object literal may only specify known properties, and 'name' does not exist in type 'DeepPartial[]'.","category":1,"code":2353}]}]},"relatedInformation":[]},{"start":3864,"length":6,"messageText":"Parameter 'domain' implicitly has an 'any' type.","category":1,"code":7006}]]],"version":"5.8.3"} \ No newline at end of file