From 7baa6bcd3dc627ec29965308617799a12c0be56f Mon Sep 17 00:00:00 2001 From: haxry Date: Wed, 2 Jul 2025 17:08:38 +0000 Subject: [PATCH 1/2] add sample zod validation --- package.json | 3 ++- pnpm-lock.yaml | 11 ++++++++++ src/resources/schema/url.ts | 7 +++++++ src/validator/index.ts | 41 +++++++++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/resources/schema/url.ts diff --git a/package.json b/package.json index 88c3ebda1..c1bed9f28 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "ts-adt": "^2.1.2", "winston": "^3.11.0", "yaml": "^2.3.4", - "yargs": "^17.7.2" + "yargs": "^17.7.2", + "zod": "^3.25.67" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97a9cd5d9..3800ffa91 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,6 +61,9 @@ importers: yargs: specifier: ^17.7.2 version: 17.7.2 + zod: + specifier: ^3.25.67 + version: 3.25.67 devDependencies: "@types/lodash": specifier: ^4.14.202 @@ -5461,6 +5464,12 @@ packages: } engines: { node: ">=10" } + zod@3.25.67: + resolution: + { + integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==, + } + snapshots: "@aashutoshrathi/word-wrap@1.2.6": {} @@ -9269,3 +9278,5 @@ snapshots: yn@3.1.1: {} yocto-queue@0.1.0: {} + + zod@3.25.67: {} diff --git a/src/resources/schema/url.ts b/src/resources/schema/url.ts new file mode 100644 index 000000000..2d9968821 --- /dev/null +++ b/src/resources/schema/url.ts @@ -0,0 +1,7 @@ +import { z } from "zod"; + +export const urlSchema = z.object({ + url: z.string().url(), +}); + +export type URL = z.infer; diff --git a/src/validator/index.ts b/src/validator/index.ts index a8fd35a47..0d19bae7d 100644 --- a/src/validator/index.ts +++ b/src/validator/index.ts @@ -2,16 +2,18 @@ import Ajv from "ajv"; import addFormats from "ajv-formats"; import projectSchema from "../resources/schema/project.json" with { type: "json" }; import collectionSchema from "../resources/schema/collection.json" with { type: "json" }; -import urlSchema from "../resources/schema/url.json" with { type: "json" }; +//import urlSchema from "../resources/schema/url.json" with { type: "json" }; import socialProfileSchema from "../resources/schema/social-profile.json" with { type: "json" }; import blockchainAddressSchema from "../resources/schema/blockchain-address.json" with { type: "json" }; import { Project } from "../types/project.js"; import { Collection } from "../types/collection.js"; -import { URL } from "../types/url.js"; +//import { URL } from "../types/url.js"; +import { URL, urlSchema } from "../resources/schema/url.js"; import { SocialProfile } from "../types/social-profile.js"; import { BlockchainAddress } from "../types/blockchain-address.js"; import { DEFAULT_FORMAT, FileFormat } from "../types/files.js"; import { readFileParse } from "../utils/files.js"; +import { z } from "zod"; // Initialize Ajv type Schema = @@ -20,6 +22,11 @@ type Schema = | "url.json" | "social-profile.json" | "blockchain-address.json"; +type SchemaZod = "url.json"; +const schemaMap = { + "url.json": urlSchema, +} as const; +type SchemaMap = typeof schemaMap; const PROJECT_SCHEMA: Schema = "project.json"; const COLLECTION_SCHEMA: Schema = "collection.json"; const URL_SCHEMA: Schema = "url.json"; @@ -69,6 +76,36 @@ function validateObject(obj: any, schemaName: Schema): ValidationResult { return { valid: true, errors: {} }; } +function validateObjectZod( + obj: unknown, + schemaName: T, +): ValidationResult { + const schema = schemaMap[schemaName]; + + if (!schema) { + return { valid: false, errors: { schema: "Schema not found" } }; + } + + const result = schema.safeParse(obj); + + if (result.success) { + return { valid: true, errors: {} }; + } + + const errors: Record = {}; + const formatted = result.error.format(); + for (const [key, value] of Object.entries(formatted)) { + if (key === "_errors") continue; + + if (value && typeof value === "object" && "_errors" in value) { + const fieldErrors = (value as z.ZodFormattedError)._errors; + errors[key] = fieldErrors.join(", "); + } + } + + return { valid: false, errors }; +} + function safeCastObject(obj: any, schemaName: Schema): T { const result = validateObject(obj, schemaName); if (!result.valid) { From 68357e91b88cdfbce1fffd763210aca3b571ab29 Mon Sep 17 00:00:00 2001 From: haxry Date: Mon, 7 Jul 2025 20:07:49 +0000 Subject: [PATCH 2/2] add changes --- .lintstagedrc | 11 +- data/projects/m/magpieprotocol.yaml | 2 +- package-lock.json | 172 ++++++++++++++++++++++++++-- package.json | 6 +- src/actions/validate.ts | 2 +- src/index.ts | 4 +- src/resources/schema/url.ts | 7 -- src/transformations/importAgora.ts | 2 +- src/types/blockchain-address.ts | 126 +------------------- src/types/collection.ts | 25 +--- src/types/named.ts | 17 +-- src/types/project.ts | 165 +------------------------- src/types/social-profile.ts | 28 +---- src/types/url.ts | 17 +-- src/validator/index.ts | 75 ++++-------- 15 files changed, 216 insertions(+), 443 deletions(-) delete mode 100644 src/resources/schema/url.ts diff --git a/.lintstagedrc b/.lintstagedrc index 7c7f5a353..d20aa739b 100644 --- a/.lintstagedrc +++ b/.lintstagedrc @@ -3,11 +3,6 @@ "eslint --ignore-path .gitignore", "prettier --write" ], - "**/*.{md,json,yaml,yml}": [ - "prettier --write" - ], - "**/*.{py}": [ - "poetry run ruff check --fix", - "pnpm pyright" - ] -} \ No newline at end of file + "**/*.{md,json,yaml,yml}": ["prettier --write"], + "**/*.{py}": ["poetry run ruff check --fix", "pnpm pyright"] +} diff --git a/data/projects/m/magpieprotocol.yaml b/data/projects/m/magpieprotocol.yaml index d1c14e4a2..5eb1fd3f0 100644 --- a/data/projects/m/magpieprotocol.yaml +++ b/data/projects/m/magpieprotocol.yaml @@ -3,4 +3,4 @@ name: magpieprotocol display_name: fly trade description: fly trade aggregates liquidity from DEXs, DeFi protocols, and bridges to provide provably better pricing compared to leading aggregators, while delivering a seamless UX. websites: - - url: https://www.fly.trade/ \ No newline at end of file + - url: https://www.fly.trade/ diff --git a/package-lock.json b/package-lock.json index 805c8ff24..ffc81f426 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,29 +1,34 @@ { "name": "oss-directory", - "version": "0.0.14", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "oss-directory", - "version": "0.0.14", + "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@ethereum-attestation-service/eas-sdk": "^1.3.7", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "chalk": "^5.3.0", + "csv": "^6.3.9", "dayjs": "^1.11.10", "dotenv": "^16.3.1", "ethers": "^6.9.0", "glob": "^10.3.10", + "json-schema-to-zod": "^2.6.1", "lodash": "^4.17.21", + "mkdirp": "^3.0.1", + "pyright": "^1.1.369", "simple-git": "^3.21.0", "tmp-promise": "^3.0.3", "ts-adt": "^2.1.2", "winston": "^3.11.0", "yaml": "^2.3.4", - "yargs": "^17.7.2" + "yargs": "^17.7.2", + "zod": "^3.25.67" }, "bin": { "oss-directory": "dist/cli.js" @@ -4209,6 +4214,39 @@ "node": ">= 8" } }, + "node_modules/csv": { + "version": "6.3.11", + "resolved": "https://registry.npmjs.org/csv/-/csv-6.3.11.tgz", + "integrity": "sha512-a8bhT76Q546jOElHcTrkzWY7Py925mfLO/jqquseH61ThOebYwOjLbWHBqdRB4K1VpU36sTyIei6Jwj7QdEZ7g==", + "license": "MIT", + "dependencies": { + "csv-generate": "^4.4.2", + "csv-parse": "^5.6.0", + "csv-stringify": "^6.5.2", + "stream-transform": "^3.3.3" + }, + "engines": { + "node": ">= 0.1.90" + } + }, + "node_modules/csv-generate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.4.2.tgz", + "integrity": "sha512-W6nVsf+rz0J3yo9FOjeer7tmzBJKaTTxf7K0uw6GZgRocZYPVpuSWWa5/aoWWrjQZj4/oNIKTYapOM7hiNjVMA==", + "license": "MIT" + }, + "node_modules/csv-parse": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.6.0.tgz", + "integrity": "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==", + "license": "MIT" + }, + "node_modules/csv-stringify": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.5.2.tgz", + "integrity": "sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==", + "license": "MIT" + }, "node_modules/d": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", @@ -7579,6 +7617,19 @@ "node": "*" } }, + "node_modules/json-schema-to-typescript/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/json-schema-to-typescript/node_modules/prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", @@ -7594,6 +7645,15 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/json-schema-to-zod": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/json-schema-to-zod/-/json-schema-to-zod-2.6.1.tgz", + "integrity": "sha512-uiHmWH21h9FjKJkRBntfVGTLpYlCZ1n98D0izIlByqQLqpmkQpNTBtfbdP04Na6+43lgsvrShFh2uWLkQDKJuQ==", + "license": "ISC", + "bin": { + "json-schema-to-zod": "dist/cjs/cli.js" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -8379,15 +8439,18 @@ } }, "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", "bin": { - "mkdirp": "bin/cmd.js" + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mnemonist": { @@ -9093,6 +9156,22 @@ } ] }, + "node_modules/pyright": { + "version": "1.1.402", + "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.402.tgz", + "integrity": "sha512-DwzfZFTlqg9j7VDvwSIORmTsYL8awHMce8DaNV7n+M3KjD0wFEWBQrxSUX6N0pv2BJcKe1PKx+8x9yfJRQvqKQ==", + "license": "MIT", + "bin": { + "pyright": "index.js", + "pyright-langserver": "langserver.index.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9770,6 +9849,12 @@ "duplexer": "~0.1.1" } }, + "node_modules/stream-transform": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.3.3.tgz", + "integrity": "sha512-dALXrXe+uq4aO5oStdHKlfCM/b3NBdouigvxVPxCdrMRAU6oHh3KNss20VbTPQNQmjAHzZGKGe66vgwegFEIog==", + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -10746,6 +10831,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.75", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.75.tgz", + "integrity": "sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -13765,6 +13859,32 @@ "which": "^2.0.1" } }, + "csv": { + "version": "6.3.11", + "resolved": "https://registry.npmjs.org/csv/-/csv-6.3.11.tgz", + "integrity": "sha512-a8bhT76Q546jOElHcTrkzWY7Py925mfLO/jqquseH61ThOebYwOjLbWHBqdRB4K1VpU36sTyIei6Jwj7QdEZ7g==", + "requires": { + "csv-generate": "^4.4.2", + "csv-parse": "^5.6.0", + "csv-stringify": "^6.5.2", + "stream-transform": "^3.3.3" + } + }, + "csv-generate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.4.2.tgz", + "integrity": "sha512-W6nVsf+rz0J3yo9FOjeer7tmzBJKaTTxf7K0uw6GZgRocZYPVpuSWWa5/aoWWrjQZj4/oNIKTYapOM7hiNjVMA==" + }, + "csv-parse": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.6.0.tgz", + "integrity": "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==" + }, + "csv-stringify": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.5.2.tgz", + "integrity": "sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==" + }, "d": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", @@ -16268,6 +16388,12 @@ "brace-expansion": "^1.1.7" } }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", @@ -16276,6 +16402,11 @@ } } }, + "json-schema-to-zod": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/json-schema-to-zod/-/json-schema-to-zod-2.6.1.tgz", + "integrity": "sha512-uiHmWH21h9FjKJkRBntfVGTLpYlCZ1n98D0izIlByqQLqpmkQpNTBtfbdP04Na6+43lgsvrShFh2uWLkQDKJuQ==" + }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -16834,10 +16965,9 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" }, "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==" }, "mnemonist": { "version": "0.38.5", @@ -17349,6 +17479,14 @@ "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true }, + "pyright": { + "version": "1.1.402", + "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.402.tgz", + "integrity": "sha512-DwzfZFTlqg9j7VDvwSIORmTsYL8awHMce8DaNV7n+M3KjD0wFEWBQrxSUX6N0pv2BJcKe1PKx+8x9yfJRQvqKQ==", + "requires": { + "fsevents": "~2.3.3" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -17846,6 +17984,11 @@ "duplexer": "~0.1.1" } }, + "stream-transform": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.3.3.tgz", + "integrity": "sha512-dALXrXe+uq4aO5oStdHKlfCM/b3NBdouigvxVPxCdrMRAU6oHh3KNss20VbTPQNQmjAHzZGKGe66vgwegFEIog==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -18531,6 +18674,11 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zod": { + "version": "3.25.75", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.75.tgz", + "integrity": "sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==" } } } diff --git a/package.json b/package.json index c1bed9f28..c62c246c3 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,8 @@ "start": "node --loader ts-node/esm src/cli.ts", "test": "NODE_OPTIONS=\"--no-warnings --experimental-vm-modules\" jest --passWithNoTests", "transform": "pnpm start run-transformation", - "types": "pnpm types:json && pnpm types:mv", - "types:json": "pnpm json2ts -i './src/resources/schema/' -o 'src/types' --cwd './src/resources/schema'", - "types:mv": "find src/types/ -name '*.d.ts' -type f | sed s/\\.d.ts$// | xargs -I _ bash -c 'mv _.d.ts _.ts'", + "types": "pnpm types:zod", + "types:zod": "pnpm json-schema-to-zod -i src/resources/schema/blockchain-address.json -o src/types/blockchain-address.ts -n BlockchainAddressSchema -t BlockchainAddress --withJsdocs && pnpm json-schema-to-zod -i src/resources/schema/collection.json -o src/types/collection.ts -n CollectionSchema -t Collection --withJsdocs && pnpm json-schema-to-zod -i src/resources/schema/named.json -o src/types/named.ts -n NamedSchema -t Named --withJsdocs && pnpm json-schema-to-zod -i src/resources/schema/project.json -o src/types/project.ts -n ProjectSchema -t Project --withJsdocs && pnpm json-schema-to-zod -i src/resources/schema/social-profile.json -o src/types/social-profile.ts -n SocialProfileSchema -t SocialProfile --withJsdocs && pnpm json-schema-to-zod -i src/resources/schema/url.json -o src/types/url.ts -n UrlSchema -t URL --withJsdocs", "validate": "pnpm validate:collections && pnpm validate:projects", "validate:collections": "pnpm start validate-collections", "validate:projects": "pnpm start validate-projects", @@ -71,6 +70,7 @@ "dotenv": "^16.3.1", "ethers": "^6.9.0", "glob": "^10.3.10", + "json-schema-to-zod": "^2.6.1", "lodash": "^4.17.21", "mkdirp": "^3.0.1", "pyright": "^1.1.369", diff --git a/src/actions/validate.ts b/src/actions/validate.ts index ae79fb444..33c88c641 100644 --- a/src/actions/validate.ts +++ b/src/actions/validate.ts @@ -126,7 +126,7 @@ export async function validateProjects(args: ValidateArgs) { project.defillama?.forEach((x) => addKey(x.url, file)); // Check that all blockchain addresses belong to a single project file project.blockchain?.forEach((x) => - x.networks.forEach((n) => addKey(`${n}:${x.address}`, file)), + x.networks.forEach((n: any) => addKey(`${n}:${x.address}`, file)), ); } catch (e) { console.error("Error validating ", file); diff --git a/src/index.ts b/src/index.ts index 12fa405a3..178d376a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ -import { Project, URL, BlockchainAddress } from "./types/project.js"; +import { Project } from "./types/project.js"; +import { URL } from "./types/url.js"; +import { BlockchainAddress } from "./types/blockchain-address.js"; import { Collection } from "./types/collection.js"; import { BlockchainNetwork, BlockchainTag } from "./types/custom.js"; export { diff --git a/src/resources/schema/url.ts b/src/resources/schema/url.ts deleted file mode 100644 index 2d9968821..000000000 --- a/src/resources/schema/url.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from "zod"; - -export const urlSchema = z.object({ - url: z.string().url(), -}); - -export type URL = z.infer; diff --git a/src/transformations/importAgora.ts b/src/transformations/importAgora.ts index 675c8cc4f..3201326ca 100644 --- a/src/transformations/importAgora.ts +++ b/src/transformations/importAgora.ts @@ -243,7 +243,7 @@ async function readInput() { ...(p.github ? p.github.map((x) => x.url) : []), ...(p.npm ? p.npm.map((x) => x.url) : []), ...(p.websites ? p.websites.map((x) => x.url) : []), - ...(p.social?.twitter ? p.social.twitter.map((x) => x.url) : []), + ...(p.social?.twitter ? p.social.twitter.map((x: any) => x.url) : []), ]; // Look for any intersection return ( diff --git a/src/types/blockchain-address.ts b/src/types/blockchain-address.ts index c6c1b67ed..f0e1b70bd 100644 --- a/src/types/blockchain-address.ts +++ b/src/types/blockchain-address.ts @@ -1,123 +1,5 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ +import { z } from "zod" -/** - * An address on a blockchain - */ -export interface BlockchainAddress { - address: string; - /** - * @minItems 1 - */ - tags: [ - ( - | "bridge" - | "contract" - | "creator" - | "deployer" - | "eoa" - | "factory" - | "proxy" - | "safe" - | "wallet" - ), - ...( - | "bridge" - | "contract" - | "creator" - | "deployer" - | "eoa" - | "factory" - | "proxy" - | "safe" - | "wallet" - )[], - ]; - /** - * @minItems 1 - */ - networks: [ - ( - | "any_evm" - | "arbitrum_one" - | "automata" - | "base" - | "bob" - | "cyber" - | "frax" - | "ham" - | "ink" - | "kroma" - | "linea" - | "lisk" - | "lyra" - | "mainnet" - | "mantle" - | "matic" - | "metal" - | "mint" - | "mode" - | "optimism" - | "orderly" - | "pgn" - | "polynomial" - | "polygon_zkevm" - | "race" - | "redstone" - | "scroll" - | "shape" - | "soneium" - | "swan" - | "swell" - | "unichain" - | "worldchain" - | "xterio" - | "zksync_era" - | "zora" - ), - ...( - | "any_evm" - | "arbitrum_one" - | "automata" - | "base" - | "bob" - | "cyber" - | "frax" - | "ham" - | "ink" - | "kroma" - | "linea" - | "lisk" - | "lyra" - | "mainnet" - | "mantle" - | "matic" - | "metal" - | "mint" - | "mode" - | "optimism" - | "orderly" - | "pgn" - | "polynomial" - | "polygon_zkevm" - | "race" - | "redstone" - | "scroll" - | "shape" - | "soneium" - | "swan" - | "swell" - | "unichain" - | "worldchain" - | "xterio" - | "zksync_era" - | "zora" - )[], - ]; - name?: string; - [k: string]: unknown; -} +/**An address on a blockchain*/ +export const BlockchainAddressSchema = z.object({ "address": z.string(), "tags": z.array(z.enum(["bridge","contract","creator","deployer","eoa","factory","proxy","safe","wallet"])).min(1), "networks": z.array(z.enum(["any_evm","arbitrum_one","automata","base","bob","cyber","frax","ham","ink","kroma","linea","lisk","lyra","mainnet","mantle","matic","metal","mint","mode","optimism","orderly","pgn","polynomial","polygon_zkevm","race","redstone","scroll","shape","soneium","swan","swell","unichain","worldchain","xterio","zksync_era","zora"])).min(1), "name": z.string().optional() }).describe("An address on a blockchain") +export type BlockchainAddress = z.infer diff --git a/src/types/collection.ts b/src/types/collection.ts index 18670c586..93198206d 100644 --- a/src/types/collection.ts +++ b/src/types/collection.ts @@ -1,22 +1,5 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ +import { z } from "zod" -/** - * A collection of projects - */ -export interface Collection { - version: number; - name: string; - display_name: string; - description?: string; - /** - * @minItems 1 - */ - projects: [string, ...string[]]; - comments?: string[]; - [k: string]: unknown; -} +/**A collection of projects*/ +export const CollectionSchema = z.object({ "version": z.number(), "name": z.string(), "display_name": z.string(), "description": z.string().optional(), "projects": z.array(z.string()).min(1), "comments": z.array(z.string()).optional() }).describe("A collection of projects") +export type Collection = z.infer diff --git a/src/types/named.ts b/src/types/named.ts index caaeda4e0..ef357790a 100644 --- a/src/types/named.ts +++ b/src/types/named.ts @@ -1,14 +1,5 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ +import { z } from "zod" -/** - * A generic named object - */ -export interface NamedEntity { - name: string; - [k: string]: unknown; -} +/**A generic named object*/ +export const NamedSchema = z.object({ "name": z.string() }).describe("A generic named object") +export type Named = z.infer diff --git a/src/types/project.ts b/src/types/project.ts index 6686b9a5b..e54d5cfef 100644 --- a/src/types/project.ts +++ b/src/types/project.ts @@ -1,162 +1,5 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ +import { z } from "zod" -/** - * A project is a collection of artifacts - */ -export interface Project { - version: number; - name: string; - display_name: string; - description?: string; - websites?: URL[]; - social?: SocialProfile; - github?: URL[]; - npm?: URL[]; - crates?: URL[]; - pypi?: URL[]; - go?: URL[]; - open_collective?: URL[]; - blockchain?: BlockchainAddress[]; - defillama?: URL[]; - comments?: string[]; - [k: string]: unknown; -} -/** - * A generic URL - */ -export interface URL { - url: string; - [k: string]: unknown; -} -/** - * All social profile - */ -export interface SocialProfile { - farcaster?: URL[]; - medium?: URL[]; - mirror?: URL[]; - telegram?: URL[]; - twitter?: URL[]; - [k: string]: unknown; -} -/** - * An address on a blockchain - */ -export interface BlockchainAddress { - address: string; - /** - * @minItems 1 - */ - tags: [ - ( - | "bridge" - | "contract" - | "creator" - | "deployer" - | "eoa" - | "factory" - | "proxy" - | "safe" - | "wallet" - ), - ...( - | "bridge" - | "contract" - | "creator" - | "deployer" - | "eoa" - | "factory" - | "proxy" - | "safe" - | "wallet" - )[], - ]; - /** - * @minItems 1 - */ - networks: [ - ( - | "any_evm" - | "arbitrum_one" - | "automata" - | "base" - | "bob" - | "cyber" - | "frax" - | "ham" - | "ink" - | "kroma" - | "linea" - | "lisk" - | "lyra" - | "mainnet" - | "mantle" - | "matic" - | "metal" - | "mint" - | "mode" - | "optimism" - | "orderly" - | "pgn" - | "polynomial" - | "polygon_zkevm" - | "race" - | "redstone" - | "scroll" - | "shape" - | "soneium" - | "swan" - | "swell" - | "unichain" - | "worldchain" - | "xterio" - | "zksync_era" - | "zora" - ), - ...( - | "any_evm" - | "arbitrum_one" - | "automata" - | "base" - | "bob" - | "cyber" - | "frax" - | "ham" - | "ink" - | "kroma" - | "linea" - | "lisk" - | "lyra" - | "mainnet" - | "mantle" - | "matic" - | "metal" - | "mint" - | "mode" - | "optimism" - | "orderly" - | "pgn" - | "polynomial" - | "polygon_zkevm" - | "race" - | "redstone" - | "scroll" - | "shape" - | "soneium" - | "swan" - | "swell" - | "unichain" - | "worldchain" - | "xterio" - | "zksync_era" - | "zora" - )[], - ]; - name?: string; - [k: string]: unknown; -} +/**A project is a collection of artifacts*/ +export const ProjectSchema = z.object({ "version": z.number(), "name": z.string(), "display_name": z.string(), "description": z.string().optional(), "websites": z.array(z.any()).optional(), "social": z.any().optional(), "github": z.array(z.any()).optional(), "npm": z.array(z.any()).optional(), "crates": z.array(z.any()).optional(), "pypi": z.array(z.any()).optional(), "go": z.array(z.any()).optional(), "open_collective": z.array(z.any()).optional(), "blockchain": z.array(z.any()).optional(), "defillama": z.array(z.any()).optional(), "comments": z.array(z.string()).optional() }).describe("A project is a collection of artifacts") +export type Project = z.infer diff --git a/src/types/social-profile.ts b/src/types/social-profile.ts index c13cad140..84d14f31c 100644 --- a/src/types/social-profile.ts +++ b/src/types/social-profile.ts @@ -1,25 +1,5 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ +import { z } from "zod" -/** - * All social profile - */ -export interface SocialProfile { - farcaster?: URL[]; - medium?: URL[]; - mirror?: URL[]; - telegram?: URL[]; - twitter?: URL[]; - [k: string]: unknown; -} -/** - * A generic URL - */ -export interface URL { - url: string; - [k: string]: unknown; -} +/**All social profile*/ +export const SocialProfileSchema = z.object({ "farcaster": z.array(z.any()).optional(), "medium": z.array(z.any()).optional(), "mirror": z.array(z.any()).optional(), "telegram": z.array(z.any()).optional(), "twitter": z.array(z.any()).optional() }).describe("All social profile") +export type SocialProfile = z.infer diff --git a/src/types/url.ts b/src/types/url.ts index eaf936953..cc3b76026 100644 --- a/src/types/url.ts +++ b/src/types/url.ts @@ -1,14 +1,5 @@ -/* eslint-disable */ -/** - * This file was automatically generated by json-schema-to-typescript. - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, - * and run json-schema-to-typescript to regenerate this file. - */ +import { z } from "zod" -/** - * A generic URL - */ -export interface URL { - url: string; - [k: string]: unknown; -} +/**A generic URL*/ +export const UrlSchema = z.object({ "url": z.string().url() }).describe("A generic URL") +export type URL = z.infer diff --git a/src/validator/index.ts b/src/validator/index.ts index 0d19bae7d..fd909d3aa 100644 --- a/src/validator/index.ts +++ b/src/validator/index.ts @@ -1,45 +1,33 @@ -import Ajv from "ajv"; -import addFormats from "ajv-formats"; -import projectSchema from "../resources/schema/project.json" with { type: "json" }; -import collectionSchema from "../resources/schema/collection.json" with { type: "json" }; -//import urlSchema from "../resources/schema/url.json" with { type: "json" }; -import socialProfileSchema from "../resources/schema/social-profile.json" with { type: "json" }; -import blockchainAddressSchema from "../resources/schema/blockchain-address.json" with { type: "json" }; -import { Project } from "../types/project.js"; -import { Collection } from "../types/collection.js"; -//import { URL } from "../types/url.js"; -import { URL, urlSchema } from "../resources/schema/url.js"; -import { SocialProfile } from "../types/social-profile.js"; -import { BlockchainAddress } from "../types/blockchain-address.js"; +import { Project, ProjectSchema } from "../types/project.js"; +import { Collection, CollectionSchema } from "../types/collection.js"; +import { URL, UrlSchema } from "../types/url.js"; +import { SocialProfile, SocialProfileSchema } from "../types/social-profile.js"; +import { + BlockchainAddress, + BlockchainAddressSchema, +} from "../types/blockchain-address.js"; import { DEFAULT_FORMAT, FileFormat } from "../types/files.js"; import { readFileParse } from "../utils/files.js"; import { z } from "zod"; -// Initialize Ajv type Schema = | "project.json" | "collection.json" | "url.json" | "social-profile.json" | "blockchain-address.json"; -type SchemaZod = "url.json"; const schemaMap = { - "url.json": urlSchema, + "url.json": UrlSchema, + "project.json": ProjectSchema, + "collection.json": CollectionSchema, + "social-profile.json": SocialProfileSchema, + "blockchain-address.json": BlockchainAddressSchema, } as const; -type SchemaMap = typeof schemaMap; const PROJECT_SCHEMA: Schema = "project.json"; const COLLECTION_SCHEMA: Schema = "collection.json"; const URL_SCHEMA: Schema = "url.json"; const SOCIAL_PROFILE_SCHEMA: Schema = "social-profile.json"; const BLOCKCHAIN_ADDRESS_SCHEMA: Schema = "blockchain-address.json"; -const ajv = new Ajv.default({ allErrors: true }); // options can be passed, e.g. {allErrors: true} -addFormats.default(ajv); -ajv.addSchema(projectSchema, PROJECT_SCHEMA); -ajv.addSchema(collectionSchema, COLLECTION_SCHEMA); -ajv.addSchema(urlSchema, URL_SCHEMA); -ajv.addSchema(socialProfileSchema, SOCIAL_PROFILE_SCHEMA); -ajv.addSchema(blockchainAddressSchema, BLOCKCHAIN_ADDRESS_SCHEMA); - /** * The result of a validation. * @property valid - Whether the data is valid. @@ -55,31 +43,8 @@ type ValidationResult = { * @param obj- path to the file * @returns A `ValidationResult` object indicating whether the data is valid and any errors that were found. */ -function validateObject(obj: any, schemaName: Schema): ValidationResult { - const validate = ajv.getSchema(schemaName); - - // Check missing validator - if (!validate) { - return { valid: false, errors: { schema: "Schema not found" } }; - } else if (!validate(obj)) { - // Enumerate validation errors - const errors: Record = {}; - for (const e of validate.errors || []) { - const key = e.params.missingProperty || "other"; - if (key && e.message) { - errors[key] = e.message; - } - } - return { valid: false, errors }; - } - - return { valid: true, errors: {} }; -} -function validateObjectZod( - obj: unknown, - schemaName: T, -): ValidationResult { +function validateObject(obj: unknown, schemaName: Schema): ValidationResult { const schema = schemaMap[schemaName]; if (!schema) { @@ -93,7 +58,7 @@ function validateObjectZod( } const errors: Record = {}; - const formatted = result.error.format(); + const formatted = result.error; for (const [key, value] of Object.entries(formatted)) { if (key === "_errors") continue; @@ -107,7 +72,7 @@ function validateObjectZod( } function safeCastObject(obj: any, schemaName: Schema): T { - const result = validateObject(obj, schemaName); + const result = validateObject(obj, schemaName); if (!result.valid) { console.log("Invalid ", schemaName); console.log(JSON.stringify(obj, null, 2)); @@ -142,7 +107,7 @@ async function readFileToObject( * @returns A `ValidationResult` object indicating whether the data is valid and any errors that were found. */ function validateProject(obj: any): ValidationResult { - return validateObject(obj, PROJECT_SCHEMA); + return validateObject(obj, PROJECT_SCHEMA); } /** @@ -151,7 +116,7 @@ function validateProject(obj: any): ValidationResult { * @returns A `ValidationResult` object indicating whether the data is valid and any errors that were found. */ function validateCollection(obj: any): ValidationResult { - return validateObject(obj, COLLECTION_SCHEMA); + return validateObject(obj, COLLECTION_SCHEMA); } /** @@ -160,7 +125,7 @@ function validateCollection(obj: any): ValidationResult { * @returns A `ValidationResult` object indicating whether the data is valid and any errors that were found. */ function validateUrl(obj: any): ValidationResult { - return validateObject(obj, URL_SCHEMA); + return validateObject(obj, URL_SCHEMA); } /** @@ -169,7 +134,7 @@ function validateUrl(obj: any): ValidationResult { * @returns A `ValidationResult` object indicating whether the data is valid and any errors that were found. */ function validateBlockchainAddress(obj: any): ValidationResult { - return validateObject(obj, BLOCKCHAIN_ADDRESS_SCHEMA); + return validateObject(obj, BLOCKCHAIN_ADDRESS_SCHEMA); } /**