xdbc 1.0.207 → 1.0.209
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/Assessment.html +350 -0
- package/Assessment.md +507 -51
- package/CHANGELOG.md +55 -0
- package/CONTRIBUTING.md +129 -17
- package/README.md +373 -71
- package/SECURITY.md +60 -18
- package/SUPPORT.md +65 -0
- package/__tests__/DBC/DEFINED.test.ts +53 -0
- package/__tests__/DBC/Decorators.test.ts +365 -0
- package/__tests__/DBC/GREATER.test.ts +8 -6
- package/__tests__/DBC/HasAttribute.test.ts +56 -0
- package/__tests__/DBC/IF.test.ts +52 -0
- package/__tests__/DBC/JSON.Parse.test.ts +1 -1
- package/__tests__/DBC/OR.test.ts +1 -1
- package/__tests__/DBC/REGEX.test.ts +1 -1
- package/__tests__/DBC/TYPE.test.ts +1 -1
- package/__tests__/DBC/UNDEFINED.test.ts +45 -0
- package/__tests__/DBC/ZOD.test.ts +54 -0
- package/jest.config.js +21 -0
- package/package.json +4 -5
- package/src/DBC/AE.ts +10 -6
- package/src/DBC/COMPARISON/GREATER.ts +11 -7
- package/src/DBC/COMPARISON/GREATER_OR_EQUAL.ts +14 -10
- package/src/DBC/COMPARISON/LESS.ts +14 -10
- package/src/DBC/COMPARISON/LESS_OR_EQUAL.ts +14 -10
- package/src/DBC/COMPARISON.ts +20 -43
- package/src/DBC/DEFINED.ts +4 -23
- package/src/DBC/EQ/DIFFERENT.ts +21 -56
- package/src/DBC/EQ.ts +7 -26
- package/src/DBC/HasAttribute.ts +9 -26
- package/src/DBC/IF.ts +8 -27
- package/src/DBC/INSTANCE.ts +5 -22
- package/src/DBC/JSON.OP.ts +4 -34
- package/src/DBC/JSON.Parse.ts +5 -25
- package/src/DBC/OR.ts +5 -14
- package/src/DBC/REGEX.ts +41 -40
- package/src/DBC/TYPE.ts +6 -25
- package/src/DBC/UNDEFINED.ts +3 -22
- package/src/DBC/ZOD.ts +10 -27
- package/src/DBC.ts +223 -55
- package/tsconfig.json +7 -4
- package/tsconfig.test.json +12 -0
- package/.parcel-cache/bf96c58b6061a62a-BundleGraph +0 -0
- package/.parcel-cache/d7c812d65aeeac59-AssetGraph +0 -0
- package/.parcel-cache/data.mdb +0 -0
- package/.parcel-cache/e81759c1f106a17f-RequestGraph +0 -0
- package/.parcel-cache/fe0db3c4eb428be2-AssetGraph +0 -0
- package/.parcel-cache/lock.mdb +0 -0
- package/.parcel-cache/snapshot-e81759c1f106a17f.txt +0 -4609
- package/dist/DBC/AE.js +0 -173
- package/dist/DBC/COMPARISON/GREATER.js +0 -21
- package/dist/DBC/COMPARISON/GREATER_OR_EQUAL.js +0 -21
- package/dist/DBC/COMPARISON/LESS.js +0 -21
- package/dist/DBC/COMPARISON/LESS_OR_EQUAL.js +0 -21
- package/dist/DBC/COMPARISON.js +0 -99
- package/dist/DBC/DEFINED.js +0 -99
- package/dist/DBC/EQ/DIFFERENT.js +0 -21
- package/dist/DBC/EQ.js +0 -100
- package/dist/DBC/GREATER.js +0 -99
- package/dist/DBC/HasAttribute.js +0 -108
- package/dist/DBC/IF.js +0 -99
- package/dist/DBC/INSTANCE.js +0 -93
- package/dist/DBC/JSON.OP.js +0 -133
- package/dist/DBC/JSON.Parse.js +0 -114
- package/dist/DBC/OR.js +0 -113
- package/dist/DBC/REGEX.js +0 -110
- package/dist/DBC/TYPE.js +0 -87
- package/dist/DBC/ZOD.js +0 -114
- package/dist/DBC.js +0 -336
- package/dist/Demo.js +0 -290
- package/dist/Test.html +0 -18
- package/dist/bundle.js +0 -2064
- package/dist/index.html +0 -18
- package/jest.config.ts +0 -20
- package/xpackage-lock.json +0 -122
package/CONTRIBUTING.md
CHANGED
|
@@ -1,40 +1,152 @@
|
|
|
1
1
|
# Contributing to XDBC
|
|
2
2
|
|
|
3
|
-
Thank you for your interest in contributing to XDBC
|
|
3
|
+
Thank you for your interest in contributing to XDBC. Contributions of all kinds are welcome — bug reports, feature requests, code improvements, documentation enhancements, and test coverage expansion.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Getting Started](#getting-started)
|
|
10
|
+
- [Reporting Issues](#reporting-issues)
|
|
11
|
+
- [Contributing Code](#contributing-code)
|
|
12
|
+
- [Development Workflow](#development-workflow)
|
|
13
|
+
- [Code Style](#code-style)
|
|
14
|
+
- [Testing](#testing)
|
|
15
|
+
- [Documentation](#documentation)
|
|
16
|
+
- [License Agreement](#license-agreement)
|
|
17
|
+
- [Code of Conduct](#code-of-conduct)
|
|
18
|
+
- [Contact](#contact)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
1. **Fork** the repository on GitHub.
|
|
25
|
+
2. **Clone** your fork locally:
|
|
26
|
+
```sh
|
|
27
|
+
git clone https://github.com/<your-username>/XDBC.git
|
|
28
|
+
cd XDBC
|
|
29
|
+
```
|
|
30
|
+
3. **Install dependencies**:
|
|
31
|
+
```sh
|
|
32
|
+
npm install
|
|
33
|
+
```
|
|
34
|
+
4. **Run the test suite** to verify the setup:
|
|
35
|
+
```sh
|
|
36
|
+
npm test
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
4
40
|
|
|
5
41
|
## Reporting Issues
|
|
6
42
|
|
|
7
|
-
If you
|
|
43
|
+
If you encounter a bug or have a feature request, please [open an issue](https://github.com/CallariS/XDBC/issues/new) on GitHub.
|
|
8
44
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
45
|
+
When reporting a bug, include:
|
|
46
|
+
|
|
47
|
+
- A clear, descriptive title
|
|
48
|
+
- Steps to reproduce the issue
|
|
49
|
+
- Expected behavior vs. actual behavior
|
|
50
|
+
- Environment details (Node.js version, TypeScript version, OS)
|
|
51
|
+
- A minimal code sample or test case, if possible
|
|
52
|
+
|
|
53
|
+
For feature requests, describe the use case and how the proposed change benefits users.
|
|
54
|
+
|
|
55
|
+
---
|
|
13
56
|
|
|
14
57
|
## Contributing Code
|
|
15
58
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
59
|
+
### Branch Strategy
|
|
60
|
+
|
|
61
|
+
- Create a feature branch from `master`:
|
|
62
|
+
```sh
|
|
63
|
+
git checkout -b feature/my-improvement
|
|
64
|
+
```
|
|
65
|
+
- Use descriptive branch names: `feature/`, `fix/`, `docs/`, `test/`
|
|
66
|
+
|
|
67
|
+
### Commit Guidelines
|
|
68
|
+
|
|
69
|
+
- Write clear, concise commit messages in imperative mood:
|
|
70
|
+
- **Good**: `Add RANGE contract for numeric boundaries`
|
|
71
|
+
- **Avoid**: `Added stuff`, `WIP`, `fix`
|
|
72
|
+
- Each commit should represent a single logical change
|
|
73
|
+
- Reference related issues where applicable: `Fix #12 — handle null in OR.check()`
|
|
74
|
+
|
|
75
|
+
### Pull Request Process
|
|
76
|
+
|
|
77
|
+
1. Ensure your branch is up to date with `master`
|
|
78
|
+
2. Run the full test suite and linter before submitting
|
|
79
|
+
3. Open a pull request with:
|
|
80
|
+
- A description of what the change does and why
|
|
81
|
+
- Links to related issues
|
|
82
|
+
- Any breaking changes clearly noted
|
|
83
|
+
4. Address review feedback promptly
|
|
84
|
+
5. A maintainer will merge once the PR is approved
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Development Workflow
|
|
89
|
+
|
|
90
|
+
| Command | Purpose |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `npm test` | Run the test suite (Jest) |
|
|
93
|
+
| `npm run build` | Build the project (Webpack) |
|
|
94
|
+
| `npm run lint` | Lint the codebase (Biome) |
|
|
95
|
+
| `npm run format` | Auto-format source files (Biome) |
|
|
96
|
+
| `npm run docs` | Generate API documentation (TypeDoc) |
|
|
97
|
+
|
|
98
|
+
---
|
|
21
99
|
|
|
22
100
|
## Code Style
|
|
23
101
|
|
|
24
|
-
|
|
102
|
+
- **TypeScript** is the primary language. All source code resides in `src/`.
|
|
103
|
+
- **Biome** is used for both linting and formatting. Run `npm run lint` and `npm run format` before submitting.
|
|
104
|
+
- Follow existing patterns:
|
|
105
|
+
- Each contract class lives in its own file under `src/DBC/`
|
|
106
|
+
- Derived contracts go in subdirectories (e.g., `src/DBC/COMPARISON/`)
|
|
107
|
+
- Every contract exposes `PRE`, `POST`, and `INVARIANT` static decorator factories
|
|
108
|
+
- Avoid introducing new dependencies unless absolutely necessary
|
|
109
|
+
- Prefer strict types over `any` where feasible
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Testing
|
|
114
|
+
|
|
115
|
+
- Tests are located in `__tests__/DBC/` and use **Jest** with `ts-jest`
|
|
116
|
+
- Every new contract or behavioral change should include corresponding tests
|
|
117
|
+
- Test file naming convention: `<ContractName>.test.ts`
|
|
118
|
+
- Run the suite with:
|
|
119
|
+
```sh
|
|
120
|
+
npm test
|
|
121
|
+
```
|
|
122
|
+
- Aim for tests that cover:
|
|
123
|
+
- `check()` method behavior (passing and failing cases)
|
|
124
|
+
- Decorator execution (`PRE`, `POST`, `INVARIANT`) where applicable
|
|
125
|
+
- Edge cases (null, undefined, empty arrays, wrong types)
|
|
126
|
+
|
|
127
|
+
---
|
|
25
128
|
|
|
26
129
|
## Documentation
|
|
27
130
|
|
|
28
|
-
|
|
131
|
+
- API documentation is generated with [TypeDoc](https://typedoc.org/) (`npm run docs`)
|
|
132
|
+
- Use JSDoc-style comments on all public classes, methods, and parameters
|
|
133
|
+
- Update the README if your change adds new contracts, features, or configuration options
|
|
134
|
+
- Keep `Demo.ts` up to date with representative usage examples
|
|
29
135
|
|
|
30
|
-
|
|
136
|
+
---
|
|
31
137
|
|
|
32
|
-
|
|
138
|
+
## License Agreement
|
|
139
|
+
|
|
140
|
+
By contributing to XDBC, you agree that your contributions will be licensed under the [MIT License](LICENSE).
|
|
141
|
+
|
|
142
|
+
---
|
|
33
143
|
|
|
34
144
|
## Code of Conduct
|
|
35
145
|
|
|
36
|
-
|
|
146
|
+
All contributors must adhere to the [Code of Conduct](CODE_OF_CONDUCT.md). Respectful, professional interaction is expected in all project spaces.
|
|
147
|
+
|
|
148
|
+
---
|
|
37
149
|
|
|
38
150
|
## Contact
|
|
39
151
|
|
|
40
|
-
|
|
152
|
+
For questions about contributing, reach out at [XDBC@WaXCode.net](mailto:XDBC@WaXCode.net).
|
package/README.md
CHANGED
|
@@ -1,103 +1,405 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://img.shields.io/npm/v/xdbc?style=flat-square" alt="npm version" />
|
|
3
|
+
<img src="https://img.shields.io/npm/l/xdbc?style=flat-square" alt="license" />
|
|
4
|
+
<img src="https://img.shields.io/npm/dt/xdbc?style=flat-square" alt="downloads" />
|
|
5
|
+
<img src="https://img.shields.io/badge/TypeScript-5.x-blue?style=flat-square&logo=typescript" alt="TypeScript" />
|
|
6
|
+
<img src="https://img.shields.io/badge/decorators-stage%203-green?style=flat-square" alt="decorators" />
|
|
7
|
+
</p>
|
|
2
8
|
|
|
3
|
-
|
|
9
|
+
# XDBC — e**X**plicit **D**esign **b**y **C**ontract for TypeScript
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|-------------------|------------------------------------------------------------|
|
|
7
|
-
| <pre>@DBC.ParamvalueProvider<br>public method(@AE.PRE([new REGEX(/^\.*XDBC.\*$/i)]) input : Array\<string>) {<br> ... <br>}</pre>|<pre>public method( input : Array\<string>) {<br> input.forEach(( element, index ) => {<br> console.assert(/^.\*XDBC.\*$/i.test(element),"inconsistent error message");<br> });<br><br> ...<br>}</pre>
|
|
8
|
-
| <pre>@REGEX.INVARIANT(/^.\*XDBC.\*$/i)<br>public field = "XDBC";</pre>|<pre>get field() : string { return ... }<br>set field( toSet : string ) {<br> console.assert(/^.\*XDBC.\*$/i.test(element),"Inconsistent error message"); <br><br> ...<br>}</pre>
|
|
9
|
-
| <pre>@REGEX.POST(/^XDBC$/i)<br>public method( input : unknown ) : string {<br> ...<br><br> return result ;<br>}</pre>|<pre>public method( input : unknown ) : string {<br> ...<br><br> if(!/^.\*XDBC.\*$/i.test(result) {<br> throw new Error("inconsistent error message");<br> }<br><br> return result ;<br>}</pre>
|
|
10
|
-
<pre>...and get consistent details about errors like: <code style = "background-color : beige ; color : red ;">[ XDBC Infringement [ From "method" in "MyClass": [ Parameter-value "+1d,+5d,-x10y" of the 1st parameter did not fulfill one of it's contracts: Violating-Arrayelement at index 2. Value has to comply to regular expression "/^(?i:(NOW)|([+-]\d+[dmy]))$/i"]]]</code></pre>
|
|
11
|
+
> A decorator-based Design by Contract framework that enforces preconditions, postconditions, and invariants through TypeScript metadata — delivering precise, self-documenting, and verifiable component contracts.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
[**D**esign **b**y **C**ontract™ (DbC)](https://en.wikipedia.org/wiki/Design_by_contract) is a software development approach focused on defining precise and verifiable contracts between software components. These contracts specify the preconditions that must be true when calling a component (e.g., a function or method), the postconditions guaranteed after the component's execution, and the invariants that must hold true throughout the lifetime of an object or class.
|
|
13
|
+
---
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## At a Glance
|
|
16
16
|
|
|
17
|
-
|
|
|
18
|
-
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
| Easier Debugging | Error causes traceable through contract violations |
|
|
17
|
+
| Approach | With XDBC | Without XDBC |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **Parameter validation** | <pre>@DBC.ParamvalueProvider<br>method(@REGEX.PRE(/^\.*XDBC.\*$/i) input: string[]) {<br> ...<br>}</pre> | <pre>method(input: string[]) {<br> input.forEach((el, i) => {<br> console.assert(/^.\*XDBC.\*$/i.test(el), "error");<br> });<br> ...<br>}</pre> |
|
|
20
|
+
| **Field invariant** | <pre>@REGEX.INVARIANT(/^.\*XDBC.\*$/i)<br>public field = "XDBC";</pre> | <pre>get field(): string { return this._field; }<br>set field(v: string) {<br> console.assert(/^.\*XDBC.\*$/i.test(v), "error");<br> this._field = v;<br>}</pre> |
|
|
21
|
+
| **Return validation** | <pre>@REGEX.POST(/^XDBC$/i)<br>method(input: unknown): string {<br> ...<br> return result;<br>}</pre> | <pre>method(input: unknown): string {<br> ...<br> if (!/^XDBC$/i.test(result)) {<br> throw new Error("error");<br> }<br> return result;<br>}</pre> |
|
|
23
22
|
|
|
23
|
+
Contract violations produce structured, actionable diagnostics:
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
```
|
|
26
|
+
[ XDBC Infringement [ From "method" in "MyClass": [ Parameter-value "+1d,+5d,-x10y"
|
|
27
|
+
of the 1st parameter did not fulfill one of it's contracts: Violating-Arrayelement at
|
|
28
|
+
index 2. Value has to comply to regular expression "/^(?i:(NOW)|([+-]\d+[dmy]))$/i"]]]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Table of Contents
|
|
34
|
+
|
|
35
|
+
- [What is Design by Contract?](#what-is-design-by-contract)
|
|
36
|
+
- [Why XDBC?](#why-xdbc)
|
|
37
|
+
- [Installation](#installation)
|
|
38
|
+
- [Quick Start](#quick-start)
|
|
39
|
+
- [Contracts Reference](#contracts-reference)
|
|
40
|
+
- [Core Concepts](#core-concepts)
|
|
41
|
+
- [Advanced Features](#advanced-features)
|
|
42
|
+
- [Configuration](#configuration)
|
|
43
|
+
- [API Documentation](#api-documentation)
|
|
44
|
+
- [Contributing](#contributing)
|
|
45
|
+
- [License](#license)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## What is Design by Contract?
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
[Design by Contract (DbC)](https://en.wikipedia.org/wiki/Design_by_contract) is a software engineering methodology that defines formal, precise, and verifiable interface specifications for software components. Each component's contract comprises:
|
|
28
52
|
|
|
29
|
-
|
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
| Readability | Generally good, contracts are clear | Can become cluttered |
|
|
35
|
-
| Maintainability | Often better, contracts are localized | Can be harder to track and modify |
|
|
36
|
-
| Production | Contracts can be kept or disabled | Often disabled for performance |
|
|
53
|
+
| Element | Purpose |
|
|
54
|
+
|---|---|
|
|
55
|
+
| **Preconditions** | Conditions that must hold true *before* a method executes |
|
|
56
|
+
| **Postconditions** | Guarantees that must hold true *after* a method returns |
|
|
57
|
+
| **Invariants** | Properties that must remain true *throughout* an object's lifetime |
|
|
37
58
|
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
### DbC vs. Assertions
|
|
60
|
+
|
|
61
|
+
| Aspect | XDBC Decorators | Manual Assertions |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| Formality | Formal, declarative, co-located with signatures | Informal, scattered through method bodies |
|
|
64
|
+
| Integration | TypeScript metadata and decorators | Built-in `console.assert` / `throw` |
|
|
65
|
+
| Expressiveness | Composable, parameterized contract objects | Simple boolean checks |
|
|
66
|
+
| Readability | Contracts are visible at the API surface | Validation logic obscures business logic |
|
|
67
|
+
| Maintainability | Contracts are localized and reusable | Duplicated checks are hard to track |
|
|
68
|
+
| Production control | Selectively enable/disable by contract type | Typically all-or-nothing |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Why XDBC?
|
|
73
|
+
|
|
74
|
+
- **Declarative** — contracts live as decorators alongside type signatures, not buried in method bodies
|
|
75
|
+
- **Composable** — combine contracts with `AE`, `OR`, and `IF` for expressive validation
|
|
76
|
+
- **Configurable** — toggle preconditions, postconditions, and invariants independently
|
|
77
|
+
- **Diagnostic** — structured error messages pinpoint the exact violation, parameter, and context
|
|
78
|
+
- **Extensible** — 16 built-in contracts, with support for custom contracts and Zod schema integration
|
|
79
|
+
- **Zero runtime overhead** — disable contract checking in production with a single flag
|
|
80
|
+
|
|
81
|
+
---
|
|
40
82
|
|
|
41
83
|
## Installation
|
|
42
84
|
|
|
43
85
|
```sh
|
|
44
|
-
npm install
|
|
86
|
+
npm install xdbc
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Requirements:** TypeScript 5.x with `experimentalDecorators` and `emitDecoratorMetadata` enabled in `tsconfig.json`.
|
|
90
|
+
|
|
91
|
+
```jsonc
|
|
92
|
+
// tsconfig.json
|
|
93
|
+
{
|
|
94
|
+
"compilerOptions": {
|
|
95
|
+
"experimentalDecorators": true,
|
|
96
|
+
"emitDecoratorMetadata": true
|
|
97
|
+
}
|
|
98
|
+
}
|
|
45
99
|
```
|
|
46
100
|
|
|
47
|
-
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Quick Start
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { DBC, REGEX, TYPE, EQ } from "xdbc";
|
|
107
|
+
|
|
108
|
+
class UserService {
|
|
109
|
+
|
|
110
|
+
// Invariant: email must always match pattern
|
|
111
|
+
@REGEX.INVARIANT(/^[^@]+@[^@]+\.[^@]+$/)
|
|
112
|
+
public email = "user@example.com";
|
|
113
|
+
|
|
114
|
+
// Precondition: name must be a string; Postcondition: return must match pattern
|
|
115
|
+
@REGEX.POST(/^Hello, .+$/)
|
|
116
|
+
@DBC.ParamvalueProvider
|
|
117
|
+
public greet(@TYPE.PRE("string") name: string): string {
|
|
118
|
+
return `Hello, ${name}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Precondition: age must be >= 0
|
|
122
|
+
@DBC.ParamvalueProvider
|
|
123
|
+
public setAge(@GREATER_OR_EQUAL.PRE(0) age: number) {
|
|
124
|
+
// ...
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Contracts Reference
|
|
132
|
+
|
|
133
|
+
XDBC ships with **16 contracts** organized into core validators and derived specializations:
|
|
134
|
+
|
|
135
|
+
### Core Contracts
|
|
48
136
|
|
|
49
|
-
|
|
137
|
+
| Contract | Description | Constructor |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| **`REGEX`** | Value must match a regular expression | `new REGEX(expression: RegExp)` |
|
|
140
|
+
| **`TYPE`** | Value must be of a specified type (supports pipe-separated: `"string\|number"`) | `new TYPE(type: string)` |
|
|
141
|
+
| **`EQ`** | Value must equal (or not equal) a reference value | `new EQ(equivalent: any, invert?: boolean)` |
|
|
142
|
+
| **`COMPARISON`** | Numeric comparison against a reference value | `new COMPARISON(equivalent, equalityPermitted, invert)` |
|
|
143
|
+
| **`INSTANCE`** | Value must be an instance of a specified class | `new INSTANCE(reference: any \| any[])` |
|
|
144
|
+
| **`AE`** | Every element in an array must satisfy a set of contracts | `new AE(conditions, index?, idxEnd?)` |
|
|
145
|
+
| **`OR`** | At least one of a set of contracts must be satisfied | `new OR(conditions: DBC[])` |
|
|
146
|
+
| **`IF`** | Conditional contract: if A holds, then B must also hold | `IF.PRE(condition, inCase, path?, invert?)` |
|
|
147
|
+
| **`JSON_OP`** | Object must contain specific properties of specific types | `new JSON_OP(properties: {name, type}[], checkElements?)` |
|
|
148
|
+
| **`JSON_Parse`** | String must be valid JSON; optionally forwards parsed result | `new JSON_Parse(receptor?: (json) => void)` |
|
|
149
|
+
| **`DEFINED`** | Value must not be `null` or `undefined` | — |
|
|
150
|
+
| **`UNDEFINED`** | Value must be `undefined` | — |
|
|
151
|
+
| **`HasAttribute`** | HTMLElement must possess a named attribute | `HasAttribute.PRE(attrName, invert?)` |
|
|
152
|
+
| **`ZOD`** | Value must validate against a Zod schema | `new ZOD(schema: z.ZodType)` |
|
|
50
153
|
|
|
51
|
-
|
|
52
|
-
- EQ (*Value has to be equal to a supplied reference value*)
|
|
53
|
-
- COMPARISON (*Value has to be greater, less, greater/equal, equal or less/equal than a supplied reference value*)
|
|
54
|
-
- INSTANCE (*Value has to be an instance of a supplied type*)
|
|
55
|
-
- JSON.OP (*Value has to contain certain properties of certain type*)
|
|
56
|
-
- JSON.Parse (*String-value has to be a parsable JSON*)
|
|
57
|
-
- OR (*At least one of a set of contracts has to be fulfilled*)
|
|
58
|
-
- REGEX (*Value has to be validated by a supplied regular expression*)
|
|
59
|
-
- TYPE (*Value has to be of a certain type*)
|
|
60
|
-
- GREATER (*Derived from COMPARISON*)
|
|
61
|
-
- GREATER_OR_EQUAL (*Derived from COMPARISON*)
|
|
62
|
-
- LESS (*Derived from COMPARISON*)
|
|
63
|
-
- LESS_OR_EQUAL (*Derived from COMPARISON*)
|
|
64
|
-
- DIFFERENT (*Derived from EQ*)
|
|
154
|
+
### Derived Contracts
|
|
65
155
|
|
|
66
|
-
|
|
67
|
-
|
|
156
|
+
| Contract | Derives From | Semantics |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| **`GREATER`** | `COMPARISON` | `value > reference` |
|
|
159
|
+
| **`GREATER_OR_EQUAL`** | `COMPARISON` | `value >= reference` |
|
|
160
|
+
| **`LESS`** | `COMPARISON` | `value < reference` |
|
|
161
|
+
| **`LESS_OR_EQUAL`** | `COMPARISON` | `value <= reference` |
|
|
162
|
+
| **`DIFFERENT`** | `EQ` | `value !== reference` |
|
|
68
163
|
|
|
69
|
-
|
|
70
|
-
<code>
|
|
71
|
-
@DBC.**ParamvalueProvider**<br>
|
|
72
|
-
public method(@REGEX.PRE(/XDBC.\*/g) input : string)
|
|
73
|
-
</code><br><br>
|
|
74
|
-
The **POST**-Method can be used to decorate a method:<br>
|
|
75
|
-
<code>
|
|
76
|
-
@EQ.POST(10)<br>
|
|
77
|
-
public method() { return 10 ;}<br></code>
|
|
78
|
-
Whenever the method returns, it's return-value will be checked.<br>
|
|
164
|
+
### Built-in Regular Expressions
|
|
79
165
|
|
|
80
|
-
|
|
81
|
-
<code>
|
|
82
|
-
@REGEX.INVARIANT(/^a$/)<br>
|
|
83
|
-
public testProperty = "a";
|
|
84
|
-
</code><br>
|
|
85
|
-
Whenever the field is assigned a value and also when initialized, the new value will be checked. So there's no need to write a getter and setter just because there's is the necessity to perform checks on the value anymore.<br>
|
|
166
|
+
`REGEX.stdExp` provides ready-to-use patterns:
|
|
86
167
|
|
|
87
|
-
|
|
168
|
+
| Key | Validates |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `htmlAttributeName` | HTML attribute names |
|
|
171
|
+
| `eMail` | Email addresses |
|
|
172
|
+
| `property` | Property identifiers |
|
|
173
|
+
| `url` | URLs |
|
|
174
|
+
| `keyPath` | Key paths |
|
|
175
|
+
| `date` | Date strings |
|
|
176
|
+
| `dateFormat` | Date format patterns |
|
|
177
|
+
| `cssSelector` | CSS selectors |
|
|
178
|
+
| `boolean` | Boolean string literals |
|
|
179
|
+
| `colorCodeHEX` | Hex color codes |
|
|
180
|
+
| `simpleHotkey` | Keyboard shortcuts |
|
|
181
|
+
| `bcp47` | BCP 47 language tags |
|
|
88
182
|
|
|
89
|
-
|
|
183
|
+
---
|
|
90
184
|
|
|
91
|
-
|
|
185
|
+
## Core Concepts
|
|
92
186
|
|
|
93
|
-
|
|
187
|
+
### Decorator Types
|
|
94
188
|
|
|
95
|
-
|
|
189
|
+
Every contract exposes three decorator factories:
|
|
96
190
|
|
|
97
|
-
|
|
191
|
+
| Decorator | Applies To | Validates |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `Contract.PRE(...)` | Method parameters | Input values before method execution |
|
|
194
|
+
| `Contract.POST(...)` | Methods | Return value after method execution |
|
|
195
|
+
| `Contract.INVARIANT(...)` | Fields / Properties | Value on every assignment (including initialization) |
|
|
196
|
+
|
|
197
|
+
### The `ParamvalueProvider` Decorator
|
|
198
|
+
|
|
199
|
+
TypeScript parameter decorators do not natively receive parameter values. Any method using `PRE` parameter contracts **must** be decorated with `@DBC.ParamvalueProvider`:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
@DBC.ParamvalueProvider
|
|
203
|
+
public process(
|
|
204
|
+
@TYPE.PRE("string") name: string,
|
|
205
|
+
@REGEX.PRE(/^\d{4}$/) code: string
|
|
206
|
+
) { ... }
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Path Resolution
|
|
210
|
+
|
|
211
|
+
All `PRE`, `POST`, and `INVARIANT` decorators accept an optional **`path`** parameter — a dotted path that specifies a nested property of the value to validate instead of the value itself:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Validate that element.tagName === "SELECT"
|
|
215
|
+
@DBC.ParamvalueProvider
|
|
216
|
+
public handleElement(@EQ.PRE("SELECT", false, "tagName") el: HTMLElement) { }
|
|
217
|
+
|
|
218
|
+
// Validate that value.length === 1
|
|
219
|
+
@EQ.INVARIANT(1, false, "length")
|
|
220
|
+
public singleChar = "X";
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Path resolution supports:
|
|
224
|
+
- **Dot notation**: `"user.address.city"`
|
|
225
|
+
- **Array indices**: `"items[0]"`
|
|
226
|
+
- **Method calls**: `"getName()"`
|
|
227
|
+
- **HTML attributes**: `"@data-id"`
|
|
228
|
+
|
|
229
|
+
### Custom Hints
|
|
230
|
+
|
|
231
|
+
Add context to error messages with the optional **`hint`** parameter:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
@DBC.ParamvalueProvider
|
|
235
|
+
public setAge(
|
|
236
|
+
@GREATER_OR_EQUAL.PRE(0, undefined, "Age must be non-negative") age: number
|
|
237
|
+
) { }
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Advanced Features
|
|
243
|
+
|
|
244
|
+
### Composing Contracts with `AE`
|
|
245
|
+
|
|
246
|
+
Validate array elements against one or more contracts, optionally targeting a specific index range:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// All elements must be non-empty strings matching a date pattern
|
|
250
|
+
@AE.PRE([new TYPE("string"), new REGEX(/^\d{4}-\d{2}-\d{2}$/)])
|
|
251
|
+
public processDates(dates: string[]) { }
|
|
252
|
+
|
|
253
|
+
// Only elements at indices 1 through 3
|
|
254
|
+
@AE.PRE([new REGEX(/^[A-Z]+$/)], 1, 3)
|
|
255
|
+
public processRange(items: string[]) { }
|
|
256
|
+
|
|
257
|
+
// Single element at index 0
|
|
258
|
+
@AE.PRE([new TYPE("number")], 0)
|
|
259
|
+
public processFirst(values: unknown[]) { }
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Logical Composition with `OR`
|
|
263
|
+
|
|
264
|
+
At least one contract must pass:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
@DBC.ParamvalueProvider
|
|
268
|
+
public setStatus(@OR.PRE([new EQ("active"), new EQ("inactive"), new EQ("pending")]) status: string) { }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Conditional Contracts with `IF`
|
|
272
|
+
|
|
273
|
+
Apply a contract only when a precondition holds:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// If the value is a string, it must also match digits-only
|
|
277
|
+
@IF.PRE(new TYPE("string"), new REGEX(/^\d+$/))
|
|
278
|
+
public processInput(value: unknown) { }
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Zod Schema Integration
|
|
282
|
+
|
|
283
|
+
Leverage Zod schemas for complex structural validation:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { z } from "zod";
|
|
287
|
+
|
|
288
|
+
const UserSchema = z.object({
|
|
289
|
+
name: z.string().min(1),
|
|
290
|
+
email: z.string().email(),
|
|
291
|
+
age: z.number().int().positive()
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
@DBC.ParamvalueProvider
|
|
295
|
+
public createUser( @ZOD.PRE(UserSchema) data: unknown) { }
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Type-Safe Static Checks
|
|
299
|
+
|
|
300
|
+
Several contracts offer static `tsCheck` methods for imperative validation outside of decorators:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// Throws if value is not a string
|
|
304
|
+
const name = TYPE.tsCheck<string>(input, "string", "Expected a string");
|
|
305
|
+
|
|
306
|
+
// Throws if value doesn't match regex
|
|
307
|
+
const code = REGEX.tsCheck<string>(input, /^\d{4}$/, "Invalid code format");
|
|
308
|
+
|
|
309
|
+
// Throws if not an instance of Date
|
|
310
|
+
const date = INSTANCE.tsCheck<Date>(input, Date, "Expected a Date");
|
|
311
|
+
|
|
312
|
+
// Throws if none of the conditions pass
|
|
313
|
+
const result = OR.tsCheck<string>(input, [new EQ("a"), new EQ("b")]);
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Configuration
|
|
319
|
+
|
|
320
|
+
### DBC Instance Settings
|
|
321
|
+
|
|
322
|
+
A default DBC instance is automatically registered at `WaXCode.DBC` when the module is imported. You can access and configure it:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { DBC } from "xdbc";
|
|
326
|
+
|
|
327
|
+
// Access the default DBC instance
|
|
328
|
+
const dbc = (globalThis as any).WaXCode.DBC as DBC;
|
|
329
|
+
|
|
330
|
+
// Toggle contract checking
|
|
331
|
+
dbc.executionSettings.checkPreconditions = true;
|
|
332
|
+
dbc.executionSettings.checkPostconditions = true;
|
|
333
|
+
dbc.executionSettings.checkInvariants = true;
|
|
334
|
+
|
|
335
|
+
// Configure infringement handling
|
|
336
|
+
dbc.infringementSettings.throwException = true; // throw DBC.Infringement on violation
|
|
337
|
+
dbc.infringementSettings.logToConsole = false; // log to console instead
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Multiple DBC Instances
|
|
341
|
+
|
|
342
|
+
Create isolated DBC instances with separate configurations using `DBC.register()`:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Register a vendor-specific instance at a custom path
|
|
346
|
+
const vendorDbc = new DBC(
|
|
347
|
+
{ throwException: false, logToConsole: true },
|
|
348
|
+
);
|
|
349
|
+
DBC.register(vendorDbc, "MyVendor.DBC");
|
|
350
|
+
|
|
351
|
+
// Route a contract to the custom instance via its path
|
|
352
|
+
@REGEX.INVARIANT(/^[A-Z]+$/, undefined, undefined, "MyVendor.DBC")
|
|
353
|
+
public code = "ABC";
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
> **Note:** `new DBC()` does not automatically mount onto `globalThis`. Call `DBC.register(instance, path)` to make an instance available for decorator resolution.
|
|
357
|
+
|
|
358
|
+
### Test Isolation
|
|
359
|
+
|
|
360
|
+
Use `DBC.isolated()` to run tests with a temporary DBC instance that doesn't affect other tests:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
DBC.isolated((tempDbc) => {
|
|
364
|
+
// tempDbc is registered at "WaXCode.DBC" for the duration of this callback
|
|
365
|
+
tempDbc.executionSettings.checkPreconditions = false;
|
|
366
|
+
|
|
367
|
+
// ... run tests with contracts disabled ...
|
|
368
|
+
});
|
|
369
|
+
// Original DBC instance is automatically restored here
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Disabling Contracts in Production
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
const dbc = (globalThis as any).WaXCode.DBC as DBC;
|
|
376
|
+
dbc.executionSettings.checkPreconditions = false;
|
|
377
|
+
dbc.executionSettings.checkPostconditions = false;
|
|
378
|
+
dbc.executionSettings.checkInvariants = false;
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## API Documentation
|
|
384
|
+
|
|
385
|
+
Full generated API documentation is available at **[callaris.github.io/XDBC](https://callaris.github.io/XDBC/)**.
|
|
386
|
+
|
|
387
|
+
See [`Demo.ts`](src/Demo.ts) for annotated usage examples.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Contributing
|
|
98
392
|
|
|
99
|
-
## Contribution
|
|
100
393
|
Participation is highly valued and warmly welcomed. The ultimate goal is to create a tool that proves genuinely useful and empowers a wide range of developers to build more robust and reliable applications.
|
|
101
394
|
|
|
395
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines, and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community standards.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## License
|
|
400
|
+
|
|
401
|
+
[MIT](LICENSE) © Callari, Salvatore
|
|
102
402
|
|
|
403
|
+
---
|
|
103
404
|
|
|
405
|
+
<sub>"Design by Contract" is a registered trademark of Eiffel Software. XDBC is an independent project and is not affiliated with or endorsed by Eiffel Software.</sub>
|