@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.
Files changed (49) hide show
  1. package/README.md +157 -155
  2. package/docs/README.md +20 -0
  3. package/docs/arrays.md +55 -0
  4. package/docs/cli.md +48 -0
  5. package/docs/compiled-validators.md +42 -0
  6. package/docs/contributing.md +43 -0
  7. package/docs/exact-mode.md +82 -0
  8. package/docs/examples.md +92 -0
  9. package/docs/introduction.md +29 -0
  10. package/docs/license.md +23 -0
  11. package/docs/named-sigils.md +93 -0
  12. package/docs/objects.md +44 -0
  13. package/docs/optional.md +48 -0
  14. package/docs/quickstart.md +65 -0
  15. package/docs/realtype.md +36 -0
  16. package/docs/roadmap.md +40 -0
  17. package/docs/sigils.md +94 -0
  18. package/docs/{functions.md,maps.md,sets.md,tuples.md,intersection.md,rest.md,examples.md,performance.md,contributing.md,license.md +0 -0
  19. package/examples/api-response.js +21 -0
  20. package/examples/api-response.md +27 -0
  21. package/examples/api-validation.js +0 -0
  22. package/examples/api-validation.md +0 -0
  23. package/examples/basic-user.js +16 -0
  24. package/examples/basic-user.md +22 -0
  25. package/examples/basic-validation.js +0 -0
  26. package/examples/basic-validation.md +0 -0
  27. package/examples/config-file.md +16 -0
  28. package/examples/config-validation.js +18 -0
  29. package/examples/config-validation.md +24 -0
  30. package/examples/login-request.js +19 -0
  31. package/examples/login-request.md +25 -0
  32. package/examples/nested-order.js +28 -0
  33. package/examples/nested-order.md +34 -0
  34. package/examples/realtype-demo.js +6 -0
  35. package/examples/realtype-demo.md +12 -0
  36. package/examples/scripts/config-file.js +0 -0
  37. package/package.json +14 -6
  38. package/src/core/assert.js +173 -50
  39. package/src/core/compile.js +43 -9
  40. package/src/core/errors.js +29 -6
  41. package/src/core/normalize.js +1 -1
  42. package/src/core/parser.js +4 -3
  43. package/src/core/partial.js +1 -0
  44. package/src/core/registry.js +33 -0
  45. package/src/core/validate.js +3 -0
  46. package/src/index.js +1 -0
  47. package/src/playground/playground.js +1 -1
  48. package/src/sigil.js +56 -16
  49. 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
+ ```
@@ -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.
@@ -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.
@@ -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
+ ```
@@ -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
+ ```
@@ -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
+ ```
@@ -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
+ ```
@@ -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