diff --git a/.github/actions/environment/action.yml b/.github/actions/environment/action.yml new file mode 100644 index 000000000..d9939a864 --- /dev/null +++ b/.github/actions/environment/action.yml @@ -0,0 +1,40 @@ +name: Setup environment +description: Setup environment with Node.js, Deno and pnpm + +runs: + using: composite + steps: + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + run_install: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: 'https://registry.npmjs.org' + cache: pnpm + + - name: Setup Deno + uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + + - name: Install dependencies + shell: bash + run: pnpm install + + - name: Create library build cache + id: library-build-cache + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/library/dist + key: library-build-cache-${{ hashFiles('library/src/**/*.ts', '!library/src/**/*.test.ts', '!library/src/**/*.test-d.ts') }} + + - name: Build library + if: steps.library-build-cache.outputs.cache-hit != 'true' + shell: bash + run: pnpm build + working-directory: library diff --git a/.github/actions/pnpm/action.yml b/.github/actions/pnpm/action.yml deleted file mode 100644 index 54f962c0c..000000000 --- a/.github/actions/pnpm/action.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Setup -description: Sets up Node.js, Deno and pnpm - -runs: - using: composite - steps: - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 9 - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - registry-url: 'https://registry.npmjs.org' - cache: pnpm - - - name: Setup Deno - uses: denoland/setup-deno@v2 - with: - deno-version: v2.x - - - name: Install dependencies - shell: bash - run: pnpm install diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c74e3d983..d79fccf1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,24 +11,21 @@ jobs: name: Install packages runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - library_build: - name: Build and publish library + library_preview: + name: Publish library via pkg.pr.new needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm - - name: Build - run: pnpm build - working-directory: library - - name: Publish + - name: Setup environment + uses: ./.github/actions/environment + - name: Publish library run: pnpx pkg-pr-new publish --compact --comment=update --pnpm working-directory: library @@ -37,10 +34,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: Prettier check run: pnpm format.check working-directory: library @@ -50,10 +47,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: ESLint check run: pnpm lint working-directory: library @@ -63,10 +60,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: Vitest tests run: pnpm test working-directory: library @@ -76,10 +73,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: Prettier check run: pnpm format.check working-directory: packages/to-json-schema @@ -89,10 +86,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: ESLint check run: pnpm lint working-directory: packages/to-json-schema @@ -102,10 +99,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: Vitest tests run: pnpm test working-directory: packages/to-json-schema @@ -115,10 +112,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: Prettier check run: pnpm format.check working-directory: website @@ -128,10 +125,10 @@ jobs: needs: install runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/pnpm + - name: Setup environment + uses: ./.github/actions/environment - name: ESLint check run: pnpm lint working-directory: website diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b6434a5c1..9498f2597 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,14 +18,14 @@ jobs: contents: read id-token: write steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm + - name: Setup environment uses: ./.github/actions/pnpm - - name: Build + - name: Build library run: pnpm build working-directory: library - - name: Publish + - name: Publish library run: pnpm publish --provenance --access public --no-git-checks working-directory: library continue-on-error: true @@ -40,9 +40,9 @@ jobs: contents: read id-token: write steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Publish + - name: Publish library run: npx jsr publish working-directory: library @@ -54,14 +54,14 @@ jobs: contents: read id-token: write steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm + - name: Setup environment uses: ./.github/actions/pnpm - - name: Build + - name: Build i18n run: pnpm build.npm working-directory: packages/i18n - - name: Publish + - name: Publish i18n run: pnpm publish --provenance --access public --no-git-checks working-directory: packages/i18n continue-on-error: true @@ -76,14 +76,14 @@ jobs: contents: read id-token: write steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm + - name: Setup environment uses: ./.github/actions/pnpm - - name: Build + - name: Build i18n run: pnpm build.jsr working-directory: packages/i18n - - name: Publish + - name: Publish i18n run: npx jsr publish --allow-dirty working-directory: packages/i18n @@ -95,14 +95,14 @@ jobs: contents: read id-token: write steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm + - name: Setup environment uses: ./.github/actions/pnpm - - name: Build + - name: Build to-json-schema run: pnpm build working-directory: packages/to-json-schema - - name: Publish + - name: Publish to-json-schema run: pnpm publish --provenance --access public --no-git-checks working-directory: packages/to-json-schema continue-on-error: true @@ -117,13 +117,10 @@ jobs: contents: read id-token: write steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm + - name: Setup environment uses: ./.github/actions/pnpm - - name: Build - run: pnpm build - working-directory: library - - name: Publish + - name: Publish to-json-schema run: npx jsr publish working-directory: packages/to-json-schema diff --git a/library/CHANGELOG.md b/library/CHANGELOG.md index a79195804..0fdfa77d5 100644 --- a/library/CHANGELOG.md +++ b/library/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to the library will be documented in this file. ## vX.X.X (Month DD, YYYY) - Add `message` method to overwrite local error message configuration of a schema (pull request #1103) +- Add `minProps` and `maxProps` validation action to validate number of object properties (pull request #1100) - Change implementation of `variant` and `variantAsync` schema to improve performance by aborting validation of discriminators early (pull request #1110) ## v1.0.0 (March 18, 2025) diff --git a/library/src/actions/index.ts b/library/src/actions/index.ts index 78c0f37bd..0f5a3e9fa 100644 --- a/library/src/actions/index.ts +++ b/library/src/actions/index.ts @@ -46,6 +46,7 @@ export * from './mapItems/index.ts'; export * from './maxBytes/index.ts'; export * from './maxGraphemes/index.ts'; export * from './maxLength/index.ts'; +export * from './maxProps/index.ts'; export * from './maxSize/index.ts'; export * from './maxValue/index.ts'; export * from './maxWords/index.ts'; @@ -54,6 +55,7 @@ export * from './mimeType/index.ts'; export * from './minBytes/index.ts'; export * from './minGraphemes/index.ts'; export * from './minLength/index.ts'; +export * from './minProps/index.ts'; export * from './minSize/index.ts'; export * from './minValue/index.ts'; export * from './minWords/index.ts'; diff --git a/library/src/actions/maxProps/index.ts b/library/src/actions/maxProps/index.ts new file mode 100644 index 000000000..67332df52 --- /dev/null +++ b/library/src/actions/maxProps/index.ts @@ -0,0 +1 @@ +export * from './maxProps.ts'; diff --git a/library/src/actions/maxProps/maxProps.test-d.ts b/library/src/actions/maxProps/maxProps.test-d.ts new file mode 100644 index 000000000..6f772bd3e --- /dev/null +++ b/library/src/actions/maxProps/maxProps.test-d.ts @@ -0,0 +1,51 @@ +import { describe, expectTypeOf, test } from 'vitest'; +import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts'; +import { + maxProps, + type MaxPropsAction, + type MaxPropsIssue, +} from './maxProps.ts'; + +describe('maxProps', () => { + type Input = Record; + + describe('should return action object', () => { + test('with undefined message', () => { + type Action = MaxPropsAction; + expectTypeOf(maxProps(10)).toEqualTypeOf(); + expectTypeOf( + maxProps(10, undefined) + ).toEqualTypeOf(); + }); + + test('with string message', () => { + expectTypeOf(maxProps(10, 'message')).toEqualTypeOf< + MaxPropsAction + >(); + }); + + test('with function message', () => { + expectTypeOf( + maxProps string>(10, () => 'message') + ).toEqualTypeOf string>>(); + }); + }); + + describe('should infer correct types', () => { + type Action = MaxPropsAction; + + test('of input', () => { + expectTypeOf>().toEqualTypeOf(); + }); + + test('of output', () => { + expectTypeOf>().toEqualTypeOf(); + }); + + test('of issue', () => { + expectTypeOf>().toEqualTypeOf< + MaxPropsIssue + >(); + }); + }); +}); diff --git a/library/src/actions/maxProps/maxProps.test.ts b/library/src/actions/maxProps/maxProps.test.ts new file mode 100644 index 000000000..f499d1f96 --- /dev/null +++ b/library/src/actions/maxProps/maxProps.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, test } from 'vitest'; +import { RecordIssue } from '../../schemas/index.ts'; +import { expectActionIssue, expectNoActionIssue } from '../../vitest/index.ts'; +import { + maxProps, + type MaxPropsAction, + type MaxPropsIssue, +} from './maxProps.ts'; + +describe('maxProps', () => { + type Input = Record; + + describe('should return action object', () => { + const baseAction: Omit, 'message'> = { + kind: 'validation', + type: 'max_props', + reference: maxProps, + expects: '<=3', + requirement: 3, + async: false, + '~run': expect.any(Function), + }; + + test('with undefined message', () => { + const action: MaxPropsAction = { + ...baseAction, + message: undefined, + }; + expect(maxProps(3)).toStrictEqual(action); + expect(maxProps(3, undefined)).toStrictEqual(action); + }); + + test('with string message', () => { + expect(maxProps(3, 'message')).toStrictEqual({ + ...baseAction, + message: 'message', + } satisfies MaxPropsAction); + }); + + test('with function message', () => { + const message = () => 'message'; + expect(maxProps(3, message)).toStrictEqual({ + ...baseAction, + message, + } satisfies MaxPropsAction); + }); + }); + + describe('should return dataset without issues', () => { + const action = maxProps(3); + + test('for untyped inputs', () => { + const issues: [RecordIssue] = [ + { + kind: 'schema', + type: 'record', + input: null, + expected: 'Object', + received: 'null', + message: 'message', + }, + ]; + expect( + action['~run']({ typed: false, value: null, issues }, {}) + ).toStrictEqual({ + typed: false, + value: null, + issues, + }); + }); + + test('for valid objects', () => { + expectNoActionIssue(action, [ + {}, + { foo: 1 }, + { foo: 1, bar: 2 }, + { foo: 1, bar: 2, baz: 3 }, + ]); + }); + }); + + describe('should return dataset with issues', () => { + const action = maxProps(3, 'message'); + const baseIssue: Omit, 'input' | 'received'> = { + kind: 'validation', + type: 'max_props', + expected: '<=3', + message: 'message', + requirement: 3, + }; + + test('for invalid objects', () => { + expectActionIssue( + action, + baseIssue, + [ + { foo: 1, bar: 2, baz: 3, qux: 4 }, + { foo: 1, bar: 2, baz: 3, qux: 4, quux: 5 }, + ], + (value) => `${Object.keys(value).length}` + ); + }); + }); +}); diff --git a/library/src/actions/maxProps/maxProps.ts b/library/src/actions/maxProps/maxProps.ts new file mode 100644 index 000000000..1959fa2ce --- /dev/null +++ b/library/src/actions/maxProps/maxProps.ts @@ -0,0 +1,128 @@ +import type { + BaseIssue, + BaseValidation, + ErrorMessage, +} from '../../types/index.ts'; +import { _addIssue } from '../../utils/index.ts'; + +/** + * Max props issue interface. + */ +export interface MaxPropsIssue< + TInput extends Record, + TRequirement extends number, +> extends BaseIssue { + /** + * The issue kind. + */ + readonly kind: 'validation'; + /** + * The issue type. + */ + readonly type: 'max_props'; + /** + * The expected property. + */ + readonly expected: `<=${TRequirement}`; + /** + * The received property. + */ + readonly received: `${number}`; + /** + * The maximum properties. + */ + readonly requirement: TRequirement; +} + +/** + * Max props action interface. + */ +export interface MaxPropsAction< + TInput extends Record, + TRequirement extends number, + TMessage extends + | ErrorMessage> + | undefined, +> extends BaseValidation> { + /** + * The action type. + */ + readonly type: 'max_props'; + /** + * The action reference. + */ + readonly reference: typeof maxProps; + /** + * The expected property. + */ + readonly expects: `<=${TRequirement}`; + /** + * The maximum properties. + */ + readonly requirement: TRequirement; + /** + * The error message. + */ + readonly message: TMessage; +} + +/** + * Creates a max properties validation action. + * + * @param requirement The maximum properties. + * + * @returns A max props action. + */ +export function maxProps< + TInput extends Record, + const TRequirement extends number, +>(requirement: TRequirement): MaxPropsAction; + +/** + * Creates a max properties validation action. + * + * @param requirement The maximum properties. + * @param message The error message. + * + * @returns A max props action. + */ +export function maxProps< + TInput extends Record, + const TRequirement extends number, + const TMessage extends + | ErrorMessage> + | undefined, +>( + requirement: TRequirement, + message: TMessage +): MaxPropsAction; + +// @__NO_SIDE_EFFECTS__ +export function maxProps( + requirement: number, + message?: ErrorMessage, number>> +): MaxPropsAction< + Record, + number, + ErrorMessage, number>> | undefined +> { + return { + kind: 'validation', + type: 'max_props', + reference: maxProps, + async: false, + expects: `<=${requirement}`, + requirement, + message, + '~run'(dataset, config) { + if (!dataset.typed) return dataset; + const count = Object.keys(dataset.value).length; + if (dataset.typed && count > this.requirement) { + _addIssue(this, 'properties', dataset, config, { + received: `${count}`, + }); + } + return dataset; + }, + }; +} diff --git a/library/src/actions/minProps/index.ts b/library/src/actions/minProps/index.ts new file mode 100644 index 000000000..d72482368 --- /dev/null +++ b/library/src/actions/minProps/index.ts @@ -0,0 +1 @@ +export * from './minProps.ts'; diff --git a/library/src/actions/minProps/minProps.test-d.ts b/library/src/actions/minProps/minProps.test-d.ts new file mode 100644 index 000000000..375497c32 --- /dev/null +++ b/library/src/actions/minProps/minProps.test-d.ts @@ -0,0 +1,51 @@ +import { describe, expectTypeOf, test } from 'vitest'; +import type { InferInput, InferIssue, InferOutput } from '../../types/index.ts'; +import { + minProps, + type MinPropsAction, + type MinPropsIssue, +} from './minProps.ts'; + +describe('minProps', () => { + type Input = Record; + + describe('should return action object', () => { + test('with undefined message', () => { + type Action = MinPropsAction; + expectTypeOf(minProps(10)).toEqualTypeOf(); + expectTypeOf( + minProps(10, undefined) + ).toEqualTypeOf(); + }); + + test('with string message', () => { + expectTypeOf(minProps(10, 'message')).toEqualTypeOf< + MinPropsAction + >(); + }); + + test('with function message', () => { + expectTypeOf( + minProps string>(10, () => 'message') + ).toEqualTypeOf string>>(); + }); + }); + + describe('should infer correct types', () => { + type Action = MinPropsAction; + + test('of input', () => { + expectTypeOf>().toEqualTypeOf(); + }); + + test('of output', () => { + expectTypeOf>().toEqualTypeOf(); + }); + + test('of issue', () => { + expectTypeOf>().toEqualTypeOf< + MinPropsIssue + >(); + }); + }); +}); diff --git a/library/src/actions/minProps/minProps.test.ts b/library/src/actions/minProps/minProps.test.ts new file mode 100644 index 000000000..75d88fb10 --- /dev/null +++ b/library/src/actions/minProps/minProps.test.ts @@ -0,0 +1,100 @@ +import { describe, expect, test } from 'vitest'; +import { RecordIssue } from '../../schemas/index.ts'; +import { expectActionIssue, expectNoActionIssue } from '../../vitest/index.ts'; +import { + minProps, + type MinPropsAction, + type MinPropsIssue, +} from './minProps.ts'; + +describe('minProps', () => { + type Input = Record; + + describe('should return action object', () => { + const baseAction: Omit, 'message'> = { + kind: 'validation', + type: 'min_props', + reference: minProps, + expects: '>=2', + requirement: 2, + async: false, + '~run': expect.any(Function), + }; + + test('with undefined message', () => { + const action: MinPropsAction = { + ...baseAction, + message: undefined, + }; + expect(minProps(2)).toStrictEqual(action); + expect(minProps(2, undefined)).toStrictEqual(action); + }); + + test('with string message', () => { + expect(minProps(2, 'message')).toStrictEqual({ + ...baseAction, + message: 'message', + } satisfies MinPropsAction); + }); + + test('with function message', () => { + const message = () => 'message'; + expect(minProps(2, message)).toStrictEqual({ + ...baseAction, + message, + } satisfies MinPropsAction); + }); + }); + + describe('should return dataset without issues', () => { + const action = minProps(3); + + test('for untyped inputs', () => { + const issues: [RecordIssue] = [ + { + kind: 'schema', + type: 'record', + input: null, + expected: 'Object', + received: 'null', + message: 'message', + }, + ]; + expect( + action['~run']({ typed: false, value: null, issues }, {}) + ).toStrictEqual({ + typed: false, + value: null, + issues, + }); + }); + + test('for valid objects', () => { + expectNoActionIssue(action, [ + { foo: 1, bar: 2, baz: 3 }, + { foo: 1, bar: 2, baz: 3, qux: 4 }, + { foo: 1, bar: 2, baz: 3, qux: 4, quux: 5 }, + ]); + }); + }); + + describe('should return dataset with issues', () => { + const action = minProps(3, 'message'); + const baseIssue: Omit, 'input' | 'received'> = { + kind: 'validation', + type: 'min_props', + expected: '>=3', + message: 'message', + requirement: 3, + }; + + test('for invalid objects', () => { + expectActionIssue( + action, + baseIssue, + [{}, { foo: 1 }, { foo: 1, bar: 2 }], + (value) => `${Object.keys(value).length}` + ); + }); + }); +}); diff --git a/library/src/actions/minProps/minProps.ts b/library/src/actions/minProps/minProps.ts new file mode 100644 index 000000000..3f1d7deef --- /dev/null +++ b/library/src/actions/minProps/minProps.ts @@ -0,0 +1,128 @@ +import type { + BaseIssue, + BaseValidation, + ErrorMessage, +} from '../../types/index.ts'; +import { _addIssue } from '../../utils/index.ts'; + +/** + * Min props issue interface. + */ +export interface MinPropsIssue< + TInput extends Record, + TRequirement extends number, +> extends BaseIssue { + /** + * The issue kind. + */ + readonly kind: 'validation'; + /** + * The issue type. + */ + readonly type: 'min_props'; + /** + * The expected property. + */ + readonly expected: `>=${TRequirement}`; + /** + * The received property. + */ + readonly received: `${number}`; + /** + * The minimum properties. + */ + readonly requirement: TRequirement; +} + +/** + * Min props action interface. + */ +export interface MinPropsAction< + TInput extends Record, + TRequirement extends number, + TMessage extends + | ErrorMessage> + | undefined, +> extends BaseValidation> { + /** + * The action type. + */ + readonly type: 'min_props'; + /** + * The action reference. + */ + readonly reference: typeof minProps; + /** + * The expected property. + */ + readonly expects: `>=${TRequirement}`; + /** + * The minimum properties. + */ + readonly requirement: TRequirement; + /** + * The error message. + */ + readonly message: TMessage; +} + +/** + * Creates a min properties validation action. + * + * @param requirement The minimum properties. + * + * @returns A min props action. + */ +export function minProps< + TInput extends Record, + const TRequirement extends number, +>(requirement: TRequirement): MinPropsAction; + +/** + * Creates a min properties validation action. + * + * @param requirement The minimum properties. + * @param message The error message. + * + * @returns A min props action. + */ +export function minProps< + TInput extends Record, + const TRequirement extends number, + const TMessage extends + | ErrorMessage> + | undefined, +>( + requirement: TRequirement, + message: TMessage +): MinPropsAction; + +// @__NO_SIDE_EFFECTS__ +export function minProps( + requirement: number, + message?: ErrorMessage, number>> +): MinPropsAction< + Record, + number, + ErrorMessage, number>> | undefined +> { + return { + kind: 'validation', + type: 'min_props', + reference: minProps, + async: false, + expects: `>=${requirement}`, + requirement, + message, + '~run'(dataset, config) { + if (!dataset.typed) return dataset; + const count = Object.keys(dataset.value).length; + if (dataset.typed && count < this.requirement) { + _addIssue(this, 'properties', dataset, config, { + received: `${count}`, + }); + } + return dataset; + }, + }; +} diff --git a/packages/to-json-schema/CHANGELOG.md b/packages/to-json-schema/CHANGELOG.md index d24cf3653..73da0d99b 100644 --- a/packages/to-json-schema/CHANGELOG.md +++ b/packages/to-json-schema/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to the library will be documented in this file. +## vX.X.X (Month DD, YYYY) + +- Add support for `minProps` and `maxProps` action (pull request #1100) +- Change Valibot peer dependency to v1.1.0 + ## v1.0.0 (March 19, 2025) - Add support for `exactOptional` and `undefinedable` schema diff --git a/packages/to-json-schema/package.json b/packages/to-json-schema/package.json index 681af1d35..b42f07170 100644 --- a/packages/to-json-schema/package.json +++ b/packages/to-json-schema/package.json @@ -64,12 +64,12 @@ "tsup": "^8.4.0", "typescript": "^5.7.3", "typescript-eslint": "^8.25.0", - "valibot": "1.0.0", + "valibot": "workspace:*", "vite": "^6.2.0", "vite-tsconfig-paths": "^5.1.4", "vitest": "3.0.7" }, "peerDependencies": { - "valibot": "^1.0.0" + "valibot": "^1.1.0" } } diff --git a/packages/to-json-schema/src/convertAction.test.ts b/packages/to-json-schema/src/convertAction.test.ts index c5ce972f7..dc5492fc7 100644 --- a/packages/to-json-schema/src/convertAction.test.ts +++ b/packages/to-json-schema/src/convertAction.test.ts @@ -381,6 +381,19 @@ describe('convertAction', () => { ); }); + test('should convert max props action', () => { + expect( + convertAction( + { type: 'object' }, + v.maxProps, 3>(3), + undefined + ) + ).toStrictEqual({ + type: 'object', + maxProperties: 3, + }); + }); + test('should convert max value action for numbers', () => { expect( convertAction( @@ -492,6 +505,19 @@ describe('convertAction', () => { ); }); + test('should convert min props action', () => { + expect( + convertAction( + { type: 'object' }, + v.minProps, 3>(3), + undefined + ) + ).toStrictEqual({ + type: 'object', + minProperties: 3, + }); + }); + test('should convert min value action for numbers', () => { expect( convertAction( diff --git a/packages/to-json-schema/src/convertAction.ts b/packages/to-json-schema/src/convertAction.ts index f3e1c2d30..8048a28e4 100644 --- a/packages/to-json-schema/src/convertAction.ts +++ b/packages/to-json-schema/src/convertAction.ts @@ -50,6 +50,12 @@ type Action = number, v.ErrorMessage> | undefined > + | v.MaxPropsAction< + Record, + number, + | v.ErrorMessage, number>> + | undefined + > | v.MaxValueAction< v.ValueInput, v.ValueInput, @@ -60,6 +66,12 @@ type Action = number, v.ErrorMessage> | undefined > + | v.MinPropsAction< + Record, + number, + | v.ErrorMessage, number>> + | undefined + > | v.MinValueAction< v.ValueInput, v.ValueInput, @@ -209,6 +221,11 @@ export function convertAction( break; } + case 'max_props': { + jsonSchema.maxProperties = valibotAction.requirement; + break; + } + case 'max_value': { if (jsonSchema.type !== 'number') { handleError( @@ -236,6 +253,11 @@ export function convertAction( break; } + case 'min_props': { + jsonSchema.minProperties = valibotAction.requirement; + break; + } + case 'min_value': { if (jsonSchema.type !== 'number') { handleError( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad856c0a2..187ffaa2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,8 +135,8 @@ importers: specifier: ^8.25.0 version: 8.25.0(eslint@9.21.0(jiti@2.4.2))(typescript@5.7.3) valibot: - specifier: 1.0.0 - version: 1.0.0(typescript@5.7.3) + specifier: workspace:* + version: link:../../library vite: specifier: ^6.2.0 version: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) diff --git a/website/src/routes/api/(actions)/maxProps/index.mdx b/website/src/routes/api/(actions)/maxProps/index.mdx new file mode 100644 index 000000000..0cc4ca13d --- /dev/null +++ b/website/src/routes/api/(actions)/maxProps/index.mdx @@ -0,0 +1,78 @@ +--- +title: maxProps +description: Creates a max properties validation action. +source: /actions/maxProps/maxProps.ts +contributors: + - fabian-hiller + - muningis +--- + +import { ApiList, Property } from '~/components'; +import { properties } from './properties'; + +# maxProps + +Creates a max properties validation action. + +```ts +const Action = v.maxProps(requirement, message); +``` + +## Generics + +- `TInput` +- `TRequirement` +- `TMessage` + +## Parameters + +- `requirement` +- `message` + +### Explanation + +With `maxProps` you can validate the number of properties in an object. If the input does not match the `requirement`, you can use `message` to customize the error message. + +## Returns + +- `Action` + +## Examples + +The following examples show how `maxProps` can be used. + +### Maximum object properties + +Schema to validate an object with a maximum of 5 properties. + +```ts +const MaxPropsSchema = v.pipe( + v.record(v.string(), v.number()), + v.maxProps(5, 'Object must not exceed 5 properties.') +); +``` + +## Related + +The following APIs can be combined with `maxProps`. + +### Schemas + + + +### Methods + + + +### Utils + + diff --git a/website/src/routes/api/(actions)/maxProps/properties.ts b/website/src/routes/api/(actions)/maxProps/properties.ts new file mode 100644 index 000000000..e1bda8548 --- /dev/null +++ b/website/src/routes/api/(actions)/maxProps/properties.ts @@ -0,0 +1,80 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TInput: { + modifier: 'extends', + type: { + type: 'custom', + name: 'Record', + generics: ['string', 'unknown'], + }, + }, + TRequirement: { + modifier: 'extends', + type: 'number', + }, + TMessage: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'ErrorMessage', + href: '../ErrorMessage/', + generics: [ + { + type: 'custom', + name: 'MaxPropsIssue', + href: '../MaxPropsIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + ], + }, + 'undefined', + ], + }, + }, + requirement: { + type: { + type: 'custom', + name: 'TRequirement', + }, + }, + message: { + type: { + type: 'custom', + name: 'TMessage', + }, + }, + Action: { + type: { + type: 'custom', + name: 'MaxPropsAction', + href: '../MaxPropsAction/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + { + type: 'custom', + name: 'TMessage', + }, + ], + }, + }, +}; diff --git a/website/src/routes/api/(actions)/minLength/index.mdx b/website/src/routes/api/(actions)/minLength/index.mdx index ab1642237..6ee326648 100644 --- a/website/src/routes/api/(actions)/minLength/index.mdx +++ b/website/src/routes/api/(actions)/minLength/index.mdx @@ -44,7 +44,7 @@ With `minLength` you can validate the length of a string or array. If the input The following examples show how `minLength` can be used. -### Minumum string length +### Minimum string length Schema to validate a string with a minimum length of 3 characters. diff --git a/website/src/routes/api/(actions)/minProps/index.mdx b/website/src/routes/api/(actions)/minProps/index.mdx new file mode 100644 index 000000000..711f87ba8 --- /dev/null +++ b/website/src/routes/api/(actions)/minProps/index.mdx @@ -0,0 +1,78 @@ +--- +title: minProps +description: Creates a min properties validation action. +source: /actions/minProps/minProps.ts +contributors: + - fabian-hiller + - muningis +--- + +import { ApiList, Property } from '~/components'; +import { properties } from './properties'; + +# minProps + +Creates a min properties validation action. + +```ts +const Action = v.minProps(requirement, message); +``` + +## Generics + +- `TInput` +- `TRequirement` +- `TMessage` + +## Parameters + +- `requirement` +- `message` + +### Explanation + +With `minProps` you can validate the number of properties in an object. If the input does not match the `requirement`, you can use `message` to customize the error message. + +## Returns + +- `Action` + +## Examples + +The following examples show how `minProps` can be used. + +### Minimum object properties + +Schema to validate an object with a minimum of 5 properties. + +```ts +const MinPropsSchema = v.pipe( + v.record(v.string(), v.number()), + v.minProps(5, 'The object should have at least 3 properties.') +); +``` + +## Related + +The following APIs can be combined with `minProps`. + +### Schemas + + + +### Methods + + + +### Utils + + diff --git a/website/src/routes/api/(actions)/minProps/properties.ts b/website/src/routes/api/(actions)/minProps/properties.ts new file mode 100644 index 000000000..2c8449b34 --- /dev/null +++ b/website/src/routes/api/(actions)/minProps/properties.ts @@ -0,0 +1,80 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TInput: { + modifier: 'extends', + type: { + type: 'custom', + name: 'Record', + generics: ['string', 'unknown'], + }, + }, + TRequirement: { + modifier: 'extends', + type: 'number', + }, + TMessage: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'ErrorMessage', + href: '../ErrorMessage/', + generics: [ + { + type: 'custom', + name: 'MinPropsIssue', + href: '../MinPropsIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + ], + }, + 'undefined', + ], + }, + }, + requirement: { + type: { + type: 'custom', + name: 'TRequirement', + }, + }, + message: { + type: { + type: 'custom', + name: 'TMessage', + }, + }, + Action: { + type: { + type: 'custom', + name: 'MinPropsAction', + href: '../MinPropsAction/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + { + type: 'custom', + name: 'TMessage', + }, + ], + }, + }, +}; diff --git a/website/src/routes/api/(async)/customAsync/index.mdx b/website/src/routes/api/(async)/customAsync/index.mdx index 9034cf30a..ed874c3cc 100644 --- a/website/src/routes/api/(async)/customAsync/index.mdx +++ b/website/src/routes/api/(async)/customAsync/index.mdx @@ -154,6 +154,7 @@ The following APIs can be combined with `customAsync`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -162,6 +163,7 @@ The following APIs can be combined with `customAsync`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(async)/fallbackAsync/index.mdx b/website/src/routes/api/(async)/fallbackAsync/index.mdx index 7313b3bf0..f9340ee0d 100644 --- a/website/src/routes/api/(async)/fallbackAsync/index.mdx +++ b/website/src/routes/api/(async)/fallbackAsync/index.mdx @@ -183,6 +183,7 @@ The following APIs can be combined with `fallbackAsync`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -191,6 +192,7 @@ The following APIs can be combined with `fallbackAsync`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(async)/intersectAsync/index.mdx b/website/src/routes/api/(async)/intersectAsync/index.mdx index 9114b1074..06d420df3 100644 --- a/website/src/routes/api/(async)/intersectAsync/index.mdx +++ b/website/src/routes/api/(async)/intersectAsync/index.mdx @@ -185,6 +185,7 @@ The following APIs can be combined with `intersectAsync`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -193,6 +194,7 @@ The following APIs can be combined with `intersectAsync`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(async)/lazyAsync/index.mdx b/website/src/routes/api/(async)/lazyAsync/index.mdx index 77ad501fe..14499d6a9 100644 --- a/website/src/routes/api/(async)/lazyAsync/index.mdx +++ b/website/src/routes/api/(async)/lazyAsync/index.mdx @@ -217,6 +217,7 @@ The following APIs can be combined with `lazyAsync`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -225,6 +226,7 @@ The following APIs can be combined with `lazyAsync`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(async)/objectAsync/index.mdx b/website/src/routes/api/(async)/objectAsync/index.mdx index 04934f03c..57265a2fc 100644 --- a/website/src/routes/api/(async)/objectAsync/index.mdx +++ b/website/src/routes/api/(async)/objectAsync/index.mdx @@ -133,7 +133,9 @@ The following APIs can be combined with `objectAsync`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(async)/objectWithRestAsync/index.mdx b/website/src/routes/api/(async)/objectWithRestAsync/index.mdx index 0bd95546f..fb70945f8 100644 --- a/website/src/routes/api/(async)/objectWithRestAsync/index.mdx +++ b/website/src/routes/api/(async)/objectWithRestAsync/index.mdx @@ -147,7 +147,9 @@ The following APIs can be combined with `objectWithRestAsync`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(async)/partialAsync/index.mdx b/website/src/routes/api/(async)/partialAsync/index.mdx index 1cac717ed..4a6045a32 100644 --- a/website/src/routes/api/(async)/partialAsync/index.mdx +++ b/website/src/routes/api/(async)/partialAsync/index.mdx @@ -139,7 +139,9 @@ The following APIs can be combined with `partialAsync`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(async)/pipeAsync/index.mdx b/website/src/routes/api/(async)/pipeAsync/index.mdx index 96c9eaf2c..bbd7b0ea6 100644 --- a/website/src/routes/api/(async)/pipeAsync/index.mdx +++ b/website/src/routes/api/(async)/pipeAsync/index.mdx @@ -6,6 +6,7 @@ source: /methods/pipe/pipeAsync.ts contributors: - fabian-hiller - EltonLobo07 + - muningis --- import { Link } from '@builder.io/qwik-city'; @@ -222,6 +223,7 @@ The following APIs can be combined with `pipeAsync`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -230,6 +232,7 @@ The following APIs can be combined with `pipeAsync`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(async)/recordAsync/index.mdx b/website/src/routes/api/(async)/recordAsync/index.mdx index b25ee22a7..0495c6bb2 100644 --- a/website/src/routes/api/(async)/recordAsync/index.mdx +++ b/website/src/routes/api/(async)/recordAsync/index.mdx @@ -132,7 +132,9 @@ The following APIs can be combined with `recordAsync`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(async)/requiredAsync/index.mdx b/website/src/routes/api/(async)/requiredAsync/index.mdx index 70737649c..2e6f150fa 100644 --- a/website/src/routes/api/(async)/requiredAsync/index.mdx +++ b/website/src/routes/api/(async)/requiredAsync/index.mdx @@ -145,7 +145,9 @@ The following APIs can be combined with `requiredAsync`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(async)/strictObjectAsync/index.mdx b/website/src/routes/api/(async)/strictObjectAsync/index.mdx index 598be3a96..0ad919903 100644 --- a/website/src/routes/api/(async)/strictObjectAsync/index.mdx +++ b/website/src/routes/api/(async)/strictObjectAsync/index.mdx @@ -133,7 +133,9 @@ The following APIs can be combined with `strictObjectAsync`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(async)/unionAsync/index.mdx b/website/src/routes/api/(async)/unionAsync/index.mdx index 9396353af..08d3a0328 100644 --- a/website/src/routes/api/(async)/unionAsync/index.mdx +++ b/website/src/routes/api/(async)/unionAsync/index.mdx @@ -178,6 +178,7 @@ The following APIs can be combined with `unionAsync`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -186,6 +187,7 @@ The following APIs can be combined with `unionAsync`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(async)/variantAsync/index.mdx b/website/src/routes/api/(async)/variantAsync/index.mdx index 45e1e86d5..877d01d86 100644 --- a/website/src/routes/api/(async)/variantAsync/index.mdx +++ b/website/src/routes/api/(async)/variantAsync/index.mdx @@ -133,7 +133,9 @@ The following APIs can be combined with `variantAsync`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(methods)/config/index.mdx b/website/src/routes/api/(methods)/config/index.mdx index 2054c7f26..6b56802be 100644 --- a/website/src/routes/api/(methods)/config/index.mdx +++ b/website/src/routes/api/(methods)/config/index.mdx @@ -201,6 +201,7 @@ The following APIs can be combined with `config`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -209,6 +210,7 @@ The following APIs can be combined with `config`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(methods)/fallback/index.mdx b/website/src/routes/api/(methods)/fallback/index.mdx index ac1612fab..6a86cb42e 100644 --- a/website/src/routes/api/(methods)/fallback/index.mdx +++ b/website/src/routes/api/(methods)/fallback/index.mdx @@ -200,6 +200,7 @@ The following APIs can be combined with `fallback`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -208,6 +209,7 @@ The following APIs can be combined with `fallback`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(methods)/message/index.mdx b/website/src/routes/api/(methods)/message/index.mdx index c4b7d4c93..93d60fab8 100644 --- a/website/src/routes/api/(methods)/message/index.mdx +++ b/website/src/routes/api/(methods)/message/index.mdx @@ -185,6 +185,7 @@ The following APIs can be combined with `message`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -193,6 +194,7 @@ The following APIs can be combined with `message`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(methods)/omit/index.mdx b/website/src/routes/api/(methods)/omit/index.mdx index bf8aa68cd..cbc354bc6 100644 --- a/website/src/routes/api/(methods)/omit/index.mdx +++ b/website/src/routes/api/(methods)/omit/index.mdx @@ -126,7 +126,9 @@ The following APIs can be combined with `omit`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(methods)/partial/index.mdx b/website/src/routes/api/(methods)/partial/index.mdx index bdd1ed5af..38849c064 100644 --- a/website/src/routes/api/(methods)/partial/index.mdx +++ b/website/src/routes/api/(methods)/partial/index.mdx @@ -139,7 +139,9 @@ The following APIs can be combined with `partial`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(methods)/pick/index.mdx b/website/src/routes/api/(methods)/pick/index.mdx index f9f35022e..b0b4dab1b 100644 --- a/website/src/routes/api/(methods)/pick/index.mdx +++ b/website/src/routes/api/(methods)/pick/index.mdx @@ -126,7 +126,9 @@ The following APIs can be combined with `pick`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(methods)/pipe/index.mdx b/website/src/routes/api/(methods)/pipe/index.mdx index 982f6de72..bd7c6474c 100644 --- a/website/src/routes/api/(methods)/pipe/index.mdx +++ b/website/src/routes/api/(methods)/pipe/index.mdx @@ -8,6 +8,7 @@ contributors: - morinokami - jasperteo - EltonLobo07 + - muningis --- import { Link } from '@builder.io/qwik-city'; @@ -204,6 +205,7 @@ The following APIs can be combined with `pipe`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -212,6 +214,7 @@ The following APIs can be combined with `pipe`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(methods)/required/index.mdx b/website/src/routes/api/(methods)/required/index.mdx index ff531c631..06f5964a5 100644 --- a/website/src/routes/api/(methods)/required/index.mdx +++ b/website/src/routes/api/(methods)/required/index.mdx @@ -148,7 +148,9 @@ The following APIs can be combined with `required`. 'brand', 'check', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(schemas)/any/index.mdx b/website/src/routes/api/(schemas)/any/index.mdx index 1b9b3accf..fa885f687 100644 --- a/website/src/routes/api/(schemas)/any/index.mdx +++ b/website/src/routes/api/(schemas)/any/index.mdx @@ -133,6 +133,7 @@ The following APIs can be combined with `any`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -141,6 +142,7 @@ The following APIs can be combined with `any`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(schemas)/custom/index.mdx b/website/src/routes/api/(schemas)/custom/index.mdx index 69059842c..71062d3dc 100644 --- a/website/src/routes/api/(schemas)/custom/index.mdx +++ b/website/src/routes/api/(schemas)/custom/index.mdx @@ -161,6 +161,7 @@ The following APIs can be combined with `custom`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -169,6 +170,7 @@ The following APIs can be combined with `custom`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(schemas)/intersect/index.mdx b/website/src/routes/api/(schemas)/intersect/index.mdx index 85d5c9c85..808354801 100644 --- a/website/src/routes/api/(schemas)/intersect/index.mdx +++ b/website/src/routes/api/(schemas)/intersect/index.mdx @@ -183,6 +183,7 @@ The following APIs can be combined with `intersect`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -191,6 +192,7 @@ The following APIs can be combined with `intersect`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(schemas)/lazy/index.mdx b/website/src/routes/api/(schemas)/lazy/index.mdx index 6a616d1f2..9d589ea4d 100644 --- a/website/src/routes/api/(schemas)/lazy/index.mdx +++ b/website/src/routes/api/(schemas)/lazy/index.mdx @@ -246,6 +246,7 @@ The following APIs can be combined with `lazy`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -254,6 +255,7 @@ The following APIs can be combined with `lazy`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(schemas)/looseObject/index.mdx b/website/src/routes/api/(schemas)/looseObject/index.mdx index 1c9ac85f1..de01c557f 100644 --- a/website/src/routes/api/(schemas)/looseObject/index.mdx +++ b/website/src/routes/api/(schemas)/looseObject/index.mdx @@ -5,6 +5,7 @@ source: /schemas/looseObject/looseObject.ts contributors: - fabian-hiller - kazizi55 + - muningis --- import { Link } from '@builder.io/qwik-city'; @@ -183,7 +184,9 @@ The following APIs can be combined with `looseObject`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(schemas)/object/index.mdx b/website/src/routes/api/(schemas)/object/index.mdx index 7a9316426..a1e2cb08a 100644 --- a/website/src/routes/api/(schemas)/object/index.mdx +++ b/website/src/routes/api/(schemas)/object/index.mdx @@ -5,6 +5,7 @@ source: /schemas/object/object.ts contributors: - fabian-hiller - kazizi55 + - muningis --- import { Link } from '@builder.io/qwik-city'; @@ -183,7 +184,9 @@ The following APIs can be combined with `object`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(schemas)/objectWithRest/index.mdx b/website/src/routes/api/(schemas)/objectWithRest/index.mdx index 509ccba01..9ab8533e4 100644 --- a/website/src/routes/api/(schemas)/objectWithRest/index.mdx +++ b/website/src/routes/api/(schemas)/objectWithRest/index.mdx @@ -5,6 +5,7 @@ source: /schemas/object/object.ts contributors: - fabian-hiller - kazizi55 + - muningis --- import { Link } from '@builder.io/qwik-city'; @@ -201,7 +202,9 @@ The following APIs can be combined with `objectWithRest`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(schemas)/record/index.mdx b/website/src/routes/api/(schemas)/record/index.mdx index f0df80805..541628be5 100644 --- a/website/src/routes/api/(schemas)/record/index.mdx +++ b/website/src/routes/api/(schemas)/record/index.mdx @@ -4,6 +4,7 @@ description: Creates a record schema. source: /schemas/record/record.ts contributors: - fabian-hiller + - muningis --- import { Link } from '@builder.io/qwik-city'; @@ -175,6 +176,8 @@ The following APIs can be combined with `record`. 'check', 'brand', 'description', + 'maxProps', + 'minProps', 'metadata', 'partialCheck', 'rawCheck', diff --git a/website/src/routes/api/(schemas)/strictObject/index.mdx b/website/src/routes/api/(schemas)/strictObject/index.mdx index 63831e6a1..53335d3f5 100644 --- a/website/src/routes/api/(schemas)/strictObject/index.mdx +++ b/website/src/routes/api/(schemas)/strictObject/index.mdx @@ -5,6 +5,7 @@ source: /schemas/strictObject/strictObject.ts contributors: - fabian-hiller - kazizi55 + - muningis --- import { Link } from '@builder.io/qwik-city'; @@ -183,7 +184,9 @@ The following APIs can be combined with `strictObject`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(schemas)/union/index.mdx b/website/src/routes/api/(schemas)/union/index.mdx index a211874b7..7b0958411 100644 --- a/website/src/routes/api/(schemas)/union/index.mdx +++ b/website/src/routes/api/(schemas)/union/index.mdx @@ -200,6 +200,7 @@ The following APIs can be combined with `union`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -208,6 +209,7 @@ The following APIs can be combined with `union`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(schemas)/unknown/index.mdx b/website/src/routes/api/(schemas)/unknown/index.mdx index d5e752029..3bfd504bf 100644 --- a/website/src/routes/api/(schemas)/unknown/index.mdx +++ b/website/src/routes/api/(schemas)/unknown/index.mdx @@ -133,6 +133,7 @@ The following APIs can be combined with `unknown`. 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -141,6 +142,7 @@ The following APIs can be combined with `unknown`. 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords', diff --git a/website/src/routes/api/(schemas)/variant/index.mdx b/website/src/routes/api/(schemas)/variant/index.mdx index 289749d99..b7f71ad92 100644 --- a/website/src/routes/api/(schemas)/variant/index.mdx +++ b/website/src/routes/api/(schemas)/variant/index.mdx @@ -149,7 +149,9 @@ The following APIs can be combined with `variant`. 'check', 'brand', 'description', + 'maxProps', 'metadata', + 'minProps', 'partialCheck', 'rawCheck', 'rawTransform', diff --git a/website/src/routes/api/(types)/MaxPropsAction/index.mdx b/website/src/routes/api/(types)/MaxPropsAction/index.mdx new file mode 100644 index 000000000..e825c6760 --- /dev/null +++ b/website/src/routes/api/(types)/MaxPropsAction/index.mdx @@ -0,0 +1,29 @@ +--- +title: MaxPropsAction +description: Max properties action interface. +contributors: + - fabian-hiller + - muningis +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# MaxPropsAction + +Max properties action interface. + +## Generics + +- `TInput` +- `TRequirement` +- `TMessage` + +## Definition + +- `MaxPropsAction` + - `type` + - `reference` + - `expects` + - `requirement` + - `message` diff --git a/website/src/routes/api/(types)/MaxPropsAction/properties.ts b/website/src/routes/api/(types)/MaxPropsAction/properties.ts new file mode 100644 index 000000000..ec6b2cf00 --- /dev/null +++ b/website/src/routes/api/(types)/MaxPropsAction/properties.ts @@ -0,0 +1,121 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TInput: { + modifier: 'extends', + type: { + type: 'custom', + name: 'Record', + generics: ['string', 'unknown'], + }, + }, + TRequirement: { + modifier: 'extends', + type: 'number', + }, + TMessage: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'ErrorMessage', + href: '../ErrorMessage/', + generics: [ + { + type: 'custom', + name: 'MaxPropsIssue', + href: '../MaxPropsIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + ], + }, + 'undefined', + ], + }, + }, + BaseValidation: { + modifier: 'extends', + type: { + type: 'custom', + name: 'BaseValidation', + href: '../BaseValidation/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'MaxPropsIssue', + href: '../MaxPropsIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + ], + }, + }, + type: { + type: { + type: 'string', + value: 'max_props', + }, + }, + reference: { + type: { + type: 'custom', + modifier: 'typeof', + name: 'maxProps', + href: '../maxProps/', + }, + }, + expects: { + type: { + type: 'template', + parts: [ + { + type: 'string', + value: '<=', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + }, + requirement: { + type: { + type: 'custom', + name: 'TRequirement', + }, + }, + message: { + type: { + type: 'custom', + name: 'TMessage', + }, + }, +}; diff --git a/website/src/routes/api/(types)/MaxPropsIssue/index.mdx b/website/src/routes/api/(types)/MaxPropsIssue/index.mdx new file mode 100644 index 000000000..3a33551b4 --- /dev/null +++ b/website/src/routes/api/(types)/MaxPropsIssue/index.mdx @@ -0,0 +1,28 @@ +--- +title: MaxPropsIssue +description: Max properties issue interface. +contributors: + - fabian-hiller + - muningis +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# MaxPropsIssue + +Max properties issue interface. + +## Generics + +- `TInput` +- `TRequirement` + +## Definition + +- `MaxPropsIssue` + - `kind` + - `type` + - `expected` + - `received` + - `requirement` diff --git a/website/src/routes/api/(types)/MaxPropsIssue/properties.ts b/website/src/routes/api/(types)/MaxPropsIssue/properties.ts new file mode 100644 index 000000000..bd3256e5c --- /dev/null +++ b/website/src/routes/api/(types)/MaxPropsIssue/properties.ts @@ -0,0 +1,69 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TInput: { + modifier: 'extends', + type: { + type: 'custom', + name: 'Record', + generics: ['string', 'unknown'], + }, + }, + TRequirement: { + modifier: 'extends', + type: 'number', + }, + BaseIssue: { + modifier: 'extends', + type: { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + ], + }, + }, + kind: { + type: { + type: 'string', + value: 'validation', + }, + }, + type: { + type: { + type: 'string', + value: 'max_props', + }, + }, + expected: { + type: { + type: 'template', + parts: [ + { + type: 'string', + value: '<=', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + }, + received: { + type: { + type: 'template', + parts: ['number'], + }, + }, + requirement: { + type: { + type: 'custom', + name: 'TRequirement', + }, + }, +}; diff --git a/website/src/routes/api/(types)/MinPropsAction/index.mdx b/website/src/routes/api/(types)/MinPropsAction/index.mdx new file mode 100644 index 000000000..21bcb5be1 --- /dev/null +++ b/website/src/routes/api/(types)/MinPropsAction/index.mdx @@ -0,0 +1,29 @@ +--- +title: MinPropsAction +description: Min props action interface. +contributors: + - fabian-hiller + - muningis +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# MinPropsAction + +Min props action interface. + +## Generics + +- `TInput` +- `TRequirement` +- `TMessage` + +## Definition + +- `MinPropsAction` + - `type` + - `reference` + - `expects` + - `requirement` + - `message` diff --git a/website/src/routes/api/(types)/MinPropsAction/properties.ts b/website/src/routes/api/(types)/MinPropsAction/properties.ts new file mode 100644 index 000000000..2d1056aaf --- /dev/null +++ b/website/src/routes/api/(types)/MinPropsAction/properties.ts @@ -0,0 +1,121 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TInput: { + modifier: 'extends', + type: { + type: 'custom', + name: 'Record', + generics: ['string', 'unknown'], + }, + }, + TRequirement: { + modifier: 'extends', + type: 'number', + }, + TMessage: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'ErrorMessage', + href: '../ErrorMessage/', + generics: [ + { + type: 'custom', + name: 'MinPropsIssue', + href: '../MinPropsIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + ], + }, + 'undefined', + ], + }, + }, + BaseValidation: { + modifier: 'extends', + type: { + type: 'custom', + name: 'BaseValidation', + href: '../BaseValidation/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'MinPropsIssue', + href: '../MinPropsIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + ], + }, + }, + type: { + type: { + type: 'string', + value: 'min_props', + }, + }, + reference: { + type: { + type: 'custom', + modifier: 'typeof', + name: 'minProps', + href: '../minProps/', + }, + }, + expects: { + type: { + type: 'template', + parts: [ + { + type: 'string', + value: '>=', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + }, + requirement: { + type: { + type: 'custom', + name: 'TRequirement', + }, + }, + message: { + type: { + type: 'custom', + name: 'TMessage', + }, + }, +}; diff --git a/website/src/routes/api/(types)/MinPropsIssue/index.mdx b/website/src/routes/api/(types)/MinPropsIssue/index.mdx new file mode 100644 index 000000000..f8ac93386 --- /dev/null +++ b/website/src/routes/api/(types)/MinPropsIssue/index.mdx @@ -0,0 +1,28 @@ +--- +title: MinPropsIssue +description: Min props issue interface. +contributors: + - fabian-hiller + - muningis +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# MinPropsIssue + +Min props issue interface. + +## Generics + +- `TInput` +- `TRequirement` + +## Definition + +- `MinPropsIssue` + - `kind` + - `type` + - `expected` + - `received` + - `requirement` diff --git a/website/src/routes/api/(types)/MinPropsIssue/properties.ts b/website/src/routes/api/(types)/MinPropsIssue/properties.ts new file mode 100644 index 000000000..9b8847595 --- /dev/null +++ b/website/src/routes/api/(types)/MinPropsIssue/properties.ts @@ -0,0 +1,69 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TInput: { + modifier: 'extends', + type: { + type: 'custom', + name: 'Record', + generics: ['string', 'unknown'], + }, + }, + TRequirement: { + modifier: 'extends', + type: 'number', + }, + BaseIssue: { + modifier: 'extends', + type: { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: [ + { + type: 'custom', + name: 'TInput', + }, + ], + }, + }, + kind: { + type: { + type: 'string', + value: 'validation', + }, + }, + type: { + type: { + type: 'string', + value: 'min_props', + }, + }, + expected: { + type: { + type: 'template', + parts: [ + { + type: 'string', + value: '>=', + }, + { + type: 'custom', + name: 'TRequirement', + }, + ], + }, + }, + received: { + type: { + type: 'template', + parts: ['number'], + }, + }, + requirement: { + type: { + type: 'custom', + name: 'TRequirement', + }, + }, +}; diff --git a/website/src/routes/api/menu.md b/website/src/routes/api/menu.md index 26e7e9730..60166bf20 100644 --- a/website/src/routes/api/menu.md +++ b/website/src/routes/api/menu.md @@ -123,6 +123,7 @@ - [maxBytes](/api/maxBytes/) - [maxGraphemes](/api/maxGraphemes/) - [maxLength](/api/maxLength/) +- [maxProps](/api/maxProps/) - [maxSize](/api/maxSize/) - [maxValue](/api/maxValue/) - [maxWords](/api/maxWords/) @@ -131,6 +132,7 @@ - [minBytes](/api/minBytes/) - [minGraphemes](/api/minGraphemes/) - [minLength](/api/minLength/) +- [minProps](/api/minProps/) - [minSize](/api/minSize/) - [minValue](/api/minValue/) - [minWords](/api/minWords/) @@ -469,6 +471,8 @@ - [MaxGraphemesIssue](/api/MaxGraphemesIssue/) - [MaxLengthAction](/api/MaxLengthAction/) - [MaxLengthIssue](/api/MaxLengthIssue/) +- [MaxPropsAction](/api/MaxPropsAction/) +- [MaxPropsIssue](/api/MaxPropsIssue/) - [MaxSizeAction](/api/MaxSizeAction/) - [MaxSizeIssue](/api/MaxSizeIssue/) - [MaxValueAction](/api/MaxValueAction/) @@ -486,6 +490,8 @@ - [MinGraphemesIssue](/api/MinGraphemesIssue/) - [MinLengthAction](/api/MinLengthAction/) - [MinLengthIssue](/api/MinLengthIssue/) +- [MinPropsAction](/api/MinPropsAction/) +- [MinPropsIssue](/api/MinPropsIssue/) - [MinSizeAction](/api/MinSizeAction/) - [MinSizeIssue](/api/MinSizeIssue/) - [MinValueAction](/api/MinValueAction/) diff --git a/website/src/routes/guides/(main-concepts)/pipelines/index.mdx b/website/src/routes/guides/(main-concepts)/pipelines/index.mdx index b3c99d4ee..0c3dd7588 100644 --- a/website/src/routes/guides/(main-concepts)/pipelines/index.mdx +++ b/website/src/routes/guides/(main-concepts)/pipelines/index.mdx @@ -89,6 +89,7 @@ Pipeline validation actions examine the input and, if the input does not meet a 'maxBytes', 'maxGraphemes', 'maxLength', + 'maxProps', 'maxSize', 'maxValue', 'maxWords', @@ -96,6 +97,7 @@ Pipeline validation actions examine the input and, if the input does not meet a 'minBytes', 'minGraphemes', 'minLength', + 'minProps', 'minSize', 'minValue', 'minWords',