Drastically reduce the bundle size by replacing Typebox with Valibot.
This commit is contained in:
parent
aa831228ea
commit
7c40e4af5f
|
@ -9,7 +9,7 @@
|
|||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "^0.32.35"
|
||||
"valibot": "^0.36.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
|
@ -36,11 +36,6 @@
|
|||
"npm": ">=6.14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.32.35",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.32.35.tgz",
|
||||
"integrity": "sha512-Ul3YyOTU++to8cgNkttakC0dWvpERr6RYoHO2W47DLbFvrwBDJUY31B1sImH6JZSYc4Kt4PyHtoPNu+vL2r2dA=="
|
||||
},
|
||||
"node_modules/@tsconfig/strictest": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/strictest/-/strictest-2.0.5.tgz",
|
||||
|
@ -59,6 +54,11 @@
|
|||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/valibot": {
|
||||
"version": "0.36.0",
|
||||
"resolved": "https://registry.npmjs.org/valibot/-/valibot-0.36.0.tgz",
|
||||
"integrity": "sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,6 @@
|
|||
"typescript": "^5.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "^0.32.35"
|
||||
"valibot": "^0.36.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { ValueErrorIterator } from "@sinclair/typebox/build/cjs/errors"
|
||||
import { type InferIssue } from "valibot"
|
||||
|
||||
type ValidationError = {
|
||||
path: string
|
||||
expected: string
|
||||
received: string
|
||||
message: string
|
||||
path: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,27 +27,11 @@ export type CodexError = {
|
|||
errors: ValidationError[]
|
||||
}
|
||||
|
||||
export const CodexValidationErrors = {
|
||||
map(iterator: ValueErrorIterator) {
|
||||
let error
|
||||
const errors = []
|
||||
export const CodexValibotIssuesMap = (issues: InferIssue<any>[]) => issues.map(i => ({
|
||||
expected: i.expected,
|
||||
received: i.received,
|
||||
message: i.message,
|
||||
path: i.path.map((item: { key: string }) => item.key).join('.')
|
||||
}))
|
||||
|
||||
while (error = iterator.First()) {
|
||||
errors.push({
|
||||
path: error.path,
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
}
|
||||
|
||||
// export class CodexError extends Error {
|
||||
// readonly status: number | null
|
||||
|
||||
// constructor(message: string, status: number | null = null) {
|
||||
// super(message)
|
||||
// this.status = status
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -56,13 +56,29 @@ function missingNumberValidationError(field: string) {
|
|||
message: "Cannot validate the input",
|
||||
errors: [
|
||||
{
|
||||
path: "/" + field,
|
||||
message: "Expected required property"
|
||||
path: field,
|
||||
expected: 'number',
|
||||
message: 'Invalid type: Expected number but received undefined',
|
||||
received: 'undefined'
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extraValidationError(field: string, value: unknown) {
|
||||
return {
|
||||
error: true,
|
||||
data: {
|
||||
type: "validation",
|
||||
message: "Cannot validate the input",
|
||||
errors: [
|
||||
{
|
||||
path: "/" + field,
|
||||
message: "Expected number"
|
||||
}
|
||||
path: field,
|
||||
expected: 'never',
|
||||
message: `Invalid type: Expected never but received "${value}"`,
|
||||
received: `"${value}"`
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -76,19 +92,17 @@ function missingStringValidationError(field: string) {
|
|||
message: "Cannot validate the input",
|
||||
errors: [
|
||||
{
|
||||
path: "/" + field,
|
||||
message: "Expected required property"
|
||||
},
|
||||
{
|
||||
path: "/" + field,
|
||||
message: "Expected string"
|
||||
path: field,
|
||||
expected: 'string',
|
||||
message: 'Invalid type: Expected string but received undefined',
|
||||
received: 'undefined'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mistypeNumberValidationError(field: string) {
|
||||
function mistypeNumberValidationError(field: string, value: string) {
|
||||
return {
|
||||
error: true,
|
||||
data: {
|
||||
|
@ -96,8 +110,10 @@ function mistypeNumberValidationError(field: string) {
|
|||
message: "Cannot validate the input",
|
||||
errors: [
|
||||
{
|
||||
path: "/" + field,
|
||||
message: "Expected number"
|
||||
path: field,
|
||||
expected: 'number',
|
||||
message: `Invalid type: Expected number but received "${value}"`,
|
||||
received: `"${value}"`
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -112,8 +128,10 @@ function minNumberValidationError(field: string, min: number) {
|
|||
message: "Cannot validate the input",
|
||||
errors: [
|
||||
{
|
||||
path: "/" + field,
|
||||
message: "Expected number to be greater or equal to " + min
|
||||
path: field,
|
||||
expected: '>=' + min,
|
||||
message: 'Invalid value: Expected >=1 but received 0',
|
||||
received: '0'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -143,7 +161,7 @@ describe("marketplace", () => {
|
|||
assert.deepStrictEqual(response, missingNumberValidationError("totalSize"));
|
||||
});
|
||||
|
||||
it("returns an error when trying to create an availability with an invalid number valid", async () => {
|
||||
it.only("returns an error when trying to create an availability with an invalid number valid", async () => {
|
||||
const response = await marketplace.createAvailability({
|
||||
duration: 3000,
|
||||
maxCollateral: 1,
|
||||
|
@ -151,7 +169,7 @@ describe("marketplace", () => {
|
|||
totalSize: "abc"
|
||||
} as any)
|
||||
|
||||
assert.deepStrictEqual(response, mistypeNumberValidationError("totalSize"));
|
||||
assert.deepStrictEqual(response, mistypeNumberValidationError("totalSize", "abc"));
|
||||
});
|
||||
|
||||
it("returns an error when trying to create an availability with zero total size", async () => {
|
||||
|
@ -172,6 +190,8 @@ describe("marketplace", () => {
|
|||
minPrice: 100,
|
||||
} as any)
|
||||
|
||||
console.info(response.error)
|
||||
|
||||
assert.deepStrictEqual(response, missingNumberValidationError("duration"));
|
||||
});
|
||||
|
||||
|
@ -206,6 +226,18 @@ describe("marketplace", () => {
|
|||
assert.deepStrictEqual(response, missingNumberValidationError("maxCollateral"));
|
||||
});
|
||||
|
||||
it("returns an error when trying to create an availability with an extra field", async () => {
|
||||
const response = await marketplace.createAvailability({
|
||||
maxCollateral: 1,
|
||||
totalSize: 3000,
|
||||
minPrice: 100,
|
||||
duration: 100,
|
||||
hello: "world"
|
||||
} as any)
|
||||
|
||||
assert.deepStrictEqual(response, extraValidationError("hello", "world"));
|
||||
});
|
||||
|
||||
it("returns a response when the request succeed", async (t) => {
|
||||
const data = { ...createAvailability(), freeSize: 1000 }
|
||||
|
||||
|
@ -218,8 +250,7 @@ describe("marketplace", () => {
|
|||
totalSize: 3000,
|
||||
minPrice: 100,
|
||||
duration: 100,
|
||||
hello: "world"
|
||||
} as any)
|
||||
})
|
||||
|
||||
assert.deepStrictEqual(response, { error: false, data });
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Value } from '@sinclair/typebox/value'
|
||||
import * as v from 'valibot'
|
||||
import { Api } from "../api/config"
|
||||
import { CodexValidationErrors } from '../errors/errors'
|
||||
import { CodexValibotIssuesMap } from '../errors/errors'
|
||||
import { Fetch } from "../fetch-safe/fetch-safe"
|
||||
import { SafeValue } from "../values/values"
|
||||
import { CodexAvailability, CodexAvailabilityCreateResponse, CodexCreateAvailabilityInput, CodexCreateStorageRequestInput, CodexCreateStorageRequestResponse, CodexPurchase, CodexReservation, CodexSlot, CodexUpdateAvailabilityInput } from "./types"
|
||||
|
@ -50,17 +50,15 @@ export class Marketplace {
|
|||
*/
|
||||
async createAvailability(input: CodexCreateAvailabilityInput)
|
||||
: Promise<SafeValue<CodexAvailabilityCreateResponse>> {
|
||||
const cleaned = Value.Clean(CodexCreateAvailabilityInput, input)
|
||||
|
||||
if (!Value.Check(CodexCreateAvailabilityInput, cleaned)) {
|
||||
const iterator = Value.Errors(CodexCreateAvailabilityInput, cleaned)
|
||||
const result = v.safeParse(CodexCreateAvailabilityInput, input)
|
||||
|
||||
if (!result.success) {
|
||||
return {
|
||||
error: true,
|
||||
data: {
|
||||
type: "validation",
|
||||
message: "Cannot validate the input",
|
||||
errors: CodexValidationErrors.map(iterator)
|
||||
errors: CodexValibotIssuesMap(result.issues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +70,7 @@ export class Marketplace {
|
|||
headers: {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(cleaned)
|
||||
body: JSON.stringify(result.output)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -81,29 +79,27 @@ export class Marketplace {
|
|||
* Existing Requests linked to this Availability will continue as is.
|
||||
*/
|
||||
async updateAvailability(input: CodexUpdateAvailabilityInput): Promise<SafeValue<CodexAvailability>> {
|
||||
const cleaned = Value.Clean(CodexUpdateAvailabilityInput, input)
|
||||
|
||||
if (!Value.Check(CodexUpdateAvailabilityInput, cleaned)) {
|
||||
const iterator = Value.Errors(CodexUpdateAvailabilityInput, cleaned)
|
||||
const result = v.safeParse(CodexUpdateAvailabilityInput, input)
|
||||
|
||||
if (!result.success) {
|
||||
return {
|
||||
error: true,
|
||||
data: {
|
||||
type: "validation",
|
||||
message: "Cannot validate the input",
|
||||
errors: CodexValidationErrors.map(iterator)
|
||||
errors: CodexValibotIssuesMap(result.issues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const url = this.url + Api.config.prefix + "/sales/availability/" + cleaned.id
|
||||
const url = this.url + Api.config.prefix + "/sales/availability/" + result.output.id
|
||||
|
||||
return Fetch.safe<CodexAvailability>(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(cleaned)
|
||||
body: JSON.stringify(result.output)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -144,22 +140,20 @@ export class Marketplace {
|
|||
* Creates a new request for storage.
|
||||
*/
|
||||
async createStorageRequest(input: CodexCreateStorageRequestInput): Promise<SafeValue<CodexCreateStorageRequestResponse>> {
|
||||
const cleaned = Value.Clean(CodexCreateStorageRequestInput, input)
|
||||
|
||||
if (!Value.Check(CodexCreateStorageRequestInput, cleaned)) {
|
||||
const iterator = Value.Errors(CodexCreateStorageRequestInput, cleaned)
|
||||
const result = v.safeParse(CodexCreateStorageRequestInput, input)
|
||||
|
||||
if (!result.success) {
|
||||
return {
|
||||
error: true,
|
||||
data: {
|
||||
type: "validation",
|
||||
message: "Cannot validate the input",
|
||||
errors: CodexValidationErrors.map(iterator)
|
||||
errors: CodexValibotIssuesMap(result.issues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { cid, ...body } = cleaned
|
||||
const { cid, ...body } = result.output
|
||||
const url = this.url + Api.config.prefix + "/storage/request/" + cid
|
||||
|
||||
return Fetch.safe<CodexCreateStorageRequestResponse>(url, {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Static, Type } from "@sinclair/typebox"
|
||||
import * as v from 'valibot';
|
||||
|
||||
export type CodexStorageRequest = {
|
||||
id: string
|
||||
|
@ -130,24 +130,24 @@ export type CodexAvailabilityCreateResponse = CodexAvailability & {
|
|||
freeSize: string
|
||||
}
|
||||
|
||||
export const CodexCreateAvailabilityInput = Type.Object({
|
||||
totalSize: Type.Number({ minimum: 1 }),
|
||||
duration: Type.Number({ minimum: 1 }),
|
||||
minPrice: Type.Number(),
|
||||
maxCollateral: Type.Number()
|
||||
export const CodexCreateAvailabilityInput = v.strictObject({
|
||||
totalSize: v.pipe(v.number(), v.minValue(1)),
|
||||
duration: v.pipe(v.number(), v.minValue(1)),
|
||||
minPrice: v.number(),
|
||||
maxCollateral: v.number(),
|
||||
})
|
||||
|
||||
export type CodexCreateAvailabilityInput = Static<typeof CodexCreateAvailabilityInput>
|
||||
export type CodexCreateAvailabilityInput = v.InferOutput<typeof CodexCreateAvailabilityInput>;
|
||||
|
||||
export const CodexUpdateAvailabilityInput = Type.Object({
|
||||
id: Type.String(),
|
||||
totalSize: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
duration: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
minPrice: Type.Optional(Type.Number()),
|
||||
maxCollateral: Type.Optional(Type.Number())
|
||||
export const CodexUpdateAvailabilityInput = v.strictObject({
|
||||
id: v.string(),
|
||||
totalSize: v.optional(v.pipe(v.number(), v.minValue(1))),
|
||||
duration: v.optional(v.pipe(v.number(), v.minValue(1))),
|
||||
minPrice: v.optional(v.number()),
|
||||
maxCollateral: v.optional(v.number()),
|
||||
})
|
||||
|
||||
export type CodexUpdateAvailabilityInput = Static<typeof CodexUpdateAvailabilityInput>
|
||||
export type CodexUpdateAvailabilityInput = v.InferOutput<typeof CodexUpdateAvailabilityInput>;
|
||||
|
||||
export type CodexReservation = {
|
||||
id: string
|
||||
|
@ -179,17 +179,17 @@ export type CodexPurchase = {
|
|||
request: CodexStorageRequest
|
||||
}
|
||||
|
||||
export const CodexCreateStorageRequestInput = Type.Object({
|
||||
cid: Type.String(),
|
||||
duration: Type.Number({ minimum: 1 }),
|
||||
reward: Type.Number(),
|
||||
proofProbability: Type.Number(),
|
||||
nodes: Type.Optional(Type.Number({ default: 1 })),
|
||||
tolerance: Type.Optional(Type.Number({ default: 0 })),
|
||||
expiry: Type.Number({ minimum: 1 }),
|
||||
collateral: Type.Number()
|
||||
export const CodexCreateStorageRequestInput = v.strictObject({
|
||||
cid: v.string(),
|
||||
duration: v.pipe(v.number(), v.minValue(1)),
|
||||
reward: v.number(),
|
||||
proofProbability: v.number(),
|
||||
nodes: v.optional(v.number(), 1),
|
||||
tolerance: v.optional(v.number(), 0),
|
||||
expiry: v.pipe(v.number(), v.minValue(1)),
|
||||
collateral: v.number(),
|
||||
})
|
||||
|
||||
export type CodexCreateStorageRequestInput = Static<typeof CodexCreateStorageRequestInput>
|
||||
export type CodexCreateStorageRequestInput = v.InferOutput<typeof CodexCreateStorageRequestInput>;
|
||||
|
||||
export type CodexCreateStorageRequestResponse = Omit<CodexCreateStorageRequestInput, "cid">
|
|
@ -11,5 +11,8 @@
|
|||
"DOM",
|
||||
],
|
||||
"outDir": "./dist",
|
||||
}
|
||||
// "module": "ES2015",
|
||||
// "moduleResolution": "Bundler"
|
||||
},
|
||||
"buildOptions": {}
|
||||
}
|
Loading…
Reference in New Issue