anyvali 0.3.1

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.
Files changed (204) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +370 -0
  3. package/VERSION +1 -0
  4. package/dist/errors.d.ts +6 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +12 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/format/validators.d.ts +2 -0
  9. package/dist/format/validators.d.ts.map +1 -0
  10. package/dist/format/validators.js +57 -0
  11. package/dist/format/validators.js.map +1 -0
  12. package/dist/forms/index.d.ts +57 -0
  13. package/dist/forms/index.d.ts.map +1 -0
  14. package/dist/forms/index.js +586 -0
  15. package/dist/forms/index.js.map +1 -0
  16. package/dist/index.d.ts +93 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +156 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/infer.d.ts +8 -0
  21. package/dist/infer.d.ts.map +1 -0
  22. package/dist/infer.js +2 -0
  23. package/dist/infer.js.map +1 -0
  24. package/dist/interchange/document.d.ts +5 -0
  25. package/dist/interchange/document.d.ts.map +1 -0
  26. package/dist/interchange/document.js +12 -0
  27. package/dist/interchange/document.js.map +1 -0
  28. package/dist/interchange/exporter.d.ts +7 -0
  29. package/dist/interchange/exporter.d.ts.map +1 -0
  30. package/dist/interchange/exporter.js +7 -0
  31. package/dist/interchange/exporter.js.map +1 -0
  32. package/dist/interchange/importer.d.ts +4 -0
  33. package/dist/interchange/importer.d.ts.map +1 -0
  34. package/dist/interchange/importer.js +229 -0
  35. package/dist/interchange/importer.js.map +1 -0
  36. package/dist/issue-codes.d.ts +19 -0
  37. package/dist/issue-codes.d.ts.map +1 -0
  38. package/dist/issue-codes.js +18 -0
  39. package/dist/issue-codes.js.map +1 -0
  40. package/dist/parse/coerce.d.ts +16 -0
  41. package/dist/parse/coerce.d.ts.map +1 -0
  42. package/dist/parse/coerce.js +115 -0
  43. package/dist/parse/coerce.js.map +1 -0
  44. package/dist/parse/defaults.d.ts +7 -0
  45. package/dist/parse/defaults.d.ts.map +1 -0
  46. package/dist/parse/defaults.js +12 -0
  47. package/dist/parse/defaults.js.map +1 -0
  48. package/dist/parse/parser.d.ts +11 -0
  49. package/dist/parse/parser.d.ts.map +1 -0
  50. package/dist/parse/parser.js +13 -0
  51. package/dist/parse/parser.js.map +1 -0
  52. package/dist/schemas/any.d.ts +7 -0
  53. package/dist/schemas/any.d.ts.map +1 -0
  54. package/dist/schemas/any.js +12 -0
  55. package/dist/schemas/any.js.map +1 -0
  56. package/dist/schemas/array.d.ts +13 -0
  57. package/dist/schemas/array.d.ts.map +1 -0
  58. package/dist/schemas/array.js +73 -0
  59. package/dist/schemas/array.js.map +1 -0
  60. package/dist/schemas/base.d.ts +37 -0
  61. package/dist/schemas/base.d.ts.map +1 -0
  62. package/dist/schemas/base.js +285 -0
  63. package/dist/schemas/base.js.map +1 -0
  64. package/dist/schemas/bool.d.ts +8 -0
  65. package/dist/schemas/bool.d.ts.map +1 -0
  66. package/dist/schemas/bool.js +27 -0
  67. package/dist/schemas/bool.js.map +1 -0
  68. package/dist/schemas/enum.d.ts +9 -0
  69. package/dist/schemas/enum.d.ts.map +1 -0
  70. package/dist/schemas/enum.js +31 -0
  71. package/dist/schemas/enum.js.map +1 -0
  72. package/dist/schemas/index.d.ts +21 -0
  73. package/dist/schemas/index.d.ts.map +1 -0
  74. package/dist/schemas/index.js +21 -0
  75. package/dist/schemas/index.js.map +1 -0
  76. package/dist/schemas/int.d.ts +32 -0
  77. package/dist/schemas/int.d.ts.map +1 -0
  78. package/dist/schemas/int.js +108 -0
  79. package/dist/schemas/int.js.map +1 -0
  80. package/dist/schemas/intersection.d.ts +16 -0
  81. package/dist/schemas/intersection.d.ts.map +1 -0
  82. package/dist/schemas/intersection.js +58 -0
  83. package/dist/schemas/intersection.js.map +1 -0
  84. package/dist/schemas/literal.d.ts +11 -0
  85. package/dist/schemas/literal.d.ts.map +1 -0
  86. package/dist/schemas/literal.js +28 -0
  87. package/dist/schemas/literal.js.map +1 -0
  88. package/dist/schemas/never.d.ts +7 -0
  89. package/dist/schemas/never.d.ts.map +1 -0
  90. package/dist/schemas/never.js +19 -0
  91. package/dist/schemas/never.js.map +1 -0
  92. package/dist/schemas/null.d.ts +7 -0
  93. package/dist/schemas/null.d.ts.map +1 -0
  94. package/dist/schemas/null.js +24 -0
  95. package/dist/schemas/null.js.map +1 -0
  96. package/dist/schemas/nullable.d.ts +10 -0
  97. package/dist/schemas/nullable.d.ts.map +1 -0
  98. package/dist/schemas/nullable.js +29 -0
  99. package/dist/schemas/nullable.js.map +1 -0
  100. package/dist/schemas/number.d.ts +27 -0
  101. package/dist/schemas/number.d.ts.map +1 -0
  102. package/dist/schemas/number.js +134 -0
  103. package/dist/schemas/number.js.map +1 -0
  104. package/dist/schemas/object.d.ts +28 -0
  105. package/dist/schemas/object.d.ts.map +1 -0
  106. package/dist/schemas/object.js +153 -0
  107. package/dist/schemas/object.js.map +1 -0
  108. package/dist/schemas/optional.d.ts +11 -0
  109. package/dist/schemas/optional.d.ts.map +1 -0
  110. package/dist/schemas/optional.js +39 -0
  111. package/dist/schemas/optional.js.map +1 -0
  112. package/dist/schemas/record.d.ts +9 -0
  113. package/dist/schemas/record.d.ts.map +1 -0
  114. package/dist/schemas/record.js +45 -0
  115. package/dist/schemas/record.js.map +1 -0
  116. package/dist/schemas/ref.d.ts +10 -0
  117. package/dist/schemas/ref.d.ts.map +1 -0
  118. package/dist/schemas/ref.js +30 -0
  119. package/dist/schemas/ref.js.map +1 -0
  120. package/dist/schemas/string.d.ts +29 -0
  121. package/dist/schemas/string.d.ts.map +1 -0
  122. package/dist/schemas/string.js +181 -0
  123. package/dist/schemas/string.js.map +1 -0
  124. package/dist/schemas/tuple.d.ts +14 -0
  125. package/dist/schemas/tuple.d.ts.map +1 -0
  126. package/dist/schemas/tuple.js +59 -0
  127. package/dist/schemas/tuple.js.map +1 -0
  128. package/dist/schemas/union.d.ts +9 -0
  129. package/dist/schemas/union.d.ts.map +1 -0
  130. package/dist/schemas/union.js +45 -0
  131. package/dist/schemas/union.js.map +1 -0
  132. package/dist/schemas/unknown.d.ts +7 -0
  133. package/dist/schemas/unknown.d.ts.map +1 -0
  134. package/dist/schemas/unknown.js +12 -0
  135. package/dist/schemas/unknown.js.map +1 -0
  136. package/dist/types.d.ts +132 -0
  137. package/dist/types.d.ts.map +1 -0
  138. package/dist/types.js +3 -0
  139. package/dist/types.js.map +1 -0
  140. package/dist/util.d.ts +6 -0
  141. package/dist/util.d.ts.map +1 -0
  142. package/dist/util.js +12 -0
  143. package/dist/util.js.map +1 -0
  144. package/package.json +41 -0
  145. package/sdk/js/CHANGELOG.md +13 -0
  146. package/src/errors.ts +17 -0
  147. package/src/format/validators.ts +71 -0
  148. package/src/forms/index.ts +789 -0
  149. package/src/index.ts +285 -0
  150. package/src/infer.ts +12 -0
  151. package/src/interchange/document.ts +18 -0
  152. package/src/interchange/exporter.ts +12 -0
  153. package/src/interchange/importer.ts +285 -0
  154. package/src/issue-codes.ts +19 -0
  155. package/src/parse/coerce.ts +133 -0
  156. package/src/parse/defaults.ts +15 -0
  157. package/src/parse/parser.ts +19 -0
  158. package/src/schemas/any.ts +14 -0
  159. package/src/schemas/array.ts +83 -0
  160. package/src/schemas/base.ts +322 -0
  161. package/src/schemas/bool.ts +30 -0
  162. package/src/schemas/enum.ts +37 -0
  163. package/src/schemas/index.ts +30 -0
  164. package/src/schemas/int.ts +129 -0
  165. package/src/schemas/intersection.ts +81 -0
  166. package/src/schemas/literal.ts +34 -0
  167. package/src/schemas/never.ts +21 -0
  168. package/src/schemas/null.ts +26 -0
  169. package/src/schemas/nullable.ts +36 -0
  170. package/src/schemas/number.ts +151 -0
  171. package/src/schemas/object.ts +203 -0
  172. package/src/schemas/optional.ts +49 -0
  173. package/src/schemas/record.ts +55 -0
  174. package/src/schemas/ref.ts +35 -0
  175. package/src/schemas/string.ts +192 -0
  176. package/src/schemas/tuple.ts +74 -0
  177. package/src/schemas/union.ts +53 -0
  178. package/src/schemas/unknown.ts +14 -0
  179. package/src/types.ts +239 -0
  180. package/src/util.ts +9 -0
  181. package/tests/conformance/runner.test.ts +28 -0
  182. package/tests/conformance/runner.ts +137 -0
  183. package/tests/forms.test.ts +146 -0
  184. package/tests/unit/coerce.test.ts +136 -0
  185. package/tests/unit/collections.test.ts +99 -0
  186. package/tests/unit/composition.test.ts +80 -0
  187. package/tests/unit/date-format.test.ts +18 -0
  188. package/tests/unit/default-mutation.test.ts +32 -0
  189. package/tests/unit/defaults.test.ts +49 -0
  190. package/tests/unit/errors.test.ts +53 -0
  191. package/tests/unit/export.test.ts +270 -0
  192. package/tests/unit/inference.test.ts +306 -0
  193. package/tests/unit/interchange.test.ts +191 -0
  194. package/tests/unit/number.test.ts +195 -0
  195. package/tests/unit/object.test.ts +208 -0
  196. package/tests/unit/parser.test.ts +151 -0
  197. package/tests/unit/primitives.test.ts +111 -0
  198. package/tests/unit/security-recursion.test.ts +105 -0
  199. package/tests/unit/security.test.ts +945 -0
  200. package/tests/unit/shared-ref-falsepos.test.ts +33 -0
  201. package/tests/unit/string-pattern-redos.test.ts +46 -0
  202. package/tests/unit/string.test.ts +147 -0
  203. package/tsconfig.json +21 -0
  204. package/vitest.config.ts +7 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ ## [0.3.1](https://github.com/BetterCorp/AnyVali/compare/js-v0.3.0...js-v0.3.1) (2026-03-31)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * resolve merge conflict, keep simplified build-release ([7807802](https://github.com/BetterCorp/AnyVali/commit/780780259da32288e030bb046571e4f37fd69ec6))
9
+
10
+ ## [0.3.0](https://github.com/BetterCorp/AnyVali/compare/js-v0.2.0...js-v0.3.0) (2026-03-30)
11
+
12
+
13
+ ### Features
14
+
15
+ * **js:** add SchemaAny type alias for generic constraints ([6c1dbe5](https://github.com/BetterCorp/AnyVali/commit/6c1dbe5e4ed1ddfc71e92657ab44a1177857d6ac))
16
+
17
+ ## [0.2.0](https://github.com/BetterCorp/AnyVali/compare/js-v0.1.3...js-v0.2.0) (2026-03-30)
18
+
19
+
20
+ ### Features
21
+
22
+ * add static type inference to all 10 SDKs ([f2902ec](https://github.com/BetterCorp/AnyVali/commit/f2902ecf9cdd1153da355b8e27fae07c36486138))
23
+
24
+ ## [0.1.3](https://github.com/BetterCorp/AnyVali/compare/js-v0.1.2...js-v0.1.3) (2026-03-30)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * force version bump across all packages ([959aaee](https://github.com/BetterCorp/AnyVali/commit/959aaeee8617156d7b531fa3fbfb588d99081ea7))
30
+ * **js:** update repository URL to BetterCorp/AnyVali ([3dae779](https://github.com/BetterCorp/AnyVali/commit/3dae779cd07c1aeb307ea9b181542073f6dba8bc))
31
+
32
+ ## [0.1.2](https://github.com/BetterCorp/AnyVali/compare/js-v0.1.1...js-v0.1.2) (2026-03-30)
33
+
34
+
35
+ ### Bug Fixes
36
+
37
+ * resolve merge conflict in release-please manifest ([5c8098c](https://github.com/BetterCorp/AnyVali/commit/5c8098ce9ee5ab0891a830c4fa82f7db20e5d9cf))
38
+
39
+ ## [0.1.1](https://github.com/BetterCorp/AnyVali/compare/js-v0.1.0...js-v0.1.1) (2026-03-30)
40
+
41
+
42
+ ### Bug Fixes
43
+
44
+ * synchronize all SDK releases ([904dda4](https://github.com/BetterCorp/AnyVali/commit/904dda427fe45aeb2321ceaf32f71683d71e96d2))
package/README.md ADDED
@@ -0,0 +1,370 @@
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://codecov.io/gh/BetterCorp/AnyVali"><img src="https://codecov.io/gh/BetterCorp/AnyVali/graph/badge.svg?token=3H068H51QN" alt="codecov" /></a>
14
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT" /></a>
15
+ <a href="https://www.npmjs.com/package/anyvali"><img src="https://img.shields.io/npm/v/anyvali.svg?label=npm" alt="npm" /></a>
16
+ <a href="https://pypi.org/project/anyvali/"><img src="https://img.shields.io/pypi/v/anyvali.svg?label=pypi" alt="PyPI" /></a>
17
+ <a href="https://crates.io/crates/anyvali"><img src="https://img.shields.io/crates/v/anyvali.svg?label=crates.io" alt="crates.io" /></a>
18
+ <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>
19
+ <a href="https://www.nuget.org/packages/AnyVali"><img src="https://img.shields.io/nuget/v/AnyVali.svg?label=nuget" alt="NuGet" /></a>
20
+ <a href="https://rubygems.org/gems/anyvali"><img src="https://img.shields.io/gem/v/anyvali.svg?label=gem" alt="Gem" /></a>
21
+ </p>
22
+
23
+ <p align="center">
24
+ <a href="https://anyvali.com">Website</a> &middot;
25
+ <a href="https://docs.anyvali.com">Docs</a> &middot;
26
+ <a href="https://github.com/BetterCorp/AnyVali/issues">Issues</a> &middot;
27
+ <a href="CONTRIBUTING.md">Contributing</a>
28
+ </p>
29
+
30
+ ---
31
+
32
+ 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.
33
+
34
+ ## Why AnyVali?
35
+
36
+ - **Write schemas natively** -- idiomatic APIs for each language, not a separate DSL
37
+ - **Share across languages** -- export to JSON, import in any other SDK
38
+ - **Safe numeric defaults** -- `number` = float64, `int` = int64 everywhere
39
+ - **Deterministic parsing** -- coerce, default, then validate, in that order
40
+ - **Conformance tested** -- shared test corpus ensures identical behavior across SDKs
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ npm install anyvali # JavaScript / TypeScript
46
+ pip install anyvali # Python
47
+ go get github.com/BetterCorp/AnyVali/sdk/go # Go
48
+ cargo add anyvali # Rust
49
+ dotnet add package AnyVali # C#
50
+ composer require anyvali/anyvali # PHP
51
+ gem install anyvali # Ruby
52
+ ```
53
+
54
+ <details>
55
+ <summary>Java / Kotlin / C++</summary>
56
+
57
+ **Java (Maven)**
58
+ ```xml
59
+ <dependency>
60
+ <groupId>com.anyvali</groupId>
61
+ <artifactId>anyvali</artifactId>
62
+ <version>0.0.1</version>
63
+ </dependency>
64
+ ```
65
+
66
+ **Kotlin (Gradle)**
67
+ ```kotlin
68
+ implementation("com.anyvali:anyvali:0.0.1")
69
+ ```
70
+
71
+ **C++ (CMake)**
72
+ ```cmake
73
+ FetchContent_Declare(anyvali GIT_REPOSITORY https://github.com/BetterCorp/AnyVali)
74
+ FetchContent_MakeAvailable(anyvali)
75
+ target_link_libraries(your_target PRIVATE anyvali)
76
+ ```
77
+ </details>
78
+
79
+ ## Quick Start
80
+
81
+ Define a schema, parse input, get structured errors or clean data.
82
+
83
+ <table>
84
+ <tr><th>JavaScript / TypeScript</th><th>Python</th></tr>
85
+ <tr>
86
+ <td>
87
+
88
+ ```typescript
89
+ import { string, int, object, array } from "anyvali";
90
+
91
+ const User = object({
92
+ name: string().minLength(1),
93
+ email: string().format('email'),
94
+ age: int().min(0).optional(),
95
+ tags: array(string()).maxItems(5),
96
+ });
97
+
98
+ // Throws on failure
99
+ const user = User.parse(input);
100
+
101
+ // Or get a result object
102
+ const result = User.safeParse(input);
103
+ if (!result.success) {
104
+ console.log(result.issues);
105
+ }
106
+ ```
107
+
108
+ </td>
109
+ <td>
110
+
111
+ ```python
112
+ import anyvali as v
113
+
114
+ User = v.object_({
115
+ "name": v.string().min_length(1),
116
+ "email": v.string().format("email"),
117
+ "age": v.int_().min(0).optional(),
118
+ "tags": v.array(v.string()).max_items(5),
119
+ })
120
+
121
+ # Raises on failure
122
+ user = User.parse(input_data)
123
+
124
+ # Or get a result object
125
+ result = User.safe_parse(input_data)
126
+ if not result.success:
127
+ print(result.issues)
128
+ ```
129
+
130
+ </td>
131
+ </tr>
132
+ </table>
133
+
134
+ <details>
135
+ <summary>Go example</summary>
136
+
137
+ ```go
138
+ import av "github.com/BetterCorp/AnyVali/sdk/go"
139
+
140
+ User := av.Object(map[string]av.Schema{
141
+ "name": av.String().MinLength(1),
142
+ "email": av.String().Format("email"),
143
+ "age": av.Optional(av.Int().Min(0)),
144
+ "tags": av.Array(av.String()).MaxItems(5),
145
+ })
146
+
147
+ result := User.SafeParse(input)
148
+ if !result.Success {
149
+ for _, issue := range result.Issues {
150
+ fmt.Printf("[%s] %s at %v\n", issue.Code, issue.Message, issue.Path)
151
+ }
152
+ }
153
+ ```
154
+ </details>
155
+
156
+ ## Type Inference
157
+
158
+ All 10 SDKs now provide static type inference, so parsed values carry the correct type without manual casts. The TypeScript SDK offers full Zod-style `Infer<T>`:
159
+
160
+ ```typescript
161
+ import { object, string, int, type Infer } from "anyvali";
162
+
163
+ const User = object({
164
+ name: string().minLength(1),
165
+ email: string().format('email'),
166
+ age: int().min(0).optional(),
167
+ });
168
+
169
+ type User = Infer<typeof User>;
170
+ // => { name: string; email: string; age?: number | undefined }
171
+
172
+ const user = User.parse(input); // fully typed, no cast needed
173
+ ```
174
+
175
+ Other SDKs use the type inference mechanism native to each language:
176
+
177
+ - **Python** -- `BaseSchema(Generic[T])`, `ParseResult(Generic[T])`; `parse()` returns `T`
178
+ - **C# / Kotlin** -- `Schema<T>` generic base class, `ParseResult<T>`
179
+ - **Java** -- `Schema<T>` generic base, `ParseResult<T>` record
180
+ - **Go** -- `TypedParse[T]()` and `TypedSafeParse[T]()` generic helper functions
181
+ - **Rust** -- `TypedSchema` trait with associated `Output` type, `parse_as<T>()` free function
182
+ - **C++** -- Template `parse_as<T>()` and `safe_parse_as<T>()` helpers
183
+ - **PHP** -- `@template` phpDoc annotations for PHPStan/Psalm
184
+ - **Ruby** -- RBS type signature file for Steep/Sorbet
185
+
186
+ ## Cross-Language Schema Sharing
187
+
188
+ AnyVali's core feature: export a schema from one language, import it in another.
189
+
190
+ ```typescript
191
+ // TypeScript frontend -- export
192
+ const doc = User.export();
193
+ const json = JSON.stringify(doc);
194
+ // Send to your backend, save to DB, put in a config file...
195
+ ```
196
+
197
+ ```python
198
+ # Python backend -- import
199
+ import json, anyvali as v
200
+
201
+ schema = v.import_schema(json.loads(schema_json))
202
+ result = schema.safe_parse(request_body) # Same validation rules!
203
+ ```
204
+
205
+ ## Forms
206
+
207
+ The JS SDK also ships a small forms layer for browser-native fields, HTML5 attributes, and AnyVali validation.
208
+
209
+ ```typescript
210
+ import { object, string, int } from "anyvali";
211
+ import { initForm } from "anyvali/forms";
212
+
213
+ const Signup = object({
214
+ email: string().format("email"),
215
+ age: int().min(18),
216
+ });
217
+
218
+ initForm("#signup", { schema: Signup });
219
+ ```
220
+
221
+ ```html
222
+ <form id="signup">
223
+ <input name="email" type="email" />
224
+ <input name="age" type="number" />
225
+ <button type="submit">Create account</button>
226
+ </form>
227
+ ```
228
+
229
+ For JSX-style attribute binding:
230
+
231
+ ```tsx
232
+ import { object, string } from "anyvali";
233
+ import { createFormBindings } from "anyvali/forms";
234
+
235
+ const Signup = object({
236
+ email: string().format("email"),
237
+ });
238
+
239
+ const form = createFormBindings({ schema: Signup });
240
+
241
+ <input {...form.field("email")} />;
242
+ ```
243
+
244
+ The portable JSON format:
245
+
246
+ ```json
247
+ {
248
+ "anyvaliVersion": "1.0",
249
+ "schemaVersion": "1",
250
+ "root": {
251
+ "kind": "object",
252
+ "properties": {
253
+ "name": { "kind": "string", "minLength": 1 },
254
+ "email": { "kind": "string", "format": "email" }
255
+ },
256
+ "required": ["name", "email"],
257
+ "unknownKeys": "strip"
258
+ },
259
+ "definitions": {},
260
+ "extensions": {}
261
+ }
262
+ ```
263
+
264
+ ## Supported SDKs
265
+
266
+ | Language | Package | Status |
267
+ |----------|---------|--------|
268
+ | JavaScript / TypeScript | [`anyvali`](https://www.npmjs.com/package/anyvali) | v0.0.1 |
269
+ | Python | [`anyvali`](https://pypi.org/project/anyvali/) | v0.0.1 |
270
+ | Go | [`github.com/BetterCorp/AnyVali/sdk/go`](https://pkg.go.dev/github.com/BetterCorp/AnyVali/sdk/go) | v0.0.1 |
271
+ | Java | `com.anyvali:anyvali` | v0.0.1 |
272
+ | C# | [`AnyVali`](https://www.nuget.org/packages/AnyVali) | v0.0.1 |
273
+ | Rust | [`anyvali`](https://crates.io/crates/anyvali) | v0.0.1 |
274
+ | PHP | `anyvali/anyvali` | v0.0.1 |
275
+ | Ruby | [`anyvali`](https://rubygems.org/gems/anyvali) | v0.0.1 |
276
+ | Kotlin | `com.anyvali:anyvali` | v0.0.1 |
277
+ | C++ | `anyvali` (CMake) | v0.0.1 |
278
+
279
+ ## CLI & HTTP API
280
+
281
+ Don't need an SDK? Use AnyVali from the command line or as a validation microservice.
282
+
283
+ ```bash
284
+ # Validate from the command line
285
+ anyvali validate schema.json '{"name": "Alice", "email": "alice@test.com"}'
286
+
287
+ # Pipe from stdin
288
+ cat payload.json | anyvali validate schema.json -
289
+
290
+ # Start a validation server
291
+ anyvali serve --port 8080 --schemas ./schemas/
292
+
293
+ # Validate via HTTP
294
+ curl -X POST http://localhost:8080/validate/user \
295
+ -H "Content-Type: application/json" \
296
+ -d '{"name": "Alice", "email": "alice@test.com"}'
297
+ ```
298
+
299
+ 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`.
300
+
301
+ See the [CLI Reference](https://docs.anyvali.com/docs/cli) and [HTTP API Reference](https://docs.anyvali.com/docs/api) for full documentation.
302
+
303
+ ## Schema Types
304
+
305
+ | Category | Types |
306
+ |----------|-------|
307
+ | **Primitives** | `string`, `bool`, `null` |
308
+ | **Numbers** | `number` (float64), `int` (int64), `float32`, `float64`, `int8`-`int64`, `uint8`-`uint64` |
309
+ | **Special** | `any`, `unknown`, `never` |
310
+ | **Values** | `literal`, `enum` |
311
+ | **Collections** | `array`, `tuple`, `object`, `record` |
312
+ | **Composition** | `union`, `intersection` |
313
+ | **Modifiers** | `optional`, `nullable` |
314
+
315
+ ## Documentation
316
+
317
+ | Guide | Description |
318
+ |-------|-------------|
319
+ | [Getting Started](https://docs.anyvali.com) | Installation, API reference, examples |
320
+ | [Numeric Semantics](https://docs.anyvali.com/docs/numeric-semantics) | Why `number` = float64 and `int` = int64 |
321
+ | [Portability Guide](https://docs.anyvali.com/docs/portability-guide) | Design schemas that work across all languages |
322
+ | [SDK Authors Guide](https://docs.anyvali.com/docs/sdk-authors-guide) | Implement a new AnyVali SDK |
323
+ | [Canonical Spec](https://docs.anyvali.com/spec/spec) | The normative specification |
324
+ | [JSON Format](https://docs.anyvali.com/spec/json-format) | Interchange format details |
325
+ | [CLI Reference](https://docs.anyvali.com/docs/cli) | Command-line validation tool |
326
+ | [HTTP API](https://docs.anyvali.com/docs/api) | Validation microservice / sidecar |
327
+ | [Development](https://docs.anyvali.com/docs/development) | Building, testing, contributing |
328
+
329
+ ## Repository Layout
330
+
331
+ ```
332
+ .
333
+ ├── docs/ Documentation guides
334
+ ├── spec/ Canonical spec, JSON format, conformance corpus
335
+ ├── sdk/
336
+ │ ├── js/ JavaScript / TypeScript SDK
337
+ │ ├── python/ Python SDK
338
+ │ ├── go/ Go SDK
339
+ │ ├── java/ Java SDK
340
+ │ ├── csharp/ C# SDK
341
+ │ ├── rust/ Rust SDK
342
+ │ ├── php/ PHP SDK
343
+ │ ├── ruby/ Ruby SDK
344
+ │ ├── kotlin/ Kotlin SDK
345
+ │ └── cpp/ C++ SDK
346
+ ├── cli/ CLI binary and HTTP API server (Go)
347
+ ├── runner.sh Build/test/CI runner
348
+ └── site/ anyvali.com source
349
+ ```
350
+
351
+ ## Contributing
352
+
353
+ Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
354
+
355
+ ```bash
356
+ ./runner.sh help # See all commands
357
+ ./runner.sh test js # Test a specific SDK
358
+ ./runner.sh ci # Run the full CI pipeline locally
359
+ pwsh -File tools/release/build_release.ps1 # Build release artifacts with Docker
360
+ ```
361
+
362
+ ## License
363
+
364
+ AnyVali is licensed under the [MIT License](LICENSE).
365
+
366
+ ---
367
+
368
+ <p align="center">
369
+ <a href="https://anyvali.com">anyvali.com</a>
370
+ </p>
package/VERSION ADDED
@@ -0,0 +1 @@
1
+ Mon Mar 30 06:29:38 SAST 2026
@@ -0,0 +1,6 @@
1
+ import type { ValidationIssue } from "./types.js";
2
+ export declare class ValidationError extends Error {
3
+ readonly issues: ValidationIssue[];
4
+ constructor(issues: ValidationIssue[]);
5
+ }
6
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAgB,MAAM,EAAE,eAAe,EAAE,CAAC;gBAE9B,MAAM,EAAE,eAAe,EAAE;CAWtC"}
package/dist/errors.js ADDED
@@ -0,0 +1,12 @@
1
+ export class ValidationError extends Error {
2
+ issues;
3
+ constructor(issues) {
4
+ const message = issues
5
+ .map((i) => `[${i.code}] ${i.path.length > 0 ? i.path.join(".") + ": " : ""}${i.message}`)
6
+ .join("\n");
7
+ super(message);
8
+ this.name = "ValidationError";
9
+ this.issues = issues;
10
+ }
11
+ }
12
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxB,MAAM,CAAoB;IAE1C,YAAY,MAAyB;QACnC,MAAM,OAAO,GAAG,MAAM;aACnB,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAChF;aACA,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export declare function validateFormat(value: string, format: string): boolean;
2
+ //# sourceMappingURL=validators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/format/validators.ts"],"names":[],"mappings":"AAiEA,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAKrE"}
@@ -0,0 +1,57 @@
1
+ // Email must have at least one dot after the @
2
+ const EMAIL_RE = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;
3
+ const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
4
+ const IPV4_RE = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
5
+ const IPV6_RE = /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|:(?::[0-9a-fA-F]{1,4}){1,7}|::)$/;
6
+ // ISO 8601 date: YYYY-MM-DD
7
+ const DATE_RE = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;
8
+ // ISO 8601 date-time: YYYY-MM-DDTHH:MM:SS with optional fractional seconds and timezone
9
+ const DATETIME_RE = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
10
+ function isValidDate(str) {
11
+ if (!DATE_RE.test(str))
12
+ return false;
13
+ const [y, m, d] = str.split("-").map(Number);
14
+ const date = new Date(y, m - 1, d);
15
+ // new Date() maps years 0-99 to 1900-1999; undo that so early ISO years
16
+ // (e.g. 0050-01-01) are not falsely rejected.
17
+ date.setFullYear(y);
18
+ return (date.getFullYear() === y &&
19
+ date.getMonth() === m - 1 &&
20
+ date.getDate() === d);
21
+ }
22
+ function isValidDateTime(str) {
23
+ if (!DATETIME_RE.test(str))
24
+ return false;
25
+ // Validate the date portion
26
+ const datePart = str.substring(0, 10);
27
+ return isValidDate(datePart);
28
+ }
29
+ function isValidUrl(str) {
30
+ try {
31
+ const url = new URL(str);
32
+ // Only accept http and https protocols
33
+ return url.protocol === "http:" || url.protocol === "https:";
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ const FORMAT_VALIDATORS = {
40
+ email: (val) => EMAIL_RE.test(val),
41
+ url: isValidUrl,
42
+ uuid: (val) => UUID_RE.test(val),
43
+ ipv4: (val) => IPV4_RE.test(val),
44
+ ipv6: (val) => IPV6_RE.test(val),
45
+ date: isValidDate,
46
+ "date-time": isValidDateTime,
47
+ };
48
+ const FORMAT_NAME_RE = /^[A-Za-z][A-Za-z0-9-]*$/;
49
+ export function validateFormat(value, format) {
50
+ if (!FORMAT_NAME_RE.test(format))
51
+ return false;
52
+ const validator = FORMAT_VALIDATORS[format];
53
+ if (!validator)
54
+ return true; // valid custom formats pass
55
+ return validator(value);
56
+ }
57
+ //# sourceMappingURL=validators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/format/validators.ts"],"names":[],"mappings":"AAEA,+CAA+C;AAC/C,MAAM,QAAQ,GACZ,sIAAsI,CAAC;AAEzI,MAAM,OAAO,GACX,+EAA+E,CAAC;AAElF,MAAM,OAAO,GACX,mFAAmF,CAAC;AAEtF,MAAM,OAAO,GACX,uZAAuZ,CAAC;AAE1Z,4BAA4B;AAC5B,MAAM,OAAO,GAAG,mDAAmD,CAAC;AAEpE,wFAAwF;AACxF,MAAM,WAAW,GACf,kIAAkI,CAAC;AAErI,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACnC,wEAAwE;IACxE,8CAA8C;IAC9C,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,CACL,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC;QACxB,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC;QACzB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CACrB,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IAClC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,4BAA4B;IAC5B,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtC,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,uCAAuC;QACvC,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,iBAAiB,GAAmD;IACxE,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;IAClC,GAAG,EAAE,UAAU;IACf,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;IAChC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;IAChC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;IAChC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,eAAe;CAC7B,CAAC;AAEF,MAAM,cAAc,GAAG,yBAAyB,CAAC;AAEjD,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,MAAc;IAC1D,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAsB,CAAC,CAAC;IAC5D,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC,CAAC,4BAA4B;IACzD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { BaseSchema } from "../schemas/base.js";
2
+ import type { AnyValiDocument, ParseResult } from "../types.js";
3
+ export type FormSchemaSource = AnyValiDocument | BaseSchema;
4
+ export type ValidationTrigger = "input" | "change" | "blur" | "submit";
5
+ export interface InitFormOptions {
6
+ schema: FormSchemaSource;
7
+ validateOn?: ValidationTrigger[];
8
+ nativeValidation?: boolean;
9
+ htmx?: boolean;
10
+ reportValidity?: boolean;
11
+ }
12
+ export interface FormController {
13
+ form: HTMLFormElement;
14
+ document: AnyValiDocument;
15
+ validate(): boolean;
16
+ getValues(): unknown;
17
+ getResult(): ParseResult<unknown>;
18
+ destroy(): void;
19
+ }
20
+ export interface CreateFormBindingsOptions {
21
+ schema: FormSchemaSource;
22
+ errorIdPrefix?: string;
23
+ }
24
+ export interface HtmxConfig {
25
+ get?: string;
26
+ post?: string;
27
+ put?: string;
28
+ patch?: string;
29
+ delete?: string;
30
+ target?: string;
31
+ swap?: string;
32
+ trigger?: string;
33
+ select?: string;
34
+ validate?: boolean;
35
+ confirm?: string;
36
+ include?: string;
37
+ encoding?: string;
38
+ ext?: string;
39
+ indicator?: string;
40
+ pushUrl?: string;
41
+ replaceUrl?: string;
42
+ }
43
+ export declare function createFormBindings(options: CreateFormBindingsOptions): {
44
+ field(path: string, attrs?: Record<string, unknown>): {
45
+ [x: string]: unknown;
46
+ };
47
+ errorSlot(path: string, attrs?: Record<string, unknown>): {
48
+ id: string;
49
+ "data-anyvali-error-for": string;
50
+ "aria-live": string;
51
+ };
52
+ htmx(config: HtmxConfig): Record<string, string>;
53
+ init(target: string | HTMLFormElement, initOptions?: Omit<InitFormOptions, "schema">): FormController;
54
+ };
55
+ export declare function initForm(target: string | HTMLFormElement, options: InitFormOptions): FormController;
56
+ export declare function getFieldAttributes(schema: FormSchemaSource, path: string, errorIdPrefix?: string): Record<string, unknown>;
57
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EACV,eAAe,EAIf,WAAW,EAIZ,MAAM,aAAa,CAAC;AAGrB,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,UAAU,CAAC;AAC5D,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,IAAI,OAAO,CAAC;IACpB,SAAS,IAAI,OAAO,CAAC;IACrB,SAAS,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,gBAAgB,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAuBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB;gBAKrD,MAAM,UAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;oBAOlC,MAAM,UAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;iBASzC,UAAU;iBAiCV,MAAM,GAAG,eAAe,gBAAe,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC;EAOtF;AAED,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,GAAG,eAAe,EAChC,OAAO,EAAE,eAAe,GACvB,cAAc,CAqIhB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,MAAM,EACZ,aAAa,SAAY,GACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgCzB"}