{"id":46275563,"url":"https://github.com/final-hill/decorator-contracts","last_synced_at":"2026-03-04T04:09:15.219Z","repository":{"id":41186536,"uuid":"261578171","full_name":"final-hill/decorator-contracts","owner":"final-hill","description":"Decorator Contracts is a library that provides the ability to create and assign Code Contracts to ECMAScript and TypeScript classes. This enables enforcement of the Liskov substitution principle and the Open-closed principle of SOLID to support Design By Contract™.","archived":false,"fork":false,"pushed_at":"2025-05-24T04:21:13.000Z","size":1502,"stargazers_count":18,"open_issues_count":7,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-06T17:18:19.836Z","etag":null,"topics":["assertion","behavioral-subtyping","code-contracts","decorators","demands","design-by-contract","encapsulation","exception-handling","if-and-only-if","invariant","liskov-substitution-principle","material-implication","organized-panic","overrides","postcondition","precondition","requires","rescue","solid"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/final-hill.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-05T20:39:43.000Z","updated_at":"2025-09-12T13:38:27.000Z","dependencies_parsed_at":"2024-06-17T16:21:44.956Z","dependency_job_id":null,"html_url":"https://github.com/final-hill/decorator-contracts","commit_stats":{"total_commits":325,"total_committers":1,"mean_commits":325.0,"dds":0.0,"last_synced_commit":"7cecfd97a0ed1c335de6ea673422644b3d60a505"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/final-hill/decorator-contracts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/final-hill%2Fdecorator-contracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/final-hill%2Fdecorator-contracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/final-hill%2Fdecorator-contracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/final-hill%2Fdecorator-contracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/final-hill","download_url":"https://codeload.github.com/final-hill/decorator-contracts/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/final-hill%2Fdecorator-contracts/sbom","scorecard":{"id":399884,"data":{"date":"2025-08-11","repo":{"name":"github.com/final-hill/decorator-contracts","commit":"a6ab4aee5a7f01367972c5f1a48aed6cb8249adc"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/27 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":2,"reason":"3 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build-release.yml:1","Warn: no topLevel permission defined: .github/workflows/build.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-release.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-release.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-release.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-release.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-release.yml:58: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-release.yml:64: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/final-hill/decorator-contracts/build.yml/master?enable=pin","Info:   0 out of   9 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU Affero General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/build-release.yml:47"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":8,"reason":"2 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-xffm-g5w8-qvg7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T19:51:05.562Z","repository_id":41186536,"created_at":"2025-08-18T19:51:05.562Z","updated_at":"2025-08-18T19:51:05.562Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30071674,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T03:25:38.285Z","status":"ssl_error","status_checked_at":"2026-03-04T03:25:05.086Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assertion","behavioral-subtyping","code-contracts","decorators","demands","design-by-contract","encapsulation","exception-handling","if-and-only-if","invariant","liskov-substitution-principle","material-implication","organized-panic","overrides","postcondition","precondition","requires","rescue","solid"],"created_at":"2026-03-04T04:09:14.544Z","updated_at":"2026-03-04T04:09:15.203Z","avatar_url":"https://github.com/final-hill.png","language":"TypeScript","readme":"# Decorator Contracts\r\n\r\n[![Build](https://github.com/final-hill/decorator-contracts/workflows/Build/badge.svg?branch=master)](https://github.com/final-hill/decorator-contracts/actions?query=workflow%3ABuild%2FRelease)\r\n[![npm version](https://badge.fury.io/js/%40final-hill%2Fdecorator-contracts.svg)](https://www.npmjs.com/package/@final-hill/decorator-contracts)\r\n[![Downloads](https://img.shields.io/npm/dm/@final-hill/decorator-contracts.svg)](https://www.npmjs.com/package/@final-hill/decorator-contracts)\r\n\r\n## Table of Contents\r\n\r\n- [Introduction](#introduction)\r\n- [Library Installation](#library-installation)\r\n- [Usage](#usage)\r\n- [Checked Mode](#checked-mode)\r\n- [Assertions](#assertions)\r\n- [Implies](#implies)\r\n- [Iff](#iff)\r\n- [Encapsulation](#encapsulation)\r\n- [Invariants](#invariants)\r\n- [Demands](#demands)\r\n- [Ensures](#ensures)\r\n- [Rescue](#rescue)\r\n- [The order of assertions](#the-order-of-assertions)\r\n- [Further Reading](#further-reading)\r\n\r\n## Introduction\r\n\r\nDecorator Contracts is a library that provides the ability to create and assign\r\nCode Contracts to ECMAScript and TypeScript classes. This enables\r\nenforcement of the\r\n[Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)\r\nof [SOLID](https://en.wikipedia.org/wiki/SOLID)\r\nand [Organized Panic](https://en.wikipedia.org/wiki/Exception_handling#Exception_handling_based_on_design_by_contract) to support\r\n[Design By Contract™](https://en.wikipedia.org/wiki/Design_by_contract).\r\n\r\nNote that the license for this library is [AGPL-3.0-only](https://www.gnu.org/licenses/agpl-3.0.en.html).\r\nYou should [know what that means](https://choosealicense.com/licenses/agpl-3.0/) before\r\nusing this. If you would like an exception to this license per section 7\r\n[contact the author](mailto:michael.haufe@final-hill.com).\r\n\r\n## Library Installation\r\n\r\nThe latest version:\r\n\r\n`npm install @final-hill/decorator-contracts`\r\n\r\nA specific version:\r\n\r\n`npm install @final-hill/decorator-contracts@x.x.x`\r\n\r\nFor use in a browser (no build step) via [unpkg.com](https://unpkg.com/):\r\n\r\n```html\r\n\u003cscript type=\"importmap\"\u003e\r\n{\r\n    \"imports\": {\r\n        \"@final-hill/decorator-contracts\": \"https://unpkg.com/@final-hill/decorator-contracts\"\r\n    }\r\n}\r\n\u003c/script\u003e\r\n```\r\n\r\nVia [skypack.dev](https://www.skypack.dev/):\r\n\r\n`\u003cscript type=\"module\" src=\"https://cdn.skypack.dev/@final-hill/decorator-contracts\"\u003e\u003c/script\u003e`\r\n\r\n## Usage\r\n\r\nAfter installation, use the decorators directly on your class methods and properties. All contracted classes must extend `Contracted` and be instantiated using the static `.new()` method.\r\n\r\n```typescript\r\nimport { Contracted, invariant, demands, ensures } from '@final-hill/decorator-contracts';\r\n\r\n@invariant((self: Stack\u003cany\u003e) =\u003e\r\n    self.isEmpty() == (self.size == 0) \u0026\u0026\r\n    self.isFull() == (self.size == self.limit) \u0026\u0026\r\n    self.size \u003e= 0 \u0026\u0026 self.size \u003c= self.limit\r\n)\r\nclass Stack\u003cT\u003e extends Contracted {\r\n    private _implementation: T[] = [];\r\n    private _size = 0;\r\n    private _limit: number;\r\n\r\n    constructor(limit: number) {\r\n        super();\r\n        this._limit = limit;\r\n    }\r\n\r\n    get limit() { return this._limit; }\r\n    get size() { return this._size; }\r\n\r\n    clear(): void {\r\n        this._implementation = [];\r\n        this._size = 0;\r\n    }\r\n\r\n    isEmpty(): boolean { return this._implementation.length == 0; }\r\n    isFull(): boolean { return this._implementation.length == this.limit; }\r\n\r\n    @demands((self: Stack\u003cany\u003e) =\u003e !self.isEmpty())\r\n    @ensures((self: Stack\u003cany\u003e, _args, old: Stack\u003cany\u003e) =\u003e\r\n        !self.isFull() \u0026\u0026 self.size == old.size - 1)\r\n    pop(): T {\r\n        this._size--;\r\n        return this._implementation.pop()!;\r\n    }\r\n\r\n    @demands((self: Stack\u003cany\u003e) =\u003e !self.isFull())\r\n    @ensures((self, [item], old: Stack\u003cany\u003e) =\u003e\r\n        !self.isEmpty() \u0026\u0026\r\n        self.top === item \u0026\u0026\r\n        self.size === old.size + 1\r\n    )\r\n    push(item: T): void {\r\n        this._size++;\r\n        this._implementation.push(item);\r\n    }\r\n\r\n    get top(): T | undefined {\r\n        return this._implementation.at(-1);\r\n    }\r\n}\r\n\r\nconst stack = Stack.new(3);\r\nstack.push(1);\r\nstack.push(2);\r\nstack.pop();\r\n```\r\n\r\n## Instantiation: Using the Static `.new(...)` Method\r\n\r\nBecause this library uses JavaScript `Proxy` to enforce contracts, you **must** instantiate all classes that extend `Contracted` using the static `.new(...)` method (not with `new MyClass(...)`).\r\n\r\nThis is required so that the instance is wrapped in a Proxy and invariants are asserted immediately after construction. Direct use of the `new` keyword will throw an error.\r\n\r\n## Checked Mode\r\n\r\nChecked Mode controls whether contract checking (invariants, demands, ensures, and rescue) is enabled or disabled at runtime. This feature allows you to turn off contract enforcement in production environments for maximum performance, while keeping it enabled during development and testing to catch bugs and contract violations early.\r\n\r\nBy default, Checked Mode is enabled. You can control it via the static `[checkedMode]` symbol property of Contracted:\r\n\r\n```typescript\r\nContracted[checkedMode] = false; // Disable Checked Mode\r\n```\r\n\r\nOne approach to managing this is to leverage environment variables. For example, you can interrogate `NODE_ENV` to determine whether to enable or disable Checked Mode:\r\n\r\n```typescript\r\nContracted[checkedMode] = process.env.NODE_ENV === 'development'\r\n```\r\n\r\n## Assertions\r\n\r\nUse the `assert` function for inline assertions:\r\n\r\n```typescript\r\nimport { assert } from '@final-hill/decorator-contracts';\r\n\r\nfunction avg(xs: number[]): number {\r\n    assert(xs.length \u003e 0, 'The list can not be empty');\r\n    return xs.reduce((sum, next) =\u003e sum + next) / xs.length;\r\n}\r\n```\r\n\r\nIf you are using TypeScript, `assert` will also assert the type of the condition:\r\n\r\n```typescript\r\nlet str: any = 'foo';\r\n\r\nstr.toUpperCase(); // str is any\r\n\r\nassert(typeof str == 'string');\r\n\r\nstr.toUpperCase(); // str is now a string\r\n```\r\n\r\n**`assert` should not be used for validating arguments**\r\n\r\nUse the `@demands` decorator for this purpose.\r\n\r\n## Implies\r\n\r\nWhen defining predicates it is a common use case to encode [material implication](https://en.wikipedia.org/wiki/Material_conditional):\r\n\r\n```typescript\r\nimport { implies } from '@final-hill/decorator-contracts';\r\n\r\nimplies(weather.isSunny, person.visitsBeach);\r\n// Equivalent to: !weather.isSunny || person.visitsBeach\r\n```\r\n\r\n## Iff\r\n\r\nWhen defining predicates it is a common use case to encode [if and only if](https://en.wikipedia.org/wiki/Logical_biconditional):\r\n\r\n```typescript\r\nimport { iff } from '@final-hill/decorator-contracts';\r\n\r\niff(person.hasTicket, person.ridesTrain);\r\n// Equivalent to: implies(p, q) \u0026\u0026 implies(q, p)\r\n```\r\n\r\n## Encapsulation\r\n\r\nTo guarantee invariants remain valid for classes, public property definitions are forbidden. All interactions with a contracted class must be done through a method or getter/setter. Only properties that start with `_` (conventionally private) can be assigned to after construction.\r\n\r\n```typescript\r\nclass Example extends Contracted {\r\n    _private = 1; // Allowed\r\n    publicProp = 2; // Not allowed\r\n}\r\n```\r\n\r\n## Note on Private Fields and Accessors\r\n\r\nDue to the use of JavaScript `Proxy` in the implementation of this library, native `#private` fields and the `accessor` keyword are **not supported**. Instead, use conventional private fields (e.g., `_private`) and define explicit getter/setter pairs for encapsulation:\r\n\r\n```typescript\r\nclass Example extends Contracted {\r\n    private _value = 0;\r\n    get value() { return this._value; }\r\n    set value(v: number) { this._value = v; }\r\n}\r\n```\r\n\r\nDo **not** use:\r\n\r\n- `#privateField` syntax\r\n- `accessor foo` syntax\r\n\r\nThese will not work correctly with the contract enforcement provided by this library.\r\n\r\n## Invariants\r\n\r\nUse the `@invariant` decorator on your class to enforce relationships between properties and methods. The invariant is checked after construction, before and after every method execution, and before and after every get/set usage.\r\n\r\n```typescript\r\n@invariant(self =\u003e self.value \u003e= 0)\r\nclass Foo extends Contracted {\r\n    protected _value = 0;\r\n    get value() { return this._value; }\r\n    set value(v: number) { this._value = v; }\r\n\r\n    inc(): void { this.value++; }\r\n    dec(): void { this.value--; }\r\n}\r\n```\r\n\r\n### Subcontracting and Invariants\r\n\r\nWhen subclassing, if a subclass defines its own `@invariant`, the resulting invariant is **strengthened**: the subclass invariant and the base class invariant are both enforced (they are logically AND-ed together). This means all invariants in the inheritance chain must hold for the object to be valid.\r\n\r\n```typescript\r\n@invariant(self =\u003e self.value \u003e= 0)\r\nclass Base extends Contracted {\r\n    // ...\r\n}\r\n\r\n@invariant(self =\u003e self.value \u003c= 10)\r\nclass Sub extends Base {\r\n    // ...\r\n}\r\n// The effective invariant for Sub is: (self.value \u003e= 0) \u0026\u0026 (self.value \u003c= 10)\r\n```\r\n\r\n## Demands\r\n\r\nUse the `@demands` decorator to specify preconditions for methods or get/set. If the condition fails, an error is thrown before the method executes.\r\n\r\n```typescript\r\nclass Foo extends Contracted {\r\n    protected _value = 0;\r\n    get value() { return this._value; }\r\n    set value(v: number) { this._value = v; }\r\n\r\n    @demands(self =\u003e self.value \u003e= 0)\r\n    dec(): void { this.value--; }\r\n}\r\n```\r\n\r\n### Subcontracting and Demands\r\n\r\nWhen subclassing, if a subclass defines its own `@demands` for a method, the resulting precondition is **weakened**: the subclass and base class demands are OR-ed together. The method can be called if **any** of the demands in the inheritance chain are satisfied.\r\n\r\n```typescript\r\nclass Base extends Contracted {\r\n    @demands((_self, x: number) =\u003e x \u003e= 0)\r\n    foo(x: number) { /* ... */ }\r\n}\r\n\r\nclass Sub extends Base {\r\n    @demands((_self, x: number) =\u003e x === 42)\r\n    override foo(x: number) { /* ... */ }\r\n}\r\n// The effective demand for Sub#foo is: (x \u003e= 0) || (x === 42)\r\n```\r\n\r\n## Ensures\r\n\r\nUse the `@ensures` decorator to specify postconditions for methods or get/set. If the condition fails, an error is thrown after the method executes.\r\n\r\n```typescript\r\nclass Foo extends Contracted {\r\n    protected _value = 0;\r\n    get value() { return this._value; }\r\n    set value(v: number) { this._value = v; }\r\n\r\n    @ensures(self =\u003e self.value \u003e= 0)\r\n    dec(): void { this.value--; }\r\n}\r\n```\r\n\r\n### Subcontracting and Ensures\r\n\r\nWhen subclassing, if a subclass defines its own `@ensures` for a method, the resulting postcondition is **strengthened**: the subclass and base class ensures are AND-ed together. The method's result must satisfy **all** ensures in the inheritance chain.\r\n\r\n```typescript\r\nclass Base extends Contracted {\r\n    @ensures((_self, _args, x: number) =\u003e x \u003e= 0)\r\n    foo(x: number) { /* ... */ }\r\n}\r\n\r\nclass Sub extends Base {\r\n    @ensures((_self, _args, x: number) =\u003e x \u003c= 10)\r\n    override foo(x: number) { /* ... */ }\r\n}\r\n// The effective ensures for Sub#foo is: (x \u003e= 0) \u0026\u0026 (x \u003c= 10)\r\n```\r\n\r\n## Rescue\r\n\r\nUse the `@rescue` decorator to handle exceptions thrown by a method, restore invariants, or retry execution:\r\n\r\n```typescript\r\nimport { rescue } from '@final-hill/decorator-contracts';\r\n\r\nclass Example extends Contracted {\r\n    private _value = 3;\r\n    get value() { return this._value; }\r\n    set value(v) { this._value = v; }\r\n\r\n    @rescue(self =\u003e self.value = 5)\r\n    method1(): void { throw new Error('I am error'); }\r\n}\r\n```\r\n\r\nYou can also use the `retry` function in your rescue handler to attempt the method again:\r\n\r\n```typescript\r\nclass Example extends Contracted {\r\n    @rescue((_self, _error, _args, retry) =\u003e retry(3))\r\n    method(value: number): number {\r\n        if (value \u003c= 0)\r\n            throw new Error('value must be greater than 0');\r\n        else\r\n            return value;\r\n    }\r\n}\r\n```\r\n\r\n### Robustness, Organized Panic, and the Benefits of `@rescue`\r\n\r\nThe `@rescue` decorator enables a mechanism for **Robustness** in your code. It allows your implementation to respond to situations not specified, providing the ability to handle exceptions in a structured way. This is sometimes called **Organized Panic**.\r\n\r\nIn addition, `@rescue` provides a foundation for **N-Version programming**, **Fault-Tolerance**, and **Redundancy**. You can implement multiple strategies for handling errors, retrying with alternative logic or data, and ensuring your application can recover from unexpected failures.\r\n\r\nUnlike traditional `try/catch`, where exceptions are non-resumable and often handled far from the source (leading to scattered and redundant error handling), `@rescue` allows for **resumable exceptions** and localized, declarative error handling. This means:\r\n\r\n- You can lexically determine where exception handling occurs.\r\n- You can restore invariants or perform corrective actions before retrying or failing.\r\n- You can optionally call `retry` to attempt the method again with new arguments or after fixing state, providing a form of fault tolerance and robustness.\r\n- You can implement alternative strategies for error recovery, supporting N-Version programming and redundancy.\r\n\r\n**Inheritance:**\r\n\r\nIf a base class declares a `@rescue` handler for a method, that handler is inherited by subclasses unless the subclass overrides it with its own `@rescue` declaration. This ensures that robust error handling is preserved throughout the inheritance chain, unless explicitly changed.\r\n\r\nThis approach leads to more maintainable and robust code, as error handling is organized and closely tied to the contract of the class feature, rather than being scattered throughout the call stack.\r\n\r\n## The order of assertions\r\n\r\nWhen `obj.feature` is called the happy path is:\r\n\r\n![image](./docs/happy-path.png)\r\n\r\nIf an error is thrown and there is no `@rescue` defined then the `@invariant` is checked before the error is raised to the caller.\r\n\r\nIf an error is thrown in the `@invariant` then it is raised to the caller.\r\n\r\nIf an error is thrown in the `@demands` then the error is raised to the caller. In this case the `@invariant` is not checked because the feature body has not been entered and the assertion cannot modify the state of the class without calling another method which is governed by its own contracts.\r\n\r\nIf an error is thrown by the feature body or the `@ensures` then the `@rescue` is executed. If `retry` is called then the process starts from the beginning.\r\n\r\nIf `@rescue` throws an error or does not call `retry` then the `@invariant` is checked before the error is raised to the caller.\r\n\r\n![image](./docs/order-of-assertions.png)\r\n\r\n## Further Reading\r\n\r\n- [Design by Contract](https://en.wikipedia.org/wiki/Design_by_contract)\r\n- [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)\r\n- [Object-Oriented Software Construction](https://en.wikipedia.org/wiki/Object-Oriented_Software_Construction)\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinal-hill%2Fdecorator-contracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffinal-hill%2Fdecorator-contracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinal-hill%2Fdecorator-contracts/lists"}