@weipertda/sigiljs 0.0.1 → 0.2.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.
- package/README.md +157 -155
- package/docs/README.md +20 -0
- package/docs/arrays.md +55 -0
- package/docs/cli.md +48 -0
- package/docs/compiled-validators.md +42 -0
- package/docs/contributing.md +43 -0
- package/docs/exact-mode.md +82 -0
- package/docs/examples.md +92 -0
- package/docs/introduction.md +29 -0
- package/docs/license.md +23 -0
- package/docs/named-sigils.md +93 -0
- package/docs/objects.md +44 -0
- package/docs/optional.md +48 -0
- package/docs/quickstart.md +65 -0
- package/docs/realtype.md +36 -0
- package/docs/roadmap.md +40 -0
- package/docs/sigils.md +94 -0
- package/docs/{functions.md,maps.md,sets.md,tuples.md,intersection.md,rest.md,examples.md,performance.md,contributing.md,license.md +0 -0
- package/examples/api-response.js +21 -0
- package/examples/api-response.md +27 -0
- package/examples/api-validation.js +0 -0
- package/examples/api-validation.md +0 -0
- package/examples/basic-user.js +16 -0
- package/examples/basic-user.md +22 -0
- package/examples/basic-validation.js +0 -0
- package/examples/basic-validation.md +0 -0
- package/examples/config-file.md +16 -0
- package/examples/config-validation.js +18 -0
- package/examples/config-validation.md +24 -0
- package/examples/login-request.js +19 -0
- package/examples/login-request.md +25 -0
- package/examples/nested-order.js +28 -0
- package/examples/nested-order.md +34 -0
- package/examples/realtype-demo.js +6 -0
- package/examples/realtype-demo.md +12 -0
- package/examples/scripts/config-file.js +0 -0
- package/package.json +18 -10
- package/src/core/assert.js +173 -50
- package/src/core/compile.js +43 -9
- package/src/core/errors.js +29 -6
- package/src/core/normalize.js +1 -1
- package/src/core/parser.js +4 -3
- package/src/core/partial.js +1 -0
- package/src/core/registry.js +33 -0
- package/src/core/validate.js +3 -0
- package/src/index.js +1 -0
- package/src/playground/playground.js +1 -1
- package/src/sigil.js +56 -16
- package/CHANGELOG.md +0 -28
package/README.md
CHANGED
|
@@ -2,260 +2,262 @@
|
|
|
2
2
|
|
|
3
3
|
Write types. Validate reality.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
SigilJS is a tiny, dependency-free JavaScript library for describing and validating data shapes using **sigils** (inline type expressions).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
A sigil is a small expression (type expression) that describes what your data should look like.
|
|
10
|
-
|
|
11
|
-
Sigils are compiled into fast validators, so repeated checks stay efficient.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
No TypeScript.
|
|
15
|
-
|
|
16
|
-
No dependencies.
|
|
17
|
-
|
|
18
|
-
Just JavaScript.
|
|
7
|
+
No TypeScript compiler, no heavy schemas, and zero runtime dependencies. Just write your types as strings, and SigilJS compiles them into blazingly fast validator functions.
|
|
19
8
|
|
|
20
9
|
---
|
|
21
10
|
|
|
22
11
|
## Installation
|
|
23
12
|
|
|
13
|
+
Install using **Bun**:
|
|
24
14
|
```bash
|
|
25
|
-
|
|
26
|
-
bun add @weipertda/sigiljs
|
|
27
|
-
|
|
15
|
+
bun add @antistructured/sigiljs
|
|
28
16
|
```
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
Or using **npm**:
|
|
32
19
|
```bash
|
|
33
|
-
|
|
34
|
-
npm install @weipertda/sigiljs
|
|
35
|
-
|
|
20
|
+
npm install @antistructured/sigiljs
|
|
36
21
|
```
|
|
37
22
|
|
|
38
23
|
---
|
|
39
24
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```javascript
|
|
43
|
-
|
|
44
|
-
import { Sigil } from "@weipertda/sigiljs"
|
|
45
|
-
|
|
46
|
-
const Email = Sigil`string`
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Validate a Value
|
|
51
|
-
|
|
52
|
-
```javascript
|
|
53
|
-
|
|
54
|
-
Email.check("hello@example.com")
|
|
55
|
-
// true
|
|
56
|
-
|
|
57
|
-
Email.check(42)
|
|
58
|
-
// false
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Optional Values
|
|
63
|
-
|
|
64
|
-
Use `?` to mark optional values.
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
|
|
68
|
-
const MaybeName = Sigil`string?`
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Matches:
|
|
25
|
+
## Quick Example
|
|
73
26
|
|
|
74
27
|
```javascript
|
|
28
|
+
import { Sigil } from "@antistructured/sigiljs";
|
|
75
29
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
30
|
+
// 1. Define your data schema using backticks
|
|
31
|
+
const User = Sigil`
|
|
32
|
+
{
|
|
33
|
+
name: string
|
|
34
|
+
age?: number
|
|
35
|
+
tags: string[]
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
// 2. Validate data shapes (returns boolean)
|
|
40
|
+
User.check({
|
|
41
|
+
name: "Alice",
|
|
42
|
+
tags: ["admin", "dev"]
|
|
43
|
+
}); // true
|
|
44
|
+
|
|
45
|
+
User.check({
|
|
46
|
+
name: "Bob",
|
|
47
|
+
age: "thirty", // Wrong type!
|
|
48
|
+
tags: []
|
|
49
|
+
}); // false
|
|
79
50
|
```
|
|
80
51
|
|
|
81
52
|
---
|
|
82
53
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
```javascript
|
|
86
|
-
|
|
87
|
-
const Tags = Sigil`string[]`
|
|
54
|
+
## Why SigilJS?
|
|
88
55
|
|
|
89
|
-
|
|
90
|
-
|
|
56
|
+
JavaScript runtime type checking is notoriously fragile:
|
|
57
|
+
1. `typeof` is notoriously weak and inconsistent (e.g., `typeof []` is `"object"`).
|
|
58
|
+
2. TypeScript types completely disappear at runtime, leaving you vulnerable to invalid API responses, bad database loads, and malformed client payloads.
|
|
59
|
+
3. Existing schema libraries (like Zod) require verbose builder chains and carry significant bundle sizes.
|
|
91
60
|
|
|
92
|
-
|
|
61
|
+
SigilJS solves this by giving you a **clean, string-based type DSL** that looks like standard TypeScript/flow but remains fully active and optimized at runtime.
|
|
93
62
|
|
|
94
63
|
---
|
|
95
64
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```javascript
|
|
65
|
+
## Sigil vs Zod
|
|
99
66
|
|
|
100
|
-
|
|
67
|
+
If you have used validation libraries like Zod, you might wonder how SigilJS compares. Zod is a robust, highly expressive builder library with a powerful ecosystem. However, SigilJS approach is built on a different developer philosophy:
|
|
101
68
|
|
|
102
|
-
|
|
69
|
+
* **Zod** uses an **expressive builder API** to construct validation trees.
|
|
70
|
+
* **SigilJS** uses **readable type expressions** that look like standard JavaScript/TypeScript types, compiled once into optimized runtime validator functions.
|
|
103
71
|
|
|
104
|
-
|
|
72
|
+
### Side-by-Side Comparison
|
|
105
73
|
|
|
106
|
-
|
|
74
|
+
```javascript
|
|
75
|
+
// Zod: Constructing a schema using method-chaining builders
|
|
76
|
+
import { z } from "zod";
|
|
107
77
|
|
|
108
|
-
|
|
78
|
+
const UserZod = z.object({
|
|
79
|
+
name: z.string(),
|
|
80
|
+
age: z.number().optional()
|
|
81
|
+
});
|
|
109
82
|
|
|
110
|
-
|
|
83
|
+
// SigilJS: Writing natural, readable type expressions
|
|
84
|
+
import { Sigil } from "@antistructured/sigiljs";
|
|
111
85
|
|
|
112
|
-
const
|
|
86
|
+
const UserSigil = Sigil`
|
|
113
87
|
{
|
|
114
88
|
name: string
|
|
115
89
|
age?: number
|
|
116
90
|
}
|
|
117
|
-
|
|
118
|
-
|
|
91
|
+
`;
|
|
119
92
|
```
|
|
120
93
|
|
|
121
|
-
|
|
94
|
+
| Feature | Zod | SigilJS |
|
|
95
|
+
|---------|-----|---------|
|
|
96
|
+
| **Philosophy** | Expressive builder API | Readable type expressions compiled to JS closures |
|
|
97
|
+
| **Syntax** | Method chaining (`z.string().optional()`) | Standard inline type DSL (``Sigil`string?```) |
|
|
98
|
+
| **Performance** | Runtime AST traversal | Compiled once to fast, cacheable validator functions |
|
|
99
|
+
| **Recursive Schemas** | Verbose `z.lazy()` wrapper | Native, lazy resolution using Named Sigils |
|
|
100
|
+
| **Bundle Size** | ~15-20kB gzipped | Tiny footprint (< 4kB), zero runtime dependencies |
|
|
122
101
|
|
|
123
102
|
---
|
|
124
103
|
|
|
125
|
-
|
|
104
|
+
## Core API
|
|
105
|
+
|
|
106
|
+
SigilJS exports the core `Sigil` class, along with two convenient single-letter aliases: `S` (recommended shorthand) and `T` (legacy/optional alias).
|
|
126
107
|
|
|
127
108
|
```javascript
|
|
109
|
+
import { Sigil, S, T, SigilValidationError } from "@antistructured/sigiljs";
|
|
128
110
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
email: string
|
|
135
|
-
}
|
|
136
|
-
items: {
|
|
137
|
-
name: string
|
|
138
|
-
price: number
|
|
139
|
-
}[]
|
|
140
|
-
}
|
|
141
|
-
`
|
|
111
|
+
// All three are identical:
|
|
112
|
+
const schema1 = Sigil`string`;
|
|
113
|
+
const schema2 = S`string`;
|
|
114
|
+
const schema3 = T`string`;
|
|
115
|
+
```
|
|
142
116
|
|
|
117
|
+
### `.check(value)`
|
|
118
|
+
Returns a boolean indicating if the value matches the schema.
|
|
119
|
+
```javascript
|
|
120
|
+
S`string`.check("hello"); // true
|
|
121
|
+
S`string`.check(123); // false
|
|
143
122
|
```
|
|
144
123
|
|
|
145
|
-
|
|
124
|
+
### `.assert(value)`
|
|
125
|
+
Validates the data, returning `true` on success. On failure, it throws a structured `SigilValidationError` containing detailed diagnostic information:
|
|
146
126
|
|
|
147
|
-
|
|
127
|
+
```javascript
|
|
128
|
+
try {
|
|
129
|
+
S`{ age: number }`.assert({ age: "thirty" });
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (error instanceof SigilValidationError) {
|
|
132
|
+
console.log(error.code); // "SIGIL_VALIDATION_FAILED"
|
|
133
|
+
console.log(error.message); // "Expected property \"age\" to be number, got string"
|
|
134
|
+
console.log(error.path); // ["age"]
|
|
135
|
+
console.log(error.expected); // "number"
|
|
136
|
+
console.log(error.actual); // "string"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
148
140
|
|
|
149
|
-
|
|
141
|
+
### Compiled Validators (`.validator` & `.compile()`)
|
|
142
|
+
Sigils are parsed and compiled once. If you want to bypass the Sigil instance overhead entirely on ultra-hot paths, you can retrieve the stable pre-compiled validator closure:
|
|
150
143
|
|
|
151
144
|
```javascript
|
|
145
|
+
const User = S`{ name: string }`;
|
|
152
146
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
realType([]) // "array"
|
|
156
|
-
realType(null) // "null"
|
|
157
|
-
realType(new Map()) // "map"
|
|
147
|
+
// Retrieve the pre-compiled function
|
|
148
|
+
const validateUser = User.validator; // or User.compile()
|
|
158
149
|
|
|
150
|
+
validateUser({ name: "Dana" }); // true
|
|
159
151
|
```
|
|
160
152
|
|
|
161
153
|
---
|
|
162
154
|
|
|
163
|
-
##
|
|
164
|
-
|
|
165
|
-
Sigil cleanly solves four problems:
|
|
155
|
+
## Exact Mode
|
|
166
156
|
|
|
167
|
-
|
|
157
|
+
By default, SigilJS runs in **Normal Mode**, allowing objects to have extra undeclared keys. To enforce strict shape matches where extra properties are disallowed, use **Exact Mode** via `Sigil.exact` (or `S.exact` / `T.exact`).
|
|
168
158
|
|
|
169
159
|
```javascript
|
|
160
|
+
// Normal Mode: allows extra keys
|
|
161
|
+
const NormalUser = S`{ name: string }`;
|
|
162
|
+
NormalUser.check({ name: "Dana", admin: true }); // true
|
|
170
163
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
164
|
+
// Exact Mode: rejects extra keys
|
|
165
|
+
const ExactUser = S.exact`{ name: string }`;
|
|
166
|
+
ExactUser.check({ name: "Dana", admin: true }); // false
|
|
174
167
|
```
|
|
175
168
|
|
|
176
|
-
|
|
169
|
+
Exact mode rules apply recursively down nested objects defined inside the exact block.
|
|
177
170
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
* SigilJS solves the runtime side of the problem.
|
|
171
|
+
---
|
|
181
172
|
|
|
182
|
-
|
|
173
|
+
## Named Sigils & Composition
|
|
183
174
|
|
|
184
|
-
|
|
175
|
+
To build large, maintainable schemas or support self-referential/circular schemas, you can define globally-reusable named sigils using `Sigil.define` (or its alias `Sigil.named`).
|
|
185
176
|
|
|
186
|
-
|
|
187
|
-
|
|
177
|
+
```javascript
|
|
178
|
+
// 1. Register a named sigil in the registry
|
|
179
|
+
Sigil.define("Email")`string`;
|
|
188
180
|
|
|
189
|
-
|
|
181
|
+
Sigil.define("Address")`
|
|
182
|
+
{
|
|
183
|
+
street: string
|
|
184
|
+
city: string
|
|
185
|
+
}
|
|
186
|
+
`;
|
|
190
187
|
|
|
188
|
+
// 2. Compose them into larger schemas by referring to them by name
|
|
189
|
+
const User = S`
|
|
190
|
+
{
|
|
191
|
+
name: string
|
|
192
|
+
email: Email
|
|
193
|
+
address: Address
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
191
196
|
```
|
|
192
197
|
|
|
193
|
-
|
|
198
|
+
### Self-Referential / Recursive Schemas
|
|
199
|
+
Since names are resolved lazily at validation/compilation time, schemas can reference themselves or each other circularly:
|
|
194
200
|
|
|
195
|
-
|
|
201
|
+
```javascript
|
|
202
|
+
// Node references Node[]
|
|
203
|
+
Sigil.define("Node")`
|
|
204
|
+
{
|
|
205
|
+
name: string
|
|
206
|
+
children?: Node[]
|
|
207
|
+
}
|
|
208
|
+
`;
|
|
209
|
+
```
|
|
196
210
|
|
|
197
211
|
---
|
|
198
212
|
|
|
199
|
-
|
|
213
|
+
## `realType()`
|
|
200
214
|
|
|
201
|
-
|
|
215
|
+
SigilJS includes `realType()`, an advanced runtime type-detector that resolves the shortcomings of JavaScript's `typeof`.
|
|
202
216
|
|
|
203
217
|
```javascript
|
|
218
|
+
import { realType } from "@antistructured/sigiljs";
|
|
204
219
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
realType('x'); // "string"
|
|
208
|
-
realType(null); // "null"
|
|
220
|
+
realType("hello"); // "string"
|
|
221
|
+
realType(42); // "number"
|
|
209
222
|
realType(NaN); // "nan"
|
|
223
|
+
realType(null); // "null"
|
|
210
224
|
realType([]); // "array"
|
|
211
225
|
realType(new Map()); // "map"
|
|
212
|
-
realType(async
|
|
213
|
-
|
|
226
|
+
realType(async () => {}); // "asyncfunction"
|
|
227
|
+
realType(function* () {}); // "generatorfunction"
|
|
214
228
|
```
|
|
215
229
|
|
|
216
|
-
|
|
230
|
+
### Custom Hooks
|
|
231
|
+
You can supply custom override hooks to map instances of custom classes back to nominal type strings:
|
|
217
232
|
|
|
218
233
|
```javascript
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
234
|
+
class DatabaseConnection {}
|
|
235
|
+
const conn = new DatabaseConnection();
|
|
236
|
+
|
|
237
|
+
realType(conn, {
|
|
238
|
+
hooks: [
|
|
239
|
+
val => val instanceof DatabaseConnection ? 'db_connection' : null
|
|
240
|
+
]
|
|
241
|
+
}); // "db_connection"
|
|
224
242
|
```
|
|
225
243
|
|
|
226
|
-
SigilJS solves runtime type validation with a tiny, dependency-free, runtime-native type system.
|
|
227
|
-
|
|
228
244
|
---
|
|
229
245
|
|
|
230
|
-
##
|
|
246
|
+
## Examples
|
|
231
247
|
|
|
232
|
-
|
|
248
|
+
To see runnable patterns, CLI setups, and advanced test suites, check the [examples/](examples/) directory.
|
|
233
249
|
|
|
234
250
|
---
|
|
235
251
|
|
|
236
|
-
##
|
|
252
|
+
## Roadmap
|
|
237
253
|
|
|
238
|
-
|
|
254
|
+
Upcoming additions to the SigilJS roadmap:
|
|
255
|
+
- [ ] **Sigil.partial**: Construct partial sub-schemas dynamically.
|
|
256
|
+
- [ ] **Format validation rules**: Regex support and common formats (e.g. uuid, ipv4).
|
|
257
|
+
- [ ] **Schema migrations / transformers**: Parse and transform input types into mapped runtime types.
|
|
239
258
|
|
|
240
259
|
---
|
|
241
260
|
|
|
242
261
|
## License
|
|
243
262
|
|
|
244
263
|
MIT
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
## CLI Playground
|
|
249
|
-
|
|
250
|
-
You can securely test out Sigil validator schemas against JSON inputs directly from your shell:
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
|
|
254
|
-
bun run src/playground.js '{"name": "Doug"}' '{name: string, age?: number}'
|
|
255
|
-
# ✅ Validation passed
|
|
256
|
-
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
## Performance Philosophy
|
|
260
|
-
|
|
261
|
-
SigilJS embraces a Functional Core / Imperative Shell architecture. It takes your schema string, turns it into a typed token stream, drops parse grouping artifacts, flattens branches, optimizes primitive unions, and finally generates a blazingly fast validator closure mapped dynamically from the ground up to minimize allocations on the hot path. Repeated tagged template passes are thoroughly memoized.
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# SigilJS Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to the SigilJS documentation! Here you will find guides and specs covering all the core concepts and APIs of SigilJS.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Introduction](introduction.md) — Why SigilJS exists and the problems it solves.
|
|
8
|
+
- [Quickstart](quickstart.md) — Get up and running in 60 seconds.
|
|
9
|
+
- [Sigils](sigils.md) — The core concept of sigils and type blueprints.
|
|
10
|
+
- [Object Schemas](objects.md) — Validating object key/value maps and nested objects.
|
|
11
|
+
- [Arrays](arrays.md) — Working with array syntax, array-postfix operators, and nested arrays.
|
|
12
|
+
- [Optional Fields](optional.md) — Handling optional properties and nullable fields.
|
|
13
|
+
- [Exact Mode](exact-mode.md) — Rejecting extra properties on objects.
|
|
14
|
+
- [Named Sigils & Composition](named-sigils.md) — Reusable schemas, composition, and circular references.
|
|
15
|
+
- [Compiled Validators](compiled-validators.md) — The performance model and direct compiled validator reuse.
|
|
16
|
+
- [realType()](realtype.md) — Deep runtime type-detection with support for custom hooks.
|
|
17
|
+
- [CLI Playground](cli.md) — Testing schemas in your terminal.
|
|
18
|
+
- [Examples](examples.md) — Concrete usage examples.
|
|
19
|
+
- [Roadmap](roadmap.md) — Project milestones towards v1.0.0.
|
|
20
|
+
- [Contributing](contributing.md) — Guidelines for contributing to SigilJS.
|
package/docs/arrays.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Arrays
|
|
2
|
+
|
|
3
|
+
Use ` [] ` to describe arrays.
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
|
|
7
|
+
Sigil`number[]`
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
|
|
15
|
+
const Scores = Sigil`number[]`
|
|
16
|
+
|
|
17
|
+
Scores.check([10, 20, 30])
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
Nested arrays are also supported.
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
|
|
27
|
+
Sigil`string[][]`
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Arrays of Objects
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
|
|
37
|
+
const Orders = Sigil`
|
|
38
|
+
{
|
|
39
|
+
id: string
|
|
40
|
+
items: {
|
|
41
|
+
name: string
|
|
42
|
+
price: number
|
|
43
|
+
}[]
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
|
|
53
|
+
Orders.check(data)
|
|
54
|
+
|
|
55
|
+
```
|
package/docs/cli.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# CLI Playground
|
|
2
|
+
|
|
3
|
+
SigilJS comes with a CLI tool for experimenting with schemas and validating raw JSON data directly in your terminal.
|
|
4
|
+
|
|
5
|
+
## Running the Playground
|
|
6
|
+
|
|
7
|
+
You can run the playground script using Bun:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun run src/playground/playground.js <json-data> <schema>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Argument Format
|
|
14
|
+
1. `<json-data>`: A valid JSON string representing the data you want to validate.
|
|
15
|
+
2. `<schema>`: The Sigil validation schema string (omit the `Sigil` tag and backticks).
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
### Valid Data
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bun run src/playground/playground.js '{"name": "Alice", "age": 30}' '{ name: string, age?: number }'
|
|
23
|
+
# Output:
|
|
24
|
+
# ✅ Validation passed
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Invalid Data
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bun run src/playground/playground.js '{"name": "Alice", "age": "thirty"}' '{ name: string, age?: number }'
|
|
31
|
+
# Output:
|
|
32
|
+
# ❌ Validation failed
|
|
33
|
+
# Code: SIGIL_VALIDATION_FAILED
|
|
34
|
+
# Message: Expected property "age" to be number, got string
|
|
35
|
+
# Path: age
|
|
36
|
+
# Expected: number
|
|
37
|
+
# Actual: string
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Invalid JSON
|
|
41
|
+
|
|
42
|
+
If the data argument is not valid JSON, the playground reports it:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bun run src/playground/playground.js 'invalid-json' '{ name: string }'
|
|
46
|
+
# Output:
|
|
47
|
+
# ❌ Error parsing JSON: Unexpected token i in JSON at position 0
|
|
48
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Compiled Validators
|
|
2
|
+
|
|
3
|
+
Every Sigil is parsed and compiled once, then reused for all subsequent validations. This design ensures maximum performance and minimal runtime overhead.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
When you create a Sigil using the template tag, SigilJS parses the type expression into an Abstract Syntax Tree (AST), normalizes it, and compiles it into an optimized validator function.
|
|
8
|
+
|
|
9
|
+
This compiled validator is cached, meaning that repeated validations do not perform any string parsing, AST traversal, or tokenizing.
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
import { Sigil } from "@antistructured/sigiljs";
|
|
13
|
+
|
|
14
|
+
const User = Sigil`
|
|
15
|
+
{
|
|
16
|
+
name: string
|
|
17
|
+
age: number
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
// Under the hood, this uses the cached, pre-compiled validator function
|
|
22
|
+
User.check({ name: "Alice", age: 30 });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Direct Validator Access
|
|
26
|
+
|
|
27
|
+
If you need direct access to the compiled validator function, you can access the `.validator` property on any Sigil instance, or call `.compile()`. Both return the exact same function reference:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
const User = Sigil`{ name: string }`;
|
|
31
|
+
|
|
32
|
+
// Retrieve the stable, compiled validator function
|
|
33
|
+
const validateUser = User.validator;
|
|
34
|
+
|
|
35
|
+
// Validate directly using the function
|
|
36
|
+
const isValid = validateUser({ name: "Bob" }); // true
|
|
37
|
+
|
|
38
|
+
// User.compile() returns the same cached reference
|
|
39
|
+
console.log(User.validator === User.compile()); // true
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
By reusing the compiled validator reference, you bypass any intermediate Sigil object logic, achieving optimal execution speed.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
First off, thank you for considering contributing to SigilJS.
|
|
4
|
+
|
|
5
|
+
## Local Development
|
|
6
|
+
|
|
7
|
+
SigilJS uses [Bun](https://bun.sh) for package management and testing.
|
|
8
|
+
|
|
9
|
+
1. Clone the repository:
|
|
10
|
+
```bash
|
|
11
|
+
|
|
12
|
+
git clone https://github.com/weipertda/sigiljs.git
|
|
13
|
+
cd sigiljs
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. Install dependencies:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
|
|
21
|
+
bun install
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
3. Run the tests:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
|
|
29
|
+
bun test
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Pull Requests
|
|
34
|
+
|
|
35
|
+
1. **Create a branch** for your feature or bugfix.
|
|
36
|
+
2. **Write tests** that prove your bug was fixed or your feature works.
|
|
37
|
+
3. **Format your code** using `bun run format`.
|
|
38
|
+
4. **Ensure all tests pass** with `bun test`.
|
|
39
|
+
5. Submit your PR with a clear description of the problem and your solution.
|
|
40
|
+
|
|
41
|
+
## Code Style
|
|
42
|
+
|
|
43
|
+
SigilJS keeps things simple and avoids dependencies where possible. If you're adding a feature, consider if it really belongs in the core library or if it can be built in user-land using existing sigils.
|