anyvali 0.0.5__tar.gz
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.
- anyvali-0.0.5/PKG-INFO +350 -0
- anyvali-0.0.5/README.md +339 -0
- anyvali-0.0.5/pyproject.toml +17 -0
- anyvali-0.0.5/setup.cfg +4 -0
- anyvali-0.0.5/src/anyvali/__init__.py +293 -0
- anyvali-0.0.5/src/anyvali/format/__init__.py +1 -0
- anyvali-0.0.5/src/anyvali/format/validators.py +110 -0
- anyvali-0.0.5/src/anyvali/interchange/__init__.py +1 -0
- anyvali-0.0.5/src/anyvali/interchange/document.py +33 -0
- anyvali-0.0.5/src/anyvali/interchange/exporter.py +51 -0
- anyvali-0.0.5/src/anyvali/interchange/importer.py +277 -0
- anyvali-0.0.5/src/anyvali/issue_codes.py +16 -0
- anyvali-0.0.5/src/anyvali/parse/__init__.py +1 -0
- anyvali-0.0.5/src/anyvali/parse/coerce.py +98 -0
- anyvali-0.0.5/src/anyvali/parse/defaults.py +18 -0
- anyvali-0.0.5/src/anyvali/parse/parser.py +28 -0
- anyvali-0.0.5/src/anyvali/schemas/__init__.py +1 -0
- anyvali-0.0.5/src/anyvali/schemas/any.py +23 -0
- anyvali-0.0.5/src/anyvali/schemas/array.py +64 -0
- anyvali-0.0.5/src/anyvali/schemas/base.py +266 -0
- anyvali-0.0.5/src/anyvali/schemas/bool.py +25 -0
- anyvali-0.0.5/src/anyvali/schemas/enum.py +43 -0
- anyvali-0.0.5/src/anyvali/schemas/integer.py +193 -0
- anyvali-0.0.5/src/anyvali/schemas/intersection.py +55 -0
- anyvali-0.0.5/src/anyvali/schemas/literal.py +34 -0
- anyvali-0.0.5/src/anyvali/schemas/never.py +22 -0
- anyvali-0.0.5/src/anyvali/schemas/null.py +28 -0
- anyvali-0.0.5/src/anyvali/schemas/nullable.py +34 -0
- anyvali-0.0.5/src/anyvali/schemas/number.py +142 -0
- anyvali-0.0.5/src/anyvali/schemas/object.py +83 -0
- anyvali-0.0.5/src/anyvali/schemas/optional.py +40 -0
- anyvali-0.0.5/src/anyvali/schemas/record.py +39 -0
- anyvali-0.0.5/src/anyvali/schemas/ref.py +69 -0
- anyvali-0.0.5/src/anyvali/schemas/string.py +125 -0
- anyvali-0.0.5/src/anyvali/schemas/tuple.py +41 -0
- anyvali-0.0.5/src/anyvali/schemas/union.py +44 -0
- anyvali-0.0.5/src/anyvali/schemas/unknown.py +23 -0
- anyvali-0.0.5/src/anyvali/types.py +72 -0
- anyvali-0.0.5/src/anyvali.egg-info/PKG-INFO +350 -0
- anyvali-0.0.5/src/anyvali.egg-info/SOURCES.txt +41 -0
- anyvali-0.0.5/src/anyvali.egg-info/dependency_links.txt +1 -0
- anyvali-0.0.5/src/anyvali.egg-info/requires.txt +4 -0
- anyvali-0.0.5/src/anyvali.egg-info/top_level.txt +1 -0
anyvali-0.0.5/PKG-INFO
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anyvali
|
|
3
|
+
Version: 0.0.5
|
|
4
|
+
Summary: Native validation with portable schema interchange
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
10
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<img src="logo.png" alt="AnyVali" width="200" />
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<h1 align="center">AnyVali</h1>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<strong>Native validation libraries for 10 languages, one portable schema model.</strong>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://github.com/BetterCorp/AnyVali/actions/workflows/ci.yml"><img src="https://github.com/BetterCorp/AnyVali/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
24
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT" /></a>
|
|
25
|
+
<a href="https://www.npmjs.com/package/@anyvali/js"><img src="https://img.shields.io/npm/v/%40anyvali%2Fjs.svg?label=npm" alt="npm" /></a>
|
|
26
|
+
<a href="https://pypi.org/project/anyvali/"><img src="https://img.shields.io/pypi/v/anyvali.svg?label=pypi" alt="PyPI" /></a>
|
|
27
|
+
<a href="https://crates.io/crates/anyvali"><img src="https://img.shields.io/crates/v/anyvali.svg?label=crates.io" alt="crates.io" /></a>
|
|
28
|
+
<a href="https://pkg.go.dev/github.com/BetterCorp/AnyVali/sdk/go"><img src="https://img.shields.io/badge/go-pkg.go.dev-blue.svg" alt="Go" /></a>
|
|
29
|
+
<a href="https://www.nuget.org/packages/AnyVali"><img src="https://img.shields.io/nuget/v/AnyVali.svg?label=nuget" alt="NuGet" /></a>
|
|
30
|
+
<a href="https://rubygems.org/gems/anyvali"><img src="https://img.shields.io/gem/v/anyvali.svg?label=gem" alt="Gem" /></a>
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<a href="https://anyvali.com">Website</a> ·
|
|
35
|
+
<a href="https://docs.anyvali.com">Docs</a> ·
|
|
36
|
+
<a href="https://github.com/BetterCorp/AnyVali/issues">Issues</a> ·
|
|
37
|
+
<a href="CONTRIBUTING.md">Contributing</a>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
AnyVali lets you write validation schemas in your language, then share them across any of 10 supported runtimes via a portable JSON format. Think Zod, but for every language.
|
|
43
|
+
|
|
44
|
+
## Why AnyVali?
|
|
45
|
+
|
|
46
|
+
- **Write schemas natively** -- idiomatic APIs for each language, not a separate DSL
|
|
47
|
+
- **Share across languages** -- export to JSON, import in any other SDK
|
|
48
|
+
- **Safe numeric defaults** -- `number` = float64, `int` = int64 everywhere
|
|
49
|
+
- **Deterministic parsing** -- coerce, default, then validate, in that order
|
|
50
|
+
- **Conformance tested** -- shared test corpus ensures identical behavior across SDKs
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install @anyvali/js # JavaScript / TypeScript
|
|
56
|
+
pip install anyvali # Python
|
|
57
|
+
go get github.com/BetterCorp/AnyVali/sdk/go # Go
|
|
58
|
+
cargo add anyvali # Rust
|
|
59
|
+
dotnet add package AnyVali # C#
|
|
60
|
+
composer require anyvali/anyvali # PHP
|
|
61
|
+
gem install anyvali # Ruby
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary>Java / Kotlin / C++</summary>
|
|
66
|
+
|
|
67
|
+
**Java (Maven)**
|
|
68
|
+
```xml
|
|
69
|
+
<dependency>
|
|
70
|
+
<groupId>com.anyvali</groupId>
|
|
71
|
+
<artifactId>anyvali</artifactId>
|
|
72
|
+
<version>0.0.1</version>
|
|
73
|
+
</dependency>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Kotlin (Gradle)**
|
|
77
|
+
```kotlin
|
|
78
|
+
implementation("com.anyvali:anyvali:0.0.1")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**C++ (CMake)**
|
|
82
|
+
```cmake
|
|
83
|
+
FetchContent_Declare(anyvali GIT_REPOSITORY https://github.com/BetterCorp/AnyVali)
|
|
84
|
+
FetchContent_MakeAvailable(anyvali)
|
|
85
|
+
target_link_libraries(your_target PRIVATE anyvali)
|
|
86
|
+
```
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
Define a schema, parse input, get structured errors or clean data.
|
|
92
|
+
|
|
93
|
+
<table>
|
|
94
|
+
<tr><th>JavaScript / TypeScript</th><th>Python</th></tr>
|
|
95
|
+
<tr>
|
|
96
|
+
<td>
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { string, int, object, array } from "@anyvali/js";
|
|
100
|
+
|
|
101
|
+
const User = object({
|
|
102
|
+
name: string().minLength(1),
|
|
103
|
+
email: string().format('email'),
|
|
104
|
+
age: int().min(0).optional(),
|
|
105
|
+
tags: array(string()).maxItems(5),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Throws on failure
|
|
109
|
+
const user = User.parse(input);
|
|
110
|
+
|
|
111
|
+
// Or get a result object
|
|
112
|
+
const result = User.safeParse(input);
|
|
113
|
+
if (!result.success) {
|
|
114
|
+
console.log(result.issues);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
</td>
|
|
119
|
+
<td>
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
import anyvali as v
|
|
123
|
+
|
|
124
|
+
User = v.object_({
|
|
125
|
+
"name": v.string().min_length(1),
|
|
126
|
+
"email": v.string().format("email"),
|
|
127
|
+
"age": v.int_().min(0).optional(),
|
|
128
|
+
"tags": v.array(v.string()).max_items(5),
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
# Raises on failure
|
|
132
|
+
user = User.parse(input_data)
|
|
133
|
+
|
|
134
|
+
# Or get a result object
|
|
135
|
+
result = User.safe_parse(input_data)
|
|
136
|
+
if not result.success:
|
|
137
|
+
print(result.issues)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
</td>
|
|
141
|
+
</tr>
|
|
142
|
+
</table>
|
|
143
|
+
|
|
144
|
+
<details>
|
|
145
|
+
<summary>Go example</summary>
|
|
146
|
+
|
|
147
|
+
```go
|
|
148
|
+
import av "github.com/BetterCorp/AnyVali/sdk/go"
|
|
149
|
+
|
|
150
|
+
User := av.Object(map[string]av.Schema{
|
|
151
|
+
"name": av.String().MinLength(1),
|
|
152
|
+
"email": av.String().Format("email"),
|
|
153
|
+
"age": av.Optional(av.Int().Min(0)),
|
|
154
|
+
"tags": av.Array(av.String()).MaxItems(5),
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
result := User.SafeParse(input)
|
|
158
|
+
if !result.Success {
|
|
159
|
+
for _, issue := range result.Issues {
|
|
160
|
+
fmt.Printf("[%s] %s at %v\n", issue.Code, issue.Message, issue.Path)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
</details>
|
|
165
|
+
|
|
166
|
+
## Cross-Language Schema Sharing
|
|
167
|
+
|
|
168
|
+
AnyVali's core feature: export a schema from one language, import it in another.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// TypeScript frontend -- export
|
|
172
|
+
const doc = User.export();
|
|
173
|
+
const json = JSON.stringify(doc);
|
|
174
|
+
// Send to your backend, save to DB, put in a config file...
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# Python backend -- import
|
|
179
|
+
import json, anyvali as v
|
|
180
|
+
|
|
181
|
+
schema = v.import_schema(json.loads(schema_json))
|
|
182
|
+
result = schema.safe_parse(request_body) # Same validation rules!
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Forms
|
|
186
|
+
|
|
187
|
+
The JS SDK also ships a small forms layer for browser-native fields, HTML5 attributes, and AnyVali validation.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { object, string, int } from "@anyvali/js";
|
|
191
|
+
import { initForm } from "@anyvali/js/forms";
|
|
192
|
+
|
|
193
|
+
const Signup = object({
|
|
194
|
+
email: string().format("email"),
|
|
195
|
+
age: int().min(18),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
initForm("#signup", { schema: Signup });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```html
|
|
202
|
+
<form id="signup">
|
|
203
|
+
<input name="email" type="email" />
|
|
204
|
+
<input name="age" type="number" />
|
|
205
|
+
<button type="submit">Create account</button>
|
|
206
|
+
</form>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
For JSX-style attribute binding:
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import { object, string } from "@anyvali/js";
|
|
213
|
+
import { createFormBindings } from "@anyvali/js/forms";
|
|
214
|
+
|
|
215
|
+
const Signup = object({
|
|
216
|
+
email: string().format("email"),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const form = createFormBindings({ schema: Signup });
|
|
220
|
+
|
|
221
|
+
<input {...form.field("email")} />;
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The portable JSON format:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"anyvaliVersion": "1.0",
|
|
229
|
+
"schemaVersion": "1",
|
|
230
|
+
"root": {
|
|
231
|
+
"kind": "object",
|
|
232
|
+
"properties": {
|
|
233
|
+
"name": { "kind": "string", "minLength": 1 },
|
|
234
|
+
"email": { "kind": "string", "format": "email" }
|
|
235
|
+
},
|
|
236
|
+
"required": ["name", "email"],
|
|
237
|
+
"unknownKeys": "reject"
|
|
238
|
+
},
|
|
239
|
+
"definitions": {},
|
|
240
|
+
"extensions": {}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Supported SDKs
|
|
245
|
+
|
|
246
|
+
| Language | Package | Status |
|
|
247
|
+
|----------|---------|--------|
|
|
248
|
+
| JavaScript / TypeScript | [`@anyvali/js`](https://www.npmjs.com/package/@anyvali/js) | v0.0.1 |
|
|
249
|
+
| Python | [`anyvali`](https://pypi.org/project/anyvali/) | v0.0.1 |
|
|
250
|
+
| Go | [`github.com/BetterCorp/AnyVali/sdk/go`](https://pkg.go.dev/github.com/BetterCorp/AnyVali/sdk/go) | v0.0.1 |
|
|
251
|
+
| Java | `com.anyvali:anyvali` | v0.0.1 |
|
|
252
|
+
| C# | [`AnyVali`](https://www.nuget.org/packages/AnyVali) | v0.0.1 |
|
|
253
|
+
| Rust | [`anyvali`](https://crates.io/crates/anyvali) | v0.0.1 |
|
|
254
|
+
| PHP | `anyvali/anyvali` | v0.0.1 |
|
|
255
|
+
| Ruby | [`anyvali`](https://rubygems.org/gems/anyvali) | v0.0.1 |
|
|
256
|
+
| Kotlin | `com.anyvali:anyvali` | v0.0.1 |
|
|
257
|
+
| C++ | `anyvali` (CMake) | v0.0.1 |
|
|
258
|
+
|
|
259
|
+
## CLI & HTTP API
|
|
260
|
+
|
|
261
|
+
Don't need an SDK? Use AnyVali from the command line or as a validation microservice.
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Validate from the command line
|
|
265
|
+
anyvali validate schema.json '{"name": "Alice", "email": "alice@test.com"}'
|
|
266
|
+
|
|
267
|
+
# Pipe from stdin
|
|
268
|
+
cat payload.json | anyvali validate schema.json -
|
|
269
|
+
|
|
270
|
+
# Start a validation server
|
|
271
|
+
anyvali serve --port 8080 --schemas ./schemas/
|
|
272
|
+
|
|
273
|
+
# Validate via HTTP
|
|
274
|
+
curl -X POST http://localhost:8080/validate/user \
|
|
275
|
+
-H "Content-Type: application/json" \
|
|
276
|
+
-d '{"name": "Alice", "email": "alice@test.com"}'
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Pre-built binaries for Linux, macOS, and Windows are available on the [releases page](https://github.com/BetterCorp/AnyVali/releases). Docker image: `docker pull anyvali/cli`.
|
|
280
|
+
|
|
281
|
+
See the [CLI Reference](https://docs.anyvali.com/docs/cli) and [HTTP API Reference](https://docs.anyvali.com/docs/api) for full documentation.
|
|
282
|
+
|
|
283
|
+
## Schema Types
|
|
284
|
+
|
|
285
|
+
| Category | Types |
|
|
286
|
+
|----------|-------|
|
|
287
|
+
| **Primitives** | `string`, `bool`, `null` |
|
|
288
|
+
| **Numbers** | `number` (float64), `int` (int64), `float32`, `float64`, `int8`-`int64`, `uint8`-`uint64` |
|
|
289
|
+
| **Special** | `any`, `unknown`, `never` |
|
|
290
|
+
| **Values** | `literal`, `enum` |
|
|
291
|
+
| **Collections** | `array`, `tuple`, `object`, `record` |
|
|
292
|
+
| **Composition** | `union`, `intersection` |
|
|
293
|
+
| **Modifiers** | `optional`, `nullable` |
|
|
294
|
+
|
|
295
|
+
## Documentation
|
|
296
|
+
|
|
297
|
+
| Guide | Description |
|
|
298
|
+
|-------|-------------|
|
|
299
|
+
| [Getting Started](https://docs.anyvali.com) | Installation, API reference, examples |
|
|
300
|
+
| [Numeric Semantics](https://docs.anyvali.com/docs/numeric-semantics) | Why `number` = float64 and `int` = int64 |
|
|
301
|
+
| [Portability Guide](https://docs.anyvali.com/docs/portability-guide) | Design schemas that work across all languages |
|
|
302
|
+
| [SDK Authors Guide](https://docs.anyvali.com/docs/sdk-authors-guide) | Implement a new AnyVali SDK |
|
|
303
|
+
| [Canonical Spec](https://docs.anyvali.com/spec/spec) | The normative specification |
|
|
304
|
+
| [JSON Format](https://docs.anyvali.com/spec/json-format) | Interchange format details |
|
|
305
|
+
| [CLI Reference](https://docs.anyvali.com/docs/cli) | Command-line validation tool |
|
|
306
|
+
| [HTTP API](https://docs.anyvali.com/docs/api) | Validation microservice / sidecar |
|
|
307
|
+
| [Development](https://docs.anyvali.com/docs/development) | Building, testing, contributing |
|
|
308
|
+
|
|
309
|
+
## Repository Layout
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
.
|
|
313
|
+
├── docs/ Documentation guides
|
|
314
|
+
├── spec/ Canonical spec, JSON format, conformance corpus
|
|
315
|
+
├── sdk/
|
|
316
|
+
│ ├── js/ JavaScript / TypeScript SDK
|
|
317
|
+
│ ├── python/ Python SDK
|
|
318
|
+
│ ├── go/ Go SDK
|
|
319
|
+
│ ├── java/ Java SDK
|
|
320
|
+
│ ├── csharp/ C# SDK
|
|
321
|
+
│ ├── rust/ Rust SDK
|
|
322
|
+
│ ├── php/ PHP SDK
|
|
323
|
+
│ ├── ruby/ Ruby SDK
|
|
324
|
+
│ ├── kotlin/ Kotlin SDK
|
|
325
|
+
│ └── cpp/ C++ SDK
|
|
326
|
+
├── cli/ CLI binary and HTTP API server (Go)
|
|
327
|
+
├── runner.sh Build/test/CI runner
|
|
328
|
+
└── site/ anyvali.com source
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Contributing
|
|
332
|
+
|
|
333
|
+
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
./runner.sh help # See all commands
|
|
337
|
+
./runner.sh test js # Test a specific SDK
|
|
338
|
+
./runner.sh ci # Run the full CI pipeline locally
|
|
339
|
+
pwsh -File tools/release/build_release.ps1 # Build release artifacts with Docker
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## License
|
|
343
|
+
|
|
344
|
+
AnyVali is licensed under the [MIT License](LICENSE).
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
<p align="center">
|
|
349
|
+
<a href="https://anyvali.com">anyvali.com</a>
|
|
350
|
+
</p>
|
anyvali-0.0.5/README.md
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.png" alt="AnyVali" width="200" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">AnyVali</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Native validation libraries for 10 languages, one portable schema model.</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://github.com/BetterCorp/AnyVali/actions/workflows/ci.yml"><img src="https://github.com/BetterCorp/AnyVali/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
13
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT" /></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/@anyvali/js"><img src="https://img.shields.io/npm/v/%40anyvali%2Fjs.svg?label=npm" alt="npm" /></a>
|
|
15
|
+
<a href="https://pypi.org/project/anyvali/"><img src="https://img.shields.io/pypi/v/anyvali.svg?label=pypi" alt="PyPI" /></a>
|
|
16
|
+
<a href="https://crates.io/crates/anyvali"><img src="https://img.shields.io/crates/v/anyvali.svg?label=crates.io" alt="crates.io" /></a>
|
|
17
|
+
<a href="https://pkg.go.dev/github.com/BetterCorp/AnyVali/sdk/go"><img src="https://img.shields.io/badge/go-pkg.go.dev-blue.svg" alt="Go" /></a>
|
|
18
|
+
<a href="https://www.nuget.org/packages/AnyVali"><img src="https://img.shields.io/nuget/v/AnyVali.svg?label=nuget" alt="NuGet" /></a>
|
|
19
|
+
<a href="https://rubygems.org/gems/anyvali"><img src="https://img.shields.io/gem/v/anyvali.svg?label=gem" alt="Gem" /></a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://anyvali.com">Website</a> ·
|
|
24
|
+
<a href="https://docs.anyvali.com">Docs</a> ·
|
|
25
|
+
<a href="https://github.com/BetterCorp/AnyVali/issues">Issues</a> ·
|
|
26
|
+
<a href="CONTRIBUTING.md">Contributing</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
AnyVali lets you write validation schemas in your language, then share them across any of 10 supported runtimes via a portable JSON format. Think Zod, but for every language.
|
|
32
|
+
|
|
33
|
+
## Why AnyVali?
|
|
34
|
+
|
|
35
|
+
- **Write schemas natively** -- idiomatic APIs for each language, not a separate DSL
|
|
36
|
+
- **Share across languages** -- export to JSON, import in any other SDK
|
|
37
|
+
- **Safe numeric defaults** -- `number` = float64, `int` = int64 everywhere
|
|
38
|
+
- **Deterministic parsing** -- coerce, default, then validate, in that order
|
|
39
|
+
- **Conformance tested** -- shared test corpus ensures identical behavior across SDKs
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @anyvali/js # JavaScript / TypeScript
|
|
45
|
+
pip install anyvali # Python
|
|
46
|
+
go get github.com/BetterCorp/AnyVali/sdk/go # Go
|
|
47
|
+
cargo add anyvali # Rust
|
|
48
|
+
dotnet add package AnyVali # C#
|
|
49
|
+
composer require anyvali/anyvali # PHP
|
|
50
|
+
gem install anyvali # Ruby
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
<details>
|
|
54
|
+
<summary>Java / Kotlin / C++</summary>
|
|
55
|
+
|
|
56
|
+
**Java (Maven)**
|
|
57
|
+
```xml
|
|
58
|
+
<dependency>
|
|
59
|
+
<groupId>com.anyvali</groupId>
|
|
60
|
+
<artifactId>anyvali</artifactId>
|
|
61
|
+
<version>0.0.1</version>
|
|
62
|
+
</dependency>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Kotlin (Gradle)**
|
|
66
|
+
```kotlin
|
|
67
|
+
implementation("com.anyvali:anyvali:0.0.1")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**C++ (CMake)**
|
|
71
|
+
```cmake
|
|
72
|
+
FetchContent_Declare(anyvali GIT_REPOSITORY https://github.com/BetterCorp/AnyVali)
|
|
73
|
+
FetchContent_MakeAvailable(anyvali)
|
|
74
|
+
target_link_libraries(your_target PRIVATE anyvali)
|
|
75
|
+
```
|
|
76
|
+
</details>
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
Define a schema, parse input, get structured errors or clean data.
|
|
81
|
+
|
|
82
|
+
<table>
|
|
83
|
+
<tr><th>JavaScript / TypeScript</th><th>Python</th></tr>
|
|
84
|
+
<tr>
|
|
85
|
+
<td>
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { string, int, object, array } from "@anyvali/js";
|
|
89
|
+
|
|
90
|
+
const User = object({
|
|
91
|
+
name: string().minLength(1),
|
|
92
|
+
email: string().format('email'),
|
|
93
|
+
age: int().min(0).optional(),
|
|
94
|
+
tags: array(string()).maxItems(5),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Throws on failure
|
|
98
|
+
const user = User.parse(input);
|
|
99
|
+
|
|
100
|
+
// Or get a result object
|
|
101
|
+
const result = User.safeParse(input);
|
|
102
|
+
if (!result.success) {
|
|
103
|
+
console.log(result.issues);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
</td>
|
|
108
|
+
<td>
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
import anyvali as v
|
|
112
|
+
|
|
113
|
+
User = v.object_({
|
|
114
|
+
"name": v.string().min_length(1),
|
|
115
|
+
"email": v.string().format("email"),
|
|
116
|
+
"age": v.int_().min(0).optional(),
|
|
117
|
+
"tags": v.array(v.string()).max_items(5),
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
# Raises on failure
|
|
121
|
+
user = User.parse(input_data)
|
|
122
|
+
|
|
123
|
+
# Or get a result object
|
|
124
|
+
result = User.safe_parse(input_data)
|
|
125
|
+
if not result.success:
|
|
126
|
+
print(result.issues)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
</td>
|
|
130
|
+
</tr>
|
|
131
|
+
</table>
|
|
132
|
+
|
|
133
|
+
<details>
|
|
134
|
+
<summary>Go example</summary>
|
|
135
|
+
|
|
136
|
+
```go
|
|
137
|
+
import av "github.com/BetterCorp/AnyVali/sdk/go"
|
|
138
|
+
|
|
139
|
+
User := av.Object(map[string]av.Schema{
|
|
140
|
+
"name": av.String().MinLength(1),
|
|
141
|
+
"email": av.String().Format("email"),
|
|
142
|
+
"age": av.Optional(av.Int().Min(0)),
|
|
143
|
+
"tags": av.Array(av.String()).MaxItems(5),
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
result := User.SafeParse(input)
|
|
147
|
+
if !result.Success {
|
|
148
|
+
for _, issue := range result.Issues {
|
|
149
|
+
fmt.Printf("[%s] %s at %v\n", issue.Code, issue.Message, issue.Path)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
</details>
|
|
154
|
+
|
|
155
|
+
## Cross-Language Schema Sharing
|
|
156
|
+
|
|
157
|
+
AnyVali's core feature: export a schema from one language, import it in another.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// TypeScript frontend -- export
|
|
161
|
+
const doc = User.export();
|
|
162
|
+
const json = JSON.stringify(doc);
|
|
163
|
+
// Send to your backend, save to DB, put in a config file...
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
# Python backend -- import
|
|
168
|
+
import json, anyvali as v
|
|
169
|
+
|
|
170
|
+
schema = v.import_schema(json.loads(schema_json))
|
|
171
|
+
result = schema.safe_parse(request_body) # Same validation rules!
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Forms
|
|
175
|
+
|
|
176
|
+
The JS SDK also ships a small forms layer for browser-native fields, HTML5 attributes, and AnyVali validation.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { object, string, int } from "@anyvali/js";
|
|
180
|
+
import { initForm } from "@anyvali/js/forms";
|
|
181
|
+
|
|
182
|
+
const Signup = object({
|
|
183
|
+
email: string().format("email"),
|
|
184
|
+
age: int().min(18),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
initForm("#signup", { schema: Signup });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```html
|
|
191
|
+
<form id="signup">
|
|
192
|
+
<input name="email" type="email" />
|
|
193
|
+
<input name="age" type="number" />
|
|
194
|
+
<button type="submit">Create account</button>
|
|
195
|
+
</form>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For JSX-style attribute binding:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { object, string } from "@anyvali/js";
|
|
202
|
+
import { createFormBindings } from "@anyvali/js/forms";
|
|
203
|
+
|
|
204
|
+
const Signup = object({
|
|
205
|
+
email: string().format("email"),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const form = createFormBindings({ schema: Signup });
|
|
209
|
+
|
|
210
|
+
<input {...form.field("email")} />;
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The portable JSON format:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"anyvaliVersion": "1.0",
|
|
218
|
+
"schemaVersion": "1",
|
|
219
|
+
"root": {
|
|
220
|
+
"kind": "object",
|
|
221
|
+
"properties": {
|
|
222
|
+
"name": { "kind": "string", "minLength": 1 },
|
|
223
|
+
"email": { "kind": "string", "format": "email" }
|
|
224
|
+
},
|
|
225
|
+
"required": ["name", "email"],
|
|
226
|
+
"unknownKeys": "reject"
|
|
227
|
+
},
|
|
228
|
+
"definitions": {},
|
|
229
|
+
"extensions": {}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Supported SDKs
|
|
234
|
+
|
|
235
|
+
| Language | Package | Status |
|
|
236
|
+
|----------|---------|--------|
|
|
237
|
+
| JavaScript / TypeScript | [`@anyvali/js`](https://www.npmjs.com/package/@anyvali/js) | v0.0.1 |
|
|
238
|
+
| Python | [`anyvali`](https://pypi.org/project/anyvali/) | v0.0.1 |
|
|
239
|
+
| Go | [`github.com/BetterCorp/AnyVali/sdk/go`](https://pkg.go.dev/github.com/BetterCorp/AnyVali/sdk/go) | v0.0.1 |
|
|
240
|
+
| Java | `com.anyvali:anyvali` | v0.0.1 |
|
|
241
|
+
| C# | [`AnyVali`](https://www.nuget.org/packages/AnyVali) | v0.0.1 |
|
|
242
|
+
| Rust | [`anyvali`](https://crates.io/crates/anyvali) | v0.0.1 |
|
|
243
|
+
| PHP | `anyvali/anyvali` | v0.0.1 |
|
|
244
|
+
| Ruby | [`anyvali`](https://rubygems.org/gems/anyvali) | v0.0.1 |
|
|
245
|
+
| Kotlin | `com.anyvali:anyvali` | v0.0.1 |
|
|
246
|
+
| C++ | `anyvali` (CMake) | v0.0.1 |
|
|
247
|
+
|
|
248
|
+
## CLI & HTTP API
|
|
249
|
+
|
|
250
|
+
Don't need an SDK? Use AnyVali from the command line or as a validation microservice.
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Validate from the command line
|
|
254
|
+
anyvali validate schema.json '{"name": "Alice", "email": "alice@test.com"}'
|
|
255
|
+
|
|
256
|
+
# Pipe from stdin
|
|
257
|
+
cat payload.json | anyvali validate schema.json -
|
|
258
|
+
|
|
259
|
+
# Start a validation server
|
|
260
|
+
anyvali serve --port 8080 --schemas ./schemas/
|
|
261
|
+
|
|
262
|
+
# Validate via HTTP
|
|
263
|
+
curl -X POST http://localhost:8080/validate/user \
|
|
264
|
+
-H "Content-Type: application/json" \
|
|
265
|
+
-d '{"name": "Alice", "email": "alice@test.com"}'
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Pre-built binaries for Linux, macOS, and Windows are available on the [releases page](https://github.com/BetterCorp/AnyVali/releases). Docker image: `docker pull anyvali/cli`.
|
|
269
|
+
|
|
270
|
+
See the [CLI Reference](https://docs.anyvali.com/docs/cli) and [HTTP API Reference](https://docs.anyvali.com/docs/api) for full documentation.
|
|
271
|
+
|
|
272
|
+
## Schema Types
|
|
273
|
+
|
|
274
|
+
| Category | Types |
|
|
275
|
+
|----------|-------|
|
|
276
|
+
| **Primitives** | `string`, `bool`, `null` |
|
|
277
|
+
| **Numbers** | `number` (float64), `int` (int64), `float32`, `float64`, `int8`-`int64`, `uint8`-`uint64` |
|
|
278
|
+
| **Special** | `any`, `unknown`, `never` |
|
|
279
|
+
| **Values** | `literal`, `enum` |
|
|
280
|
+
| **Collections** | `array`, `tuple`, `object`, `record` |
|
|
281
|
+
| **Composition** | `union`, `intersection` |
|
|
282
|
+
| **Modifiers** | `optional`, `nullable` |
|
|
283
|
+
|
|
284
|
+
## Documentation
|
|
285
|
+
|
|
286
|
+
| Guide | Description |
|
|
287
|
+
|-------|-------------|
|
|
288
|
+
| [Getting Started](https://docs.anyvali.com) | Installation, API reference, examples |
|
|
289
|
+
| [Numeric Semantics](https://docs.anyvali.com/docs/numeric-semantics) | Why `number` = float64 and `int` = int64 |
|
|
290
|
+
| [Portability Guide](https://docs.anyvali.com/docs/portability-guide) | Design schemas that work across all languages |
|
|
291
|
+
| [SDK Authors Guide](https://docs.anyvali.com/docs/sdk-authors-guide) | Implement a new AnyVali SDK |
|
|
292
|
+
| [Canonical Spec](https://docs.anyvali.com/spec/spec) | The normative specification |
|
|
293
|
+
| [JSON Format](https://docs.anyvali.com/spec/json-format) | Interchange format details |
|
|
294
|
+
| [CLI Reference](https://docs.anyvali.com/docs/cli) | Command-line validation tool |
|
|
295
|
+
| [HTTP API](https://docs.anyvali.com/docs/api) | Validation microservice / sidecar |
|
|
296
|
+
| [Development](https://docs.anyvali.com/docs/development) | Building, testing, contributing |
|
|
297
|
+
|
|
298
|
+
## Repository Layout
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
.
|
|
302
|
+
├── docs/ Documentation guides
|
|
303
|
+
├── spec/ Canonical spec, JSON format, conformance corpus
|
|
304
|
+
├── sdk/
|
|
305
|
+
│ ├── js/ JavaScript / TypeScript SDK
|
|
306
|
+
│ ├── python/ Python SDK
|
|
307
|
+
│ ├── go/ Go SDK
|
|
308
|
+
│ ├── java/ Java SDK
|
|
309
|
+
│ ├── csharp/ C# SDK
|
|
310
|
+
│ ├── rust/ Rust SDK
|
|
311
|
+
│ ├── php/ PHP SDK
|
|
312
|
+
│ ├── ruby/ Ruby SDK
|
|
313
|
+
│ ├── kotlin/ Kotlin SDK
|
|
314
|
+
│ └── cpp/ C++ SDK
|
|
315
|
+
├── cli/ CLI binary and HTTP API server (Go)
|
|
316
|
+
├── runner.sh Build/test/CI runner
|
|
317
|
+
└── site/ anyvali.com source
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Contributing
|
|
321
|
+
|
|
322
|
+
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
./runner.sh help # See all commands
|
|
326
|
+
./runner.sh test js # Test a specific SDK
|
|
327
|
+
./runner.sh ci # Run the full CI pipeline locally
|
|
328
|
+
pwsh -File tools/release/build_release.ps1 # Build release artifacts with Docker
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## License
|
|
332
|
+
|
|
333
|
+
AnyVali is licensed under the [MIT License](LICENSE).
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
<p align="center">
|
|
338
|
+
<a href="https://anyvali.com">anyvali.com</a>
|
|
339
|
+
</p>
|