@scalar/validation 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+
2
+ > @scalar/validation@0.1.0 build /home/runner/work/scalar/scalar/packages/validation
3
+ > tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json
4
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @scalar/validation
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#8567](https://github.com/scalar/scalar/pull/8567): feat: initial commit ✨
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-present Scalar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # `@scalar/validation`
2
+
3
+ Small, schema-first helpers to **check** unknown data and **coerce** it into predictable shapes. Schemas are plain JavaScript objects (easy to serialize or log), and TypeScript can infer output types with `Static`.
4
+
5
+ ## Install
6
+
7
+ This package lives in the Scalar monorepo. In workspace consumers:
8
+
9
+ ```bash
10
+ pnpm add @scalar/validation
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { coerce, number, object, string, validate, type Static } from '@scalar/validation'
17
+
18
+ const userSchema = object({
19
+ id: number(),
20
+ name: string(),
21
+ })
22
+
23
+ type User = Static<typeof userSchema>
24
+
25
+ validate(userSchema, { id: 1, name: 'Ada' }) // true
26
+ validate(userSchema, { id: 1, name: 2 }) // false
27
+
28
+ // Best-effort shaping: invalid primitives fall back to defaults
29
+ coerce(userSchema, { id: 'x', name: 'Ada' }) // { id: 0, name: 'Ada' }
30
+ ```
31
+
32
+ ## Concepts
33
+
34
+ ### `validate(schema, value)`
35
+
36
+ Returns `true` if `value` satisfies `schema`, otherwise `false`.
37
+
38
+ - **`undefined` schema** — always fails.
39
+ - **`number()`** — finite numbers only (`NaN` and `Infinity` fail).
40
+ - **`object({ ... })`** — value must be a **plain object** (see [Objects and records](#objects-and-records)). Each declared property is validated; **extra properties are not rejected**.
41
+ - **`union([...])`** — matches if **any** branch matches.
42
+
43
+ ### `coerce(schema, value)`
44
+
45
+ Returns a value typed as `Static<typeof schema>`. It is **not** strict validation: it **normalizes** toward the schema.
46
+
47
+ - Valid primitives are returned as-is.
48
+ - Invalid **number** → `0`, invalid **string** → `''`, invalid **boolean** → `false`.
49
+ - **`nullable()`** — result is always `null`.
50
+ - **`notDefined()`** — result is always `undefined`.
51
+ - **`literal(x)`** — result is always the schema’s literal `x` (the declared constant).
52
+ - **`array` / `object` / `record`** — built recursively; wrong shapes become empty containers or defaulted fields.
53
+ - **`union`** — picks a branch using a **scoring** heuristic (object shape and literal tags weigh more than “property exists”).
54
+ - Optional third argument: internal **`WeakMap` cache** for cyclic graphs; you normally omit it.
55
+
56
+ Use **`validate`** when you need a yes/no. Use **`coerce`** when you want a stable default-filled structure (for example normalizing config or parsed JSON).
57
+
58
+ ## Schema builders
59
+
60
+ | Builder | Validates | `Static` type (idea) |
61
+ |--------|-----------|----------------------|
62
+ | `number()` | Finite `number` | `number` |
63
+ | `string()` | `string` | `string` |
64
+ | `boolean()` | `boolean` | `boolean` |
65
+ | `nullable()` | `null` only | `null` |
66
+ | `notDefined()` | `undefined` only | `undefined` |
67
+ | `any()` | Anything | `any` |
68
+ | `literal(v)` | Strict equality to `v` | `typeof v` |
69
+ | `array(item)` | Array; every item matches `item` | `Static<item>[]` |
70
+ | `record(key, value)` | Plain object; keys and values match | `Record<…, …>` |
71
+ | `object(props)` | Plain object; each key in `props` | Object of static fields |
72
+ | `union([a, b, …])` | Matches any member | Union of branches |
73
+ | `optional(s)` | Shorthand for `union([s, notDefined()])` | `Static<s> \| undefined` |
74
+ | `lazy(() => schema)` | Defers schema (recursion) | Inferred from inner schema |
75
+ | `evaluate(fn, schema)` | Runs `fn(value)` then validates `schema` | `Static<schema>` |
76
+
77
+ ```ts
78
+ import { lazy, object, string, union, literal } from '@scalar/validation'
79
+
80
+ // Discriminated-style union
81
+ const message = union([
82
+ object({ type: literal('text'), body: string() }),
83
+ object({ type: literal('ping') }),
84
+ ])
85
+ ```
86
+
87
+ ### `evaluate` — parse then validate
88
+
89
+ ```ts
90
+ import { evaluate, number, string } from '@scalar/validation'
91
+
92
+ const trimmed = evaluate((v) => (typeof v === 'string' ? v.trim() : v), string())
93
+
94
+ validate(trimmed, ' hi ') // true (after trim)
95
+ ```
96
+
97
+ ## Objects and records
98
+
99
+ **Plain object** means: not `null`, and prototype is `Object.prototype` or `null`. Arrays, `Date`, and most class instances **do not** count as objects for `object()` / `record()` validation.
100
+
101
+ **`object`** checks only the keys you list. Missing keys are read as `undefined`, so pair them with `optional(...)` when a field may be absent.
102
+
103
+ **`record`** during **validation** checks every key against the key schema and every value against the value schema. During **coercion**, entries keep their string keys as-is and only values are coerced.
104
+
105
+ ## TypeScript: `Static` and `Schema`
106
+
107
+ - **`Static<S>`** — inferred TypeScript type for data that matches schema `S` (depth-limited to avoid infinite recursion on very deep types).
108
+ - **`Schema`** — union of all schema shapes; use when you store or pass schemas around.
109
+
110
+ ## Development
111
+
112
+ ```bash
113
+ pnpm --filter @scalar/validation test
114
+ pnpm --filter @scalar/validation types:check
115
+ pnpm --filter @scalar/validation build
116
+ ```
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,22 @@
1
+ import type { Schema } from './schema.js';
2
+ import type { Static } from './types.js';
3
+ /**
4
+ * Coerces an unknown value toward the static type implied by `schema`. Values that
5
+ * pass {@link validate} for that branch are kept; otherwise primitives default to
6
+ * `0`, `''`, or `false`, and arrays, records, and objects are built recursively.
7
+ * Unions pick the best-matching branch; `evaluate` runs `expression` before the inner schema.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { coerce, number, object, string } from '@scalar/validation'
12
+ *
13
+ * coerce(number(), 42) // 42
14
+ * coerce(number(), 'nope') // 0 — invalid number uses default
15
+ * coerce(object({ id: number(), name: string() }), { id: '1', name: 'Ada' }) // { id: 0, name: 'Ada' }
16
+ * ```
17
+ *
18
+ * The optional `cache` argument tracks visited object–schema pairs to stop infinite recursion
19
+ * on cyclic graphs; callers normally omit it.
20
+ */
21
+ export declare const coerce: <S extends Schema>(schema: S, value: unknown, cache?: WeakMap<object, Set<Schema>>) => Static<S>;
22
+ //# sourceMappingURL=coerce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../src/coerce.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAwDrC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,MAAM,EACrC,QAAQ,CAAC,EACT,OAAO,OAAO,EACd,QAAO,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAiB,KAClD,MAAM,CAAC,CAAC,CA0FV,CAAA"}
package/dist/coerce.js ADDED
@@ -0,0 +1,149 @@
1
+ import { isObject } from './helpers/is-object.js';
2
+ import { validate } from './validate.js';
3
+ /**
4
+ * Computes a "score" indicating how well a value matches a schema,
5
+ * used for picking the best branch in union coercion.
6
+ *
7
+ * Higher score means a closer match. Literals and matching object shapes
8
+ * are weighted more heavily. Objects are scored by shape/literals;
9
+ * arrays/records by structural type; primitives by validation; unions try all branches.
10
+ */
11
+ const scoreUnion = (schema, value) => {
12
+ if (schema.type === 'object') {
13
+ if (!isObject(value)) {
14
+ return 0;
15
+ }
16
+ // For each key in the schema's properties:
17
+ // - +10 if the value matches an explicit literal for the key.
18
+ // - +1 if the property exists (not literal match).
19
+ return Object.keys(schema.properties).reduce((acc, key) => {
20
+ const exists = key in value;
21
+ const isLiteralMatch = schema.properties[key].type === 'literal' && value[key] === schema.properties[key].value;
22
+ if (isLiteralMatch) {
23
+ return acc + 10;
24
+ }
25
+ return acc + (exists ? 1 : 0);
26
+ }, 0);
27
+ }
28
+ if (schema.type === 'array') {
29
+ // Score 1 if value is an array, otherwise 0
30
+ return Array.isArray(value) ? 1 : 0;
31
+ }
32
+ if (schema.type === 'record') {
33
+ // TODO: implement smarter scoring for records (just a placeholder for now)
34
+ return isObject(value) ? 1 : 0;
35
+ }
36
+ if (schema.type === 'union') {
37
+ // For a union, use the highest score among all sub-schemas
38
+ return Math.max(...schema.schemas.map((schema) => scoreUnion(schema, value)));
39
+ }
40
+ if (schema.type === 'lazy') {
41
+ // For a lazy schema, evaluate the inner schema and recurse
42
+ return scoreUnion(schema.schema(), value);
43
+ }
44
+ if (schema.type === 'evaluate') {
45
+ // For an evaluate schema, evaluate the expression and recurse
46
+ return scoreUnion(schema.schema, schema.expression(value));
47
+ }
48
+ // For primitives and any other type, return 1 if valid, otherwise 0
49
+ return validate(schema, value) ? 1 : 0;
50
+ };
51
+ /**
52
+ * Coerces an unknown value toward the static type implied by `schema`. Values that
53
+ * pass {@link validate} for that branch are kept; otherwise primitives default to
54
+ * `0`, `''`, or `false`, and arrays, records, and objects are built recursively.
55
+ * Unions pick the best-matching branch; `evaluate` runs `expression` before the inner schema.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { coerce, number, object, string } from '@scalar/validation'
60
+ *
61
+ * coerce(number(), 42) // 42
62
+ * coerce(number(), 'nope') // 0 — invalid number uses default
63
+ * coerce(object({ id: number(), name: string() }), { id: '1', name: 'Ada' }) // { id: 0, name: 'Ada' }
64
+ * ```
65
+ *
66
+ * The optional `cache` argument tracks visited object–schema pairs to stop infinite recursion
67
+ * on cyclic graphs; callers normally omit it.
68
+ */
69
+ export const coerce = (schema, value, cache = new WeakMap()) => {
70
+ // Prevent infinite recursion
71
+ if (isObject(value) && cache.get(value)?.has(schema)) {
72
+ return value;
73
+ }
74
+ // Track visited schemas to prevent infinite recursion
75
+ if (isObject(value)) {
76
+ const schemas = cache.get(value) || new Set();
77
+ schemas.add(schema);
78
+ cache.set(value, schemas);
79
+ }
80
+ // If no schema is provided, return the value as is
81
+ if (!schema) {
82
+ return value;
83
+ }
84
+ if (schema.type === 'any') {
85
+ return value;
86
+ }
87
+ if (schema.type === 'number') {
88
+ if (validate(schema, value)) {
89
+ return value;
90
+ }
91
+ return 0;
92
+ }
93
+ if (schema.type === 'string') {
94
+ if (validate(schema, value)) {
95
+ return value;
96
+ }
97
+ return '';
98
+ }
99
+ if (schema.type === 'boolean') {
100
+ if (validate(schema, value)) {
101
+ return value;
102
+ }
103
+ return false;
104
+ }
105
+ if (schema.type === 'nullable') {
106
+ return null;
107
+ }
108
+ if (schema.type === 'notDefined') {
109
+ return undefined;
110
+ }
111
+ if (schema.type === 'array') {
112
+ if (!Array.isArray(value)) {
113
+ return [];
114
+ }
115
+ return value.map((item) => coerce(schema.items, item, cache));
116
+ }
117
+ if (schema.type === 'record') {
118
+ if (!isObject(value)) {
119
+ return {};
120
+ }
121
+ return Object.fromEntries(Object.entries(value).map(([key, value]) => [key, coerce(schema.value, value, cache)]));
122
+ }
123
+ if (schema.type === 'object') {
124
+ const keys = Object.keys(schema.properties);
125
+ const target = isObject(value) ? value : null;
126
+ return Object.fromEntries(keys.map((key) => [key, coerce(schema.properties[key], target?.[key], cache)]));
127
+ }
128
+ if (schema.type === 'union') {
129
+ const branch = schema.schemas.reduce((acc, schema) => {
130
+ const score = scoreUnion(schema, value);
131
+ return score > acc.score ? { schema, score } : acc;
132
+ }, { schema: schema.schemas[0], score: 0 });
133
+ // We need some way to pick one of the union values
134
+ return coerce(branch.schema, value, cache);
135
+ }
136
+ if (schema.type === 'literal') {
137
+ return schema.value;
138
+ }
139
+ if (schema.type === 'lazy') {
140
+ return coerce(schema.schema(), value, cache);
141
+ }
142
+ if (schema.type === 'evaluate') {
143
+ return coerce(schema.schema, schema.expression(value), cache);
144
+ }
145
+ // We need to assert here that schema has the type never so we know we handle all cases
146
+ const _exhaustive = schema;
147
+ console.warn('Unknown schema type:', _exhaustive);
148
+ return value;
149
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Stolen from |@scalar/helpers/object/is-object.ts|
3
+ * so we don't have to depend on it.
4
+ */
5
+ /**
6
+ * Returns true if the provided value is a record object
7
+ * (i.e. not null, not an array, and has an actual object as the prototype).
8
+ *
9
+ * Differs from the previous isObject in that it returns false for Date,
10
+ * RegExp, Error, Map, Set, WeakMap, WeakSet, Promise, and other non-plain objects.
11
+ *
12
+ * Examples:
13
+ * isObject({}) // true
14
+ * isObject({ a: 1 }) // true
15
+ * isObject([]) // false (Array)
16
+ * isObject(null) // false
17
+ * isObject(123) // false
18
+ * isObject('string') // false
19
+ * isObject(new Error('test')) // false
20
+ * isObject(new Date()) // false
21
+ * isObject(Object.create(null)) // true
22
+ */
23
+ export declare const isObject: (value: unknown) => value is Record<string | number | symbol, unknown>;
24
+ //# sourceMappingURL=is-object.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-object.d.ts","sourceRoot":"","sources":["../../src/helpers/is-object.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAO1F,CAAA"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Stolen from |@scalar/helpers/object/is-object.ts|
3
+ * so we don't have to depend on it.
4
+ */
5
+ /**
6
+ * Returns true if the provided value is a record object
7
+ * (i.e. not null, not an array, and has an actual object as the prototype).
8
+ *
9
+ * Differs from the previous isObject in that it returns false for Date,
10
+ * RegExp, Error, Map, Set, WeakMap, WeakSet, Promise, and other non-plain objects.
11
+ *
12
+ * Examples:
13
+ * isObject({}) // true
14
+ * isObject({ a: 1 }) // true
15
+ * isObject([]) // false (Array)
16
+ * isObject(null) // false
17
+ * isObject(123) // false
18
+ * isObject('string') // false
19
+ * isObject(new Error('test')) // false
20
+ * isObject(new Date()) // false
21
+ * isObject(Object.create(null)) // true
22
+ */
23
+ export const isObject = (value) => {
24
+ if (value === null || typeof value !== 'object') {
25
+ return false;
26
+ }
27
+ const proto = Object.getPrototypeOf(value);
28
+ return proto === Object.prototype || proto === null;
29
+ };
@@ -0,0 +1,5 @@
1
+ export { coerce } from './coerce.js';
2
+ export { type Schema, any, array, boolean, evaluate, lazy, literal, notDefined, nullable, number, object, optional, record, string, union, } from './schema.js';
3
+ export type { Static } from './types.js';
4
+ export { validate } from './validate.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EACL,KAAK,MAAM,EACX,GAAG,EACH,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,GACN,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { coerce } from './coerce.js';
2
+ export { any, array, boolean, evaluate, lazy, literal, notDefined, nullable, number, object, optional, record, string, union, } from './schema.js';
3
+ export { validate } from './validate.js';
@@ -0,0 +1,85 @@
1
+ /** Schema for finite numeric values. {@link Static} resolves to `number`. */
2
+ export type NumberSchema = {
3
+ type: 'number';
4
+ };
5
+ /** Schema for string values. {@link Static} resolves to `string`. */
6
+ export type StringSchema = {
7
+ type: 'string';
8
+ };
9
+ /** Schema for boolean values. {@link Static} resolves to `boolean`. */
10
+ export type BooleanSchema = {
11
+ type: 'boolean';
12
+ };
13
+ /** Schema for `null`. {@link Static} resolves to `null`. */
14
+ export type NullableSchema = {
15
+ type: 'nullable';
16
+ };
17
+ /** Schema for a missing or omitted value. {@link Static} resolves to `undefined`. */
18
+ export type NotDefinedSchema = {
19
+ type: 'notDefined';
20
+ };
21
+ /** Schema that accepts any value without narrowing. {@link Static} resolves to `any`. */
22
+ export type AnySchema = {
23
+ type: 'any';
24
+ };
25
+ /** Schema for homogeneous lists. {@link Static} resolves to an array of the item static type. */
26
+ export type ArraySchema<Item extends Schema> = {
27
+ type: 'array';
28
+ items: Item;
29
+ };
30
+ /** Schema for key-value maps with uniform value shape. Keys are constrained to string or number schemas. */
31
+ export type RecordSchema<Key extends StringSchema | NumberSchema | AnySchema, Value extends Schema> = {
32
+ type: 'record';
33
+ key: Key;
34
+ value: Value;
35
+ };
36
+ /** Schema for objects with a fixed set of named properties, each with its own schema. */
37
+ export type ObjectSchema<Properties extends Record<string, Schema>> = {
38
+ type: 'object';
39
+ properties: Properties;
40
+ };
41
+ /** Schema that matches if any member schema matches (discriminated union when literals or object tags differ). */
42
+ export type UnionSchema<Schemas extends Schema[]> = {
43
+ type: 'union';
44
+ schemas: Schemas;
45
+ };
46
+ /** Schema for a single exact constant (string, number, boolean, or bigint). {@link Static} is that literal type. */
47
+ export type LiteralSchema<T extends string | number | boolean | bigint> = {
48
+ type: 'literal';
49
+ value: T;
50
+ };
51
+ /**
52
+ * Schema for self-referential or recursive types (such as trees or linked lists).
53
+ * The `schema` property is a factory function returning a schema instance, allowing
54
+ * references to itself without causing circular definition errors at type-level.
55
+ */
56
+ export type LazySchema<S extends () => Schema> = {
57
+ type: 'lazy';
58
+ schema: S;
59
+ };
60
+ /**
61
+ * Schema that runs a coercion or transform (`expression`) on the input, then validates with the inner schema.
62
+ * Use when parsing needs a preprocessing step before the usual rules apply.
63
+ */
64
+ export type EvaluateSchema<S extends Schema> = {
65
+ type: 'evaluate';
66
+ expression: (value: unknown) => unknown;
67
+ schema: S;
68
+ };
69
+ export type Schema = NumberSchema | StringSchema | BooleanSchema | NullableSchema | NotDefinedSchema | AnySchema | ArraySchema<any> | RecordSchema<any, any> | ObjectSchema<Record<string, any>> | UnionSchema<any[]> | LiteralSchema<any> | LazySchema<any> | EvaluateSchema<any>;
70
+ declare const number: () => NumberSchema;
71
+ declare const string: () => StringSchema;
72
+ declare const boolean: () => BooleanSchema;
73
+ declare const nullable: () => NullableSchema;
74
+ declare const notDefined: () => NotDefinedSchema;
75
+ declare const any: () => AnySchema;
76
+ declare const array: <Item extends Schema>(items: Item) => ArraySchema<Item>;
77
+ declare const record: <Key extends StringSchema | AnySchema, Value extends Schema>(key: Key, value: Value) => RecordSchema<Key, Value>;
78
+ declare const object: <Properties extends Record<string, Schema>>(properties: Properties) => ObjectSchema<Properties>;
79
+ declare const union: <Schemas extends Schema[]>(schemas: Schemas) => UnionSchema<Schemas>;
80
+ declare const optional: <S extends Schema>(schema: S) => UnionSchema<(NotDefinedSchema | S)[]>;
81
+ declare const literal: <Value extends string | number | boolean | bigint>(value: Value) => LiteralSchema<Value>;
82
+ declare const lazy: <S extends () => Schema>(schema: S) => LazySchema<S>;
83
+ declare const evaluate: <S extends Schema>(expression: (value: unknown) => unknown, schema: S) => EvaluateSchema<S>;
84
+ export { number, string, boolean, nullable, notDefined, any, array, record, object, union, optional, literal, lazy, evaluate, };
85
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/dist/schema.js ADDED
@@ -0,0 +1,50 @@
1
+ const number = () => ({
2
+ type: 'number',
3
+ });
4
+ const string = () => ({
5
+ type: 'string',
6
+ });
7
+ const boolean = () => ({
8
+ type: 'boolean',
9
+ });
10
+ const nullable = () => ({
11
+ type: 'nullable',
12
+ });
13
+ const notDefined = () => ({
14
+ type: 'notDefined',
15
+ });
16
+ const any = () => ({
17
+ type: 'any',
18
+ });
19
+ const array = (items) => ({
20
+ type: 'array',
21
+ items,
22
+ });
23
+ const record = (key, value) => ({
24
+ type: 'record',
25
+ key,
26
+ value,
27
+ });
28
+ const object = (properties) => ({
29
+ type: 'object',
30
+ properties,
31
+ });
32
+ const union = (schemas) => ({
33
+ type: 'union',
34
+ schemas,
35
+ });
36
+ const optional = (schema) => union([schema, notDefined()]);
37
+ const literal = (value) => ({
38
+ type: 'literal',
39
+ value,
40
+ });
41
+ const lazy = (schema) => ({
42
+ type: 'lazy',
43
+ schema,
44
+ });
45
+ const evaluate = (expression, schema) => ({
46
+ type: 'evaluate',
47
+ expression,
48
+ schema,
49
+ });
50
+ export { number, string, boolean, nullable, notDefined, any, array, record, object, union, optional, literal, lazy, evaluate, };
@@ -0,0 +1,8 @@
1
+ import type { AnySchema, ArraySchema, BooleanSchema, EvaluateSchema, LazySchema, LiteralSchema, NotDefinedSchema, NullableSchema, NumberSchema, ObjectSchema, RecordSchema, StringSchema, UnionSchema } from './schema.js';
2
+ export type Static<T> = _Static<T, 10>;
3
+ type _Static<T, Depth extends number = 10> = Depth extends 0 ? any : T extends LiteralSchema<infer Value> ? Value : T extends NumberSchema ? number : T extends StringSchema ? string : T extends BooleanSchema ? boolean : T extends NullableSchema ? null : T extends NotDefinedSchema ? undefined : T extends AnySchema ? any : T extends ArraySchema<infer Item> ? Array<_Static<Item, Prev<Depth>>> : T extends RecordSchema<infer Key, infer Value> ? Record<_Static<Key, Prev<Depth>> & PropertyKey, _Static<Value, Prev<Depth>>> : T extends ObjectSchema<infer Properties> ? {
4
+ [K in keyof Properties]: _Static<Properties[K], Prev<Depth>>;
5
+ } : T extends UnionSchema<infer Schemas> ? _Static<Schemas[number], Prev<Depth>> : T extends EvaluateSchema<infer S> ? _Static<S, Prev<Depth>> : T extends LazySchema<infer S> ? _Static<ReturnType<S>, Prev<Depth>> : never;
6
+ type Prev<T extends number> = T extends 10 ? 9 : T extends 9 ? 8 : T extends 8 ? 7 : T extends 7 ? 6 : T extends 6 ? 5 : T extends 5 ? 4 : T extends 4 ? 3 : T extends 3 ? 2 : T extends 2 ? 1 : T extends 1 ? 0 : 0;
7
+ export {};
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACZ,MAAM,UAAU,CAAA;AAGjB,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAGtC,KAAK,OAAO,CAAC,CAAC,EAAE,KAAK,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK,SAAS,CAAC,GACxD,GAAG,GACH,CAAC,SAAS,aAAa,CAAC,MAAM,KAAK,CAAC,GAClC,KAAK,GACL,CAAC,SAAS,YAAY,GACpB,MAAM,GACN,CAAC,SAAS,YAAY,GACpB,MAAM,GACN,CAAC,SAAS,aAAa,GACrB,OAAO,GACP,CAAC,SAAS,cAAc,GACtB,IAAI,GACJ,CAAC,SAAS,gBAAgB,GACxB,SAAS,GACT,CAAC,SAAS,SAAS,GACjB,GAAG,GACH,CAAC,SAAS,WAAW,CAAC,MAAM,IAAI,CAAC,GAC/B,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GACjC,CAAC,SAAS,YAAY,CAAC,MAAM,GAAG,EAAE,MAAM,KAAK,CAAC,GAC5C,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,WAAW,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAC5E,CAAC,SAAS,YAAY,CAAC,MAAM,UAAU,CAAC,GACtC;KAAG,CAAC,IAAI,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;CAAE,GAChE,CAAC,SAAS,WAAW,CAAC,MAAM,OAAO,CAAC,GAClC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GACrC,CAAC,SAAS,cAAc,CAAC,MAAM,CAAC,CAAC,GAC/B,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GACvB,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAC3B,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GACnC,KAAK,CAAA;AAGnC,KAAK,IAAI,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,EAAE,GACtC,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,SAAS,CAAC,GACT,CAAC,GACD,CAAC,CAAA"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import type { Schema } from './schema.js';
2
+ /**
3
+ * Validates that a given value matches the specified schema.
4
+ *
5
+ * The schema describes the expected structure/type of data.
6
+ * Supported schema types include:
7
+ * - 'any': Accepts any value.
8
+ * - 'number': Only numbers are valid.
9
+ * - 'string': Only strings are valid.
10
+ * - 'boolean': Only booleans are valid.
11
+ * - 'nullable': Only `null` is valid.
12
+ * - 'notDefined': Only `undefined` is valid.
13
+ * - 'array': Array with all items validated recursively.
14
+ * - 'record': Object with string/number keys and values, checked recursively.
15
+ * - 'object': Object with fixed property keys, each validated recursively.
16
+ * - 'union': Accepts if value matches any of the listed schemas.
17
+ * - 'literal': Exact match with a literal value.
18
+ * - 'recursive': Schema referring to itself for nested validation (e.g. trees).
19
+ * - 'evaluate': Transforms value then validates against an inner schema.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { number, object, string, validate } from '@scalar/validation'
24
+ *
25
+ * const schema = object({ id: number(), name: string() })
26
+ * validate(schema, { id: 1, name: 'Ada' }) // true
27
+ * validate(schema, { id: 1, name: 2 }) // false
28
+ * ```
29
+ *
30
+ * If schema is `undefined`, validation fails.
31
+ * Returns true if the value matches the schema, false otherwise.
32
+ */
33
+ export declare const validate: (schema: Schema | undefined, value: unknown) => boolean;
34
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,GAAG,SAAS,EAAE,OAAO,OAAO,KAAG,OAwDrE,CAAA"}