# icc-contracts

> Code-contract library for javascript. Supports pre-conditions.

Latest version **0.1.0** (published 2020-07-05) · MIT license · 0 weekly downloads

## Install

```sh
npm install icc-contracts
pnpm add icc-contracts
yarn add icc-contracts
bun add icc-contracts
```

## Health

**Score 20/100 (F)** — status: abandoned.

Positive: esm support; no vulnerabilities.

Warnings: low downloads; no types; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.1.0 |
| Published | 2020-07-05 |
| First published | 2020-07-05 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 30.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Duncan Kimpton |
| Maintainers | dgkimpton |
| Keywords | code, contract, contracts |

## Links

- npm: https://www.npmjs.com/package/icc-contracts
- Repository: https://github.com/dgkimpton/icc-contracts
- Homepage: https://github.com/dgkimpton/icc-contracts#readme
- Issues: https://github.com/dgkimpton/icc-contracts/issues
- npm.io page: https://npm.io/package/icc-contracts

## Dependencies (1)

- [@babel/polyfill](https://npm.io/package/@babel/polyfill.md) ^7.10.4

## Alternatives

- [flatbuffers](https://npm.io/package/flatbuffers.md) — 6.0M weekly downloads
- [@exodus/patch-broken-hermes-typed-arrays](https://npm.io/package/@exodus/patch-broken-hermes-typed-arrays.md) — 28.5K weekly downloads
- [@native-to-anchor/buffer-layout](https://npm.io/package/@native-to-anchor/buffer-layout.md) — 12.2K weekly downloads
- [binary-parser-encoder](https://npm.io/package/binary-parser-encoder.md) — 5.3K weekly downloads
- [datasets-fi-postalcodes](https://npm.io/package/datasets-fi-postalcodes.md) — 1.1K weekly downloads

## Recent versions

- 0.1.0 (latest) — 2020-07-05

## README

# 🦔 Integrated Code Contracts
## **icc-contracts**, mostly invisible confidence

## ⚠ Warning
This repository is currently an experimental concept and is subject to heavy change. Not recommended for general use.

### Overview
In general, code-contracts allow the specification of pre-conditions and post-conditions.

icc-contracts focuses on providing pre-conditions in pure JavaScript.

### Motivation
But why? Are unit-tests not sufficient?

Well written unit-tests cover 100% of the functionality of the unit. Unit-tests don't cover the way that the unit is integrated into the product.  For that we have IntegrationTests. However, when you combine your units and something doesn't work, how do you know what went wrong? Code-contracts to the rescue! Code-contracts help you quickly pinpoint the failing interaction. Since they also document the expectations, they help you understand how to resolve the problem too.

### icc-contracts
Icc is a way of writing code-contract pre-conditions as JavaScript.

They run when the code does, providing guard rails as you develop. In other words, making sure the input is in the format you expected.

It must be assumed that, given correct input, your code will produce the expected output. If you aren't sure of that, add more unit tests!

#### Public Only
If you've written good unit-tests, then you are sure the unit is doing the correct thing. That is, it is internally consistent and meets the specifications. Therefore, the only time things can go wrong is when incorrect input is provided.
Given that only public methods can provide input, these are the only ones adding uncertainty. So add code-contracts to your public methods (only).

#### Performance
Icc-contracts always run when the public methods do Clearly this means there is a performance cost to pay. My opinion is: the performance cost is fine when testing, it is not acceptable in production. So, icc is designed to make it super easy to completely eliminate this overhead in production code as part of your build.
If you're a [Rollup](https://rollupjs.org/) user, simply run the [@rollup/plugin-strip](https://github.com/rollup/plugins/tree/master/packages/strip) to remove all __icc:__ labeled code.

## Installing
Requires [Node 14](https://nodejs.org/en/download/current/) or higher.

At the command line run
```npm install --save icc-contracts```

Within your source files you need to add:
For es6 projects:
``` import contract from 'icc-contracts';```

For CommonJS projects:
``` const contract = require('icc-contracts');```

## Usage Explanation

Given a (stupidly) simple public method
```js
function squareRoot(n) {
    // implementation
}
```
we can intuit that it should be taking a number. Since it's well named, we can also predict it will return the square root of that number. What will it do when it gets a negative number?

One option would be to "just do something" and carry on. [Math.sqrt](https://www.w3schools.com/jsref/jsref_sqrt.asp) chose to return [NaN](https://www.w3schools.com/jsref/jsref_nan.asp).

A safer option might be to reject the negative input.

```js
function squareRoot(n) {
    if(n < 0) throw new Error("n must be positive");
    // implementation
}
```
That, right there, is a code-contract pre-condition. We write them all the time.
Let's not forget that we probably want to be sure we are given a number too:

```js
function squareRoot(n) {
    if('number' !== typeof n) throw new Error(`'n' should be a 'number' but was '${typeof n}'`);
    if(n < 0) throw new Error(`'n' should be positive but was '${n}'`);
    // implementation
}
```
It's getting tedious already, and this is a simple function.

How would this look using **icc-contracts**?

```js
import contract from 'icc-contracts';

function squareRoot(n) {
    icc:contract(is=>[[{n}, is.number, is.positive]]);
    // implementation
}
```

Same readable errors, same verification, much more readable. The expressive pattern also reduces the chance for simple typos to trip you up.

And it is expressed as a single JavaScript labelled-statement, which means we can easily remove it during our build process. Something that would be nigh on impossible to do with the guards in the bare code version :)

### Multiple arguments
It isn't any harder to handle multiple arguments.
```js
import contract from 'icc-contracts';

function func(n, p) {
    icc:contract(is=>[
		[{n}, is.number, is.positive],
		[{p}, is.string, is.notEmpty],
	]);
    // implementation
}
```

### Objects
So far, so simple. But of course, most parameters are not simple native types. So what to do with objects?

No problem, the 'with' condition allows us to apply all the same logic to individual members of objects too.
```js
import contract from 'icc-contracts';

function func(args) {
    icc:contract(is=>[
		[{args}, is.with({
			n: [is.number, is.positive],
			p: [is.string, is.notEmpty],
		})],
	]);
    // implementation
}
```

### Optionals
Ok, ok, but what about those times when we can reasonably accept two different things through the same argument, like optional parameters? Simple, there's a condition for that too.
```js
import contract from 'icc-contracts';

function func(arg) {
    icc:contract(is=>[
		[{arg}, is.either([
			[is.undefined],
			[is.string, is.notEmpty],
		])],
	]);
    // implementation
}
```

## Conditions
Note: 'argument' refers to the argument of the method for which you are specifying the contract.

Until now, is a limited list of conditions that can be applied to arguments. I'm happy to take suggestions for new ones.

|type|condition|description|
|----|---------|-----------|
|is.undefined |            | checks if the argument is undefined|
|is.number    |            | checks if the argument is a number |
|             |is.positive | checks if an argument is a positive|
|             |is.negative | checks if an argument is a negative|
|             |is.greaterThan(v) | checks if an argument is > v|
|             |is.lessThan(v)    | checks if an argument is < v|
|             |is.inRange(inclusiveBegin, inclusiveEnd) | checks if an argument is >= inclusiveBegin && <= inclusiveEnd|
|is.string    |             |checks if the argument is a string|
|             |is.notEmpty  | checks if the argument is not ''|
|is.object    |             |checks if the argument is an object|
|             |is.instanceof(type)| checks if the argument is an instance of the specfied type|
|             |is.with(obj) |'obj' must have a member for each desired member on the argument. Each member must consist of an Array of conditions.<br/>e.g.`{a:[is.number], b:[is.string, is.notEmpty]}`<br/>Only check the properties you care about, the argument may well contain additional properties which your method can safely ignore. |
|is.either([...])|          |takes an array of condition arrays. If any of the arrays pass, either passes. If none of them pass, a compound error of all the failures is thrown.|


## Meta

[LICENSE (Mozilla Public License)](./LICENSE)

---
_Source: https://npm.io/package/icc-contracts · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
