@weipertda/sigiljs 0.0.3 → 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 +14 -6
- 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
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Exact Mode
|
|
2
|
+
|
|
3
|
+
By default, SigilJS allows objects to have extra, undeclared properties. This is called **Normal Mode**.
|
|
4
|
+
|
|
5
|
+
If you want to strictly reject any undeclared properties, you can use **Exact Mode** via `Sigil.exact`.
|
|
6
|
+
|
|
7
|
+
## Normal Mode vs. Exact Mode
|
|
8
|
+
|
|
9
|
+
### Normal Mode (Default)
|
|
10
|
+
In normal mode, extra keys are ignored and permitted. This is useful when you only care about a subset of the fields in a payload (e.g. from an API response).
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
import { Sigil } from "@antistructured/sigiljs";
|
|
14
|
+
|
|
15
|
+
const User = Sigil`
|
|
16
|
+
{
|
|
17
|
+
name: string
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
User.check({ name: "Dana" }); // true
|
|
22
|
+
User.check({ name: "Dana", admin: true }); // true (extra keys are allowed)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Exact Mode
|
|
26
|
+
In exact mode, any undeclared keys are rejected, causing validation to fail.
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
const StrictUser = Sigil.exact`
|
|
30
|
+
{
|
|
31
|
+
name: string
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
StrictUser.check({ name: "Dana" }); // true
|
|
36
|
+
StrictUser.check({ name: "Dana", admin: true }); // false (extra keys are rejected!)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Nested Exact Objects
|
|
40
|
+
|
|
41
|
+
Exactness is applied globally to nested objects defined within a `Sigil.exact` block.
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const StrictOrder = Sigil.exact`
|
|
45
|
+
{
|
|
46
|
+
id: string
|
|
47
|
+
customer: {
|
|
48
|
+
name: string
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
// Fails validation because of the extra "email" key inside the nested customer object
|
|
54
|
+
StrictOrder.check({
|
|
55
|
+
id: "order_123",
|
|
56
|
+
customer: {
|
|
57
|
+
name: "Dana",
|
|
58
|
+
email: "dana@example.com"
|
|
59
|
+
}
|
|
60
|
+
}); // false
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Validation Errors
|
|
64
|
+
|
|
65
|
+
When a validation fails in exact mode due to an unexpected property, the assertion error reports exactly which property was unexpected:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
try {
|
|
69
|
+
StrictUser.assert({ name: "Dana", admin: true });
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.log(error.toJSON());
|
|
72
|
+
/*
|
|
73
|
+
{
|
|
74
|
+
code: "SIGIL_VALIDATION_FAILED",
|
|
75
|
+
message: "Unexpected property \"admin\"",
|
|
76
|
+
path: ["admin"],
|
|
77
|
+
expected: "undefined",
|
|
78
|
+
actual: "boolean"
|
|
79
|
+
}
|
|
80
|
+
*/
|
|
81
|
+
}
|
|
82
|
+
```
|
package/docs/examples.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
SigilJS is useful anywhere JavaScript interacts with unknown data.
|
|
4
|
+
|
|
5
|
+
Common use cases:
|
|
6
|
+
|
|
7
|
+
- API response validation
|
|
8
|
+
- Request body validation
|
|
9
|
+
- Configuration files
|
|
10
|
+
- CLI tools
|
|
11
|
+
- JSON parsing
|
|
12
|
+
- Database results
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## API Response Validation
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
|
|
20
|
+
const ApiResponse = Sigil`
|
|
21
|
+
{
|
|
22
|
+
user: {
|
|
23
|
+
id: string
|
|
24
|
+
email: string
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`
|
|
28
|
+
|
|
29
|
+
ApiResponse.assert(await response.json())
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Config Validation
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
|
|
39
|
+
const Config = Sigil`
|
|
40
|
+
{
|
|
41
|
+
port: number
|
|
42
|
+
host: string
|
|
43
|
+
debug?: boolean
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
Config.assert(JSON.parse(configFile))
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Request Validation
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
|
|
57
|
+
const LoginRequest = Sigil`
|
|
58
|
+
{
|
|
59
|
+
email: string
|
|
60
|
+
password: string
|
|
61
|
+
}
|
|
62
|
+
`
|
|
63
|
+
|
|
64
|
+
LoginRequest.assert(req.body)
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Validation Methods
|
|
71
|
+
|
|
72
|
+
Each sigil provides two validation methods.
|
|
73
|
+
|
|
74
|
+
### check()
|
|
75
|
+
|
|
76
|
+
Returns a boolean.
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
|
|
80
|
+
User.check(data)
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### assert()
|
|
85
|
+
|
|
86
|
+
Throws an error if validation fails.
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
|
|
90
|
+
User.assert(data)
|
|
91
|
+
|
|
92
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Introduction
|
|
2
|
+
|
|
3
|
+
JavaScript programs constantly deal with data.
|
|
4
|
+
|
|
5
|
+
* API responses.
|
|
6
|
+
* User input.
|
|
7
|
+
* Configuration files.
|
|
8
|
+
* Database records.
|
|
9
|
+
* JSON payloads.
|
|
10
|
+
|
|
11
|
+
And the most common question we need to answer is simple:
|
|
12
|
+
|
|
13
|
+
>"Does this data actually match what I expect?"
|
|
14
|
+
|
|
15
|
+
JavaScript gives us very few tools to answer that question.
|
|
16
|
+
|
|
17
|
+
` typeof ` is inconsistent:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
|
|
21
|
+
typeof [] // "object"
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
And TypeScript types disappear completely once the code runs.
|
|
26
|
+
|
|
27
|
+
SigilJS solves the runtime side of the problem.
|
|
28
|
+
|
|
29
|
+
Instead of writing complex validation logic, you describe the shape of your data using a **sigil**, then validate values against it.
|
package/docs/license.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# License
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026 SigilJS Contributors
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Named Sigils & Composition
|
|
2
|
+
|
|
3
|
+
As your application grows, you will want to reuse sigil definitions, compose larger schemas from smaller pieces, and support self-referential or circular schemas. SigilJS supports this via **Named Sigils** created with `Sigil.define` (or its alias `Sigil.named`).
|
|
4
|
+
|
|
5
|
+
## Defining a Reusable Sigil
|
|
6
|
+
|
|
7
|
+
You register a named sigil globally by passing a unique name to `Sigil.define`:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
import { Sigil } from "@antistructured/sigiljs";
|
|
11
|
+
|
|
12
|
+
// Register the "Email" sigil
|
|
13
|
+
const Email = Sigil.define("Email")`string`;
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Once defined, you can reference `Email` directly by name inside any other Sigil's template string:
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
const User = Sigil`
|
|
20
|
+
{
|
|
21
|
+
name: string
|
|
22
|
+
email: Email
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
User.check({
|
|
27
|
+
name: "Charlie",
|
|
28
|
+
email: "charlie@example.com"
|
|
29
|
+
}); // true
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Schema Composition
|
|
33
|
+
|
|
34
|
+
Composition allows you to modularize your schema architecture:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
Sigil.define("Address")`
|
|
38
|
+
{
|
|
39
|
+
street: string
|
|
40
|
+
city: string
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const UserProfile = Sigil`
|
|
45
|
+
{
|
|
46
|
+
name: string
|
|
47
|
+
billingAddress: Address
|
|
48
|
+
shippingAddress: Address
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Self-Referential & Circular Schemas
|
|
54
|
+
|
|
55
|
+
Named sigils allow you to define recursive models (like trees, folder hierarchies, or linked lists).
|
|
56
|
+
|
|
57
|
+
Because references are resolved lazily at validation/compilation time, you can refer to a named sigil that has not yet been registered or refers to itself.
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// A Node has a name, and an optional list of child Nodes
|
|
61
|
+
const Node = Sigil.define("Node")`
|
|
62
|
+
{
|
|
63
|
+
name: string
|
|
64
|
+
children?: Node[]
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
Node.check({
|
|
69
|
+
name: "root",
|
|
70
|
+
children: [
|
|
71
|
+
{ name: "src" },
|
|
72
|
+
{
|
|
73
|
+
name: "tests",
|
|
74
|
+
children: [
|
|
75
|
+
{ name: "validate.test.js" }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}); // true
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Registry Lifecycle
|
|
83
|
+
|
|
84
|
+
Named sigils are stored in a global registry. When testing, you may want to reset the registry to prevent cross-test contamination:
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
import { registry } from "@antistructured/sigiljs";
|
|
88
|
+
|
|
89
|
+
// Clear all registered named sigils
|
|
90
|
+
registry.clear();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Duplicate registrations follow a **last-write-wins** approach, meaning redefining a name will overwrite the previous definition.
|
package/docs/objects.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Object Schemas
|
|
2
|
+
|
|
3
|
+
SigilJS can describe full object structures.
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
|
|
7
|
+
const User = Sigil`
|
|
8
|
+
{
|
|
9
|
+
name: string
|
|
10
|
+
age: number
|
|
11
|
+
}
|
|
12
|
+
`
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
|
|
20
|
+
User.check({
|
|
21
|
+
name: "Alex",
|
|
22
|
+
age: 30
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Nested Objects
|
|
30
|
+
|
|
31
|
+
Sigils support nested structures.
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
|
|
35
|
+
const Profile = Sigil`
|
|
36
|
+
{
|
|
37
|
+
user: {
|
|
38
|
+
name: string
|
|
39
|
+
email: string
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`
|
|
43
|
+
|
|
44
|
+
```
|
package/docs/optional.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Optional Values
|
|
2
|
+
|
|
3
|
+
Use ` ? ` to allow ` undefined `.
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
|
|
7
|
+
Sigil`string?`
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This means the value can be:
|
|
12
|
+
|
|
13
|
+
- a string
|
|
14
|
+
- undefined
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
|
|
20
|
+
const MaybeName = Sigil`string?`
|
|
21
|
+
|
|
22
|
+
MaybeName.check(undefined)
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Optional Object Properties
|
|
29
|
+
|
|
30
|
+
Optional object properties use ` ? ` after the property name.
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
|
|
34
|
+
const User = Sigil`
|
|
35
|
+
{
|
|
36
|
+
name: string
|
|
37
|
+
age?: number
|
|
38
|
+
}
|
|
39
|
+
`
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
|
|
46
|
+
User.check({ name: "Alex" })
|
|
47
|
+
|
|
48
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
Using Bun:
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
|
|
9
|
+
bun add @antistructured/sigiljs
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Using npm:
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
|
|
17
|
+
npm install @antistructured/sigiljs
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Create Your First Sigil
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
|
|
27
|
+
import { Sigil } from "@antistructured/sigiljs"
|
|
28
|
+
|
|
29
|
+
const Email = Sigil`string`
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This sigil describes any string.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Validate Data
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
|
|
41
|
+
Email.check("hello@example.com")
|
|
42
|
+
// true
|
|
43
|
+
|
|
44
|
+
Email.check(42)
|
|
45
|
+
// false
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If you want validation to throw errors instead:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
Email.assert(42)
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.log(`
|
|
57
|
+
${error.message} // "Expected string, got number"
|
|
58
|
+
${error.code} // "SIGIL_VALIDATION_FAILED"
|
|
59
|
+
${error.path} // []
|
|
60
|
+
${error.expected} // "string"
|
|
61
|
+
${error.actual} // "number"
|
|
62
|
+
`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
```
|
package/docs/realtype.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# realType
|
|
2
|
+
|
|
3
|
+
SigilJS includes ` realType() `, which improves on JavaScript's ` typeof `.
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
|
|
7
|
+
import { realType } from "sigiljs"
|
|
8
|
+
|
|
9
|
+
realType([]) // "array"
|
|
10
|
+
realType(null) // "null"
|
|
11
|
+
realType(new Map()) // "map"
|
|
12
|
+
realType(new Date()) // "date"
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This is especially useful when building validation or debugging tools.
|
|
17
|
+
|
|
18
|
+
## Custom Type Hooks
|
|
19
|
+
|
|
20
|
+
You can supply custom override hooks to map instances of custom classes or objects back to nominal type strings. Hooks are checked before standard type resolution:
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
import { realType } from "@antistructured/sigiljs";
|
|
24
|
+
|
|
25
|
+
class MyCustomConnection {}
|
|
26
|
+
|
|
27
|
+
const conn = new MyCustomConnection();
|
|
28
|
+
|
|
29
|
+
const typeName = realType(conn, {
|
|
30
|
+
hooks: [
|
|
31
|
+
val => val instanceof MyCustomConnection ? 'connection' : null
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log(typeName); // "connection"
|
|
36
|
+
```
|
package/docs/roadmap.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# SigilJS Project Roadmap
|
|
2
|
+
|
|
3
|
+
This document outlines the milestones and roadmap towards a stable `v1.0.0` release.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## v0.0.x: Foundations & Groundwork [Completed]
|
|
8
|
+
* [x] **Parser Stability**: Robust tokenization and AST parsing for basic types, nested objects, optionals, and arrays.
|
|
9
|
+
* [x] **Documentation Hub**: Reference manuals for core features.
|
|
10
|
+
* [x] **Examples & Playground**: CLI utility for testing type expressions.
|
|
11
|
+
* [x] **Packaging & Project Hygiene**: Clean npm/Bun packages with zero runtime dependencies.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## v0.1.x: Compiler & Path-Aware Diagnostics [Completed / Current]
|
|
16
|
+
* [x] **Compiled Validators**: High-performance pipeline compiled to optimized JS closures, bypassing AST lookups on validation paths.
|
|
17
|
+
* [x] **Exact Object Mode**: Recursive support for strict object schema verification.
|
|
18
|
+
* [x] **Reusable Named Sigils**: Composable, registered types with support for recursive/circular schemas.
|
|
19
|
+
* [x] **Stronger Assert Errors**: Fully path-aware diagnostic exceptions (`SigilValidationError`) indicating exact paths (`["user", "age"]`) and types (`expected`/`actual`).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## v0.2.x: Advanced Composition & Registry Tools [Next Focus]
|
|
24
|
+
* [ ] **Named Sigil Collections**: Grouping/importing collections of custom types to easily share domain vocabularies.
|
|
25
|
+
* [ ] **Better Composition API**: Fluent operators for extending and modifying existing sigils.
|
|
26
|
+
* [ ] **Canonical Schema Caching**: Global structural canonicalization cache for deduplicating syntactically identical sigils across different packages.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## v0.3.x: CLI Upgrades & Optimization Hot-Paths
|
|
31
|
+
* [x] **Benchmark Suite**: Establish comparative execution times against Zod and handwritten Javascript.
|
|
32
|
+
* [ ] **CLI Enhancements**: Enhanced formatting and interactive modes for the playground binary.
|
|
33
|
+
* [ ] **V8 Optimizer Alignment**: Fine-tune code-generation functions to maximize JIT compiler inline caching.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## v1.0.0: Production-Ready Release
|
|
38
|
+
* [ ] **Stable Grammar & AST Spec**: Lock the DSL grammar rules against breaking additions.
|
|
39
|
+
* [ ] **API Freeze**: Lock core exports (`Sigil`, `S`, `T`, `realType`) and instance methods.
|
|
40
|
+
* [ ] **Extensive Stress-Testing**: End-to-end production verification.
|
package/docs/sigils.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Sigils
|
|
2
|
+
|
|
3
|
+
A **sigil** is a small expression that describes what data should look like.
|
|
4
|
+
|
|
5
|
+
Sigils are written using a tagged template:
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
|
|
9
|
+
Sigil`string`
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Sigils compile into fast runtime validators.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Primitive Types
|
|
18
|
+
|
|
19
|
+
SigilJS supports common JavaScript primitives.
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
|
|
23
|
+
Sigil`string`
|
|
24
|
+
Sigil`number`
|
|
25
|
+
Sigil`boolean`
|
|
26
|
+
Sigil`bigint`
|
|
27
|
+
Sigil`symbol`
|
|
28
|
+
Sigil`null`
|
|
29
|
+
Sigil`undefined`
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
|
|
37
|
+
const Name = Sigil`string`
|
|
38
|
+
|
|
39
|
+
Name.check("Alex")
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## The Sigil Mental Model
|
|
46
|
+
|
|
47
|
+
The easiest way to think about SigilJS is:
|
|
48
|
+
|
|
49
|
+
A **sigil is a blueprint for data**.
|
|
50
|
+
|
|
51
|
+
Example blueprint:
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
|
|
55
|
+
const User = Sigil`
|
|
56
|
+
{
|
|
57
|
+
name: string
|
|
58
|
+
age?: number
|
|
59
|
+
}
|
|
60
|
+
`
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
You can then **cast the sigil** against real values.
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
|
|
68
|
+
User.check(data)
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## API Aliases: `Sigil`, `S`, and `T`
|
|
75
|
+
|
|
76
|
+
To accommodate different developer preferences, SigilJS exports three aliases for the template tag:
|
|
77
|
+
|
|
78
|
+
1. **`Sigil` (Recommended for clarity)**: The standard and most descriptive import. Perfect for public APIs, shared utilities, or when team readability is the priority.
|
|
79
|
+
```javascript
|
|
80
|
+
import { Sigil } from "@antistructured/sigiljs";
|
|
81
|
+
const User = Sigil`{ name: string }`;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
2. **`S` (Recommended shorthand)**: A single-letter shorthand that feels like standard types or schemas. Great for reducing boilerplate in inline declarations.
|
|
85
|
+
```javascript
|
|
86
|
+
import { S } from "@antistructured/sigiljs";
|
|
87
|
+
const User = S`{ name: string }`;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
3. **`T` (Legacy/Optional)**: Kept strictly for backwards compatibility with earlier versions. We recommend using `Sigil` or `S` in new codebases.
|
|
91
|
+
```javascript
|
|
92
|
+
import { T } from "@antistructured/sigiljs";
|
|
93
|
+
const User = T`{ name: string }`;
|
|
94
|
+
```
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Sigil } from '../src/index.js'
|
|
2
|
+
|
|
3
|
+
const ApiResponse = Sigil`
|
|
4
|
+
{
|
|
5
|
+
user: {
|
|
6
|
+
id: string
|
|
7
|
+
email: string
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
const response = await fetch("https://example.com/api/user")
|
|
14
|
+
const data = await response.json()
|
|
15
|
+
|
|
16
|
+
ApiResponse.assert(data)
|
|
17
|
+
|
|
18
|
+
console.log("Valid response")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
main()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# [examples/api-response.js](https://github.dev/weipertda/sigiljs/blob/main/examples/scripts/api-response.js)
|
|
2
|
+
|
|
3
|
+
```javascript
|
|
4
|
+
|
|
5
|
+
import { Sigil } from "../src/index.js"
|
|
6
|
+
|
|
7
|
+
const ApiResponse = Sigil`
|
|
8
|
+
{
|
|
9
|
+
user: {
|
|
10
|
+
id: string
|
|
11
|
+
email: string
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
async function main() {
|
|
17
|
+
const response = await fetch("https://example.com/api/user")
|
|
18
|
+
const data = await response.json()
|
|
19
|
+
|
|
20
|
+
ApiResponse.assert(data)
|
|
21
|
+
|
|
22
|
+
console.log("Valid response")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
main()
|
|
26
|
+
|
|
27
|
+
```
|
|
File without changes
|
|
File without changes
|