guarden 1.0.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/LICENSE +21 -0
- package/README.md +370 -0
- package/dist/cjs/assert/assertions.js +82 -0
- package/dist/cjs/assert/assertions.js.map +1 -0
- package/dist/cjs/assert/index.js +13 -0
- package/dist/cjs/assert/index.js.map +1 -0
- package/dist/cjs/assert/invariant.js +43 -0
- package/dist/cjs/assert/invariant.js.map +1 -0
- package/dist/cjs/env/index.js +11 -0
- package/dist/cjs/env/index.js.map +1 -0
- package/dist/cjs/env/schema.js +313 -0
- package/dist/cjs/env/schema.js.map +1 -0
- package/dist/cjs/guards/advanced.js +190 -0
- package/dist/cjs/guards/advanced.js.map +1 -0
- package/dist/cjs/guards/combinators.js +195 -0
- package/dist/cjs/guards/combinators.js.map +1 -0
- package/dist/cjs/guards/index.js +68 -0
- package/dist/cjs/guards/index.js.map +1 -0
- package/dist/cjs/guards/primitives.js +123 -0
- package/dist/cjs/guards/primitives.js.map +1 -0
- package/dist/cjs/guards/structures.js +113 -0
- package/dist/cjs/guards/structures.js.map +1 -0
- package/dist/cjs/index.js +135 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/result/async.js +132 -0
- package/dist/cjs/result/async.js.map +1 -0
- package/dist/cjs/result/index.js +15 -0
- package/dist/cjs/result/index.js.map +1 -0
- package/dist/cjs/result/option.js +175 -0
- package/dist/cjs/result/option.js.map +1 -0
- package/dist/cjs/result/result.js +208 -0
- package/dist/cjs/result/result.js.map +1 -0
- package/dist/cjs/transform/coerce.js +151 -0
- package/dist/cjs/transform/coerce.js.map +1 -0
- package/dist/cjs/transform/index.js +36 -0
- package/dist/cjs/transform/index.js.map +1 -0
- package/dist/cjs/transform/pipe.js +18 -0
- package/dist/cjs/transform/pipe.js.map +1 -0
- package/dist/cjs/transform/sanitize.js +218 -0
- package/dist/cjs/transform/sanitize.js.map +1 -0
- package/dist/cjs/utils/errors.js +94 -0
- package/dist/cjs/utils/errors.js.map +1 -0
- package/dist/cjs/utils/types.js +6 -0
- package/dist/cjs/utils/types.js.map +1 -0
- package/dist/esm/assert/assertions.js +75 -0
- package/dist/esm/assert/assertions.js.map +1 -0
- package/dist/esm/assert/index.js +3 -0
- package/dist/esm/assert/index.js.map +1 -0
- package/dist/esm/assert/invariant.js +39 -0
- package/dist/esm/assert/invariant.js.map +1 -0
- package/dist/esm/env/index.js +2 -0
- package/dist/esm/env/index.js.map +1 -0
- package/dist/esm/env/schema.js +304 -0
- package/dist/esm/env/schema.js.map +1 -0
- package/dist/esm/guards/advanced.js +170 -0
- package/dist/esm/guards/advanced.js.map +1 -0
- package/dist/esm/guards/combinators.js +182 -0
- package/dist/esm/guards/combinators.js.map +1 -0
- package/dist/esm/guards/index.js +8 -0
- package/dist/esm/guards/index.js.map +1 -0
- package/dist/esm/guards/primitives.js +108 -0
- package/dist/esm/guards/primitives.js.map +1 -0
- package/dist/esm/guards/structures.js +97 -0
- package/dist/esm/guards/structures.js.map +1 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/result/async.js +127 -0
- package/dist/esm/result/async.js.map +1 -0
- package/dist/esm/result/index.js +4 -0
- package/dist/esm/result/index.js.map +1 -0
- package/dist/esm/result/option.js +170 -0
- package/dist/esm/result/option.js.map +1 -0
- package/dist/esm/result/result.js +203 -0
- package/dist/esm/result/result.js.map +1 -0
- package/dist/esm/transform/coerce.js +143 -0
- package/dist/esm/transform/coerce.js.map +1 -0
- package/dist/esm/transform/index.js +4 -0
- package/dist/esm/transform/index.js.map +1 -0
- package/dist/esm/transform/pipe.js +14 -0
- package/dist/esm/transform/pipe.js.map +1 -0
- package/dist/esm/transform/sanitize.js +195 -0
- package/dist/esm/transform/sanitize.js.map +1 -0
- package/dist/esm/utils/errors.js +84 -0
- package/dist/esm/utils/errors.js.map +1 -0
- package/dist/esm/utils/types.js +5 -0
- package/dist/esm/utils/types.js.map +1 -0
- package/dist/types/assert/assertions.d.ts +52 -0
- package/dist/types/assert/assertions.d.ts.map +1 -0
- package/dist/types/assert/index.d.ts +3 -0
- package/dist/types/assert/index.d.ts.map +1 -0
- package/dist/types/assert/invariant.d.ts +29 -0
- package/dist/types/assert/invariant.d.ts.map +1 -0
- package/dist/types/env/index.d.ts +2 -0
- package/dist/types/env/index.d.ts.map +1 -0
- package/dist/types/env/schema.d.ts +131 -0
- package/dist/types/env/schema.d.ts.map +1 -0
- package/dist/types/guards/advanced.d.ts +101 -0
- package/dist/types/guards/advanced.d.ts.map +1 -0
- package/dist/types/guards/combinators.d.ts +120 -0
- package/dist/types/guards/combinators.d.ts.map +1 -0
- package/dist/types/guards/index.d.ts +5 -0
- package/dist/types/guards/index.d.ts.map +1 -0
- package/dist/types/guards/primitives.d.ts +75 -0
- package/dist/types/guards/primitives.d.ts.map +1 -0
- package/dist/types/guards/structures.d.ts +58 -0
- package/dist/types/guards/structures.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/result/async.d.ts +72 -0
- package/dist/types/result/async.d.ts.map +1 -0
- package/dist/types/result/index.d.ts +4 -0
- package/dist/types/result/index.d.ts.map +1 -0
- package/dist/types/result/option.d.ts +106 -0
- package/dist/types/result/option.d.ts.map +1 -0
- package/dist/types/result/result.d.ts +126 -0
- package/dist/types/result/result.d.ts.map +1 -0
- package/dist/types/transform/coerce.d.ts +73 -0
- package/dist/types/transform/coerce.d.ts.map +1 -0
- package/dist/types/transform/index.d.ts +4 -0
- package/dist/types/transform/index.d.ts.map +1 -0
- package/dist/types/transform/pipe.d.ts +44 -0
- package/dist/types/transform/pipe.d.ts.map +1 -0
- package/dist/types/transform/sanitize.d.ts +108 -0
- package/dist/types/transform/sanitize.d.ts.map +1 -0
- package/dist/types/utils/errors.d.ts +51 -0
- package/dist/types/utils/errors.d.ts.map +1 -0
- package/dist/types/utils/types.d.ts +29 -0
- package/dist/types/utils/types.d.ts.map +1 -0
- package/package.json +144 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Avinashvelu03
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🛡️ Guarden
|
|
4
|
+
|
|
5
|
+
### TypeScript-First Runtime Safety Toolkit
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/guarden)
|
|
8
|
+
[](https://bundlephobia.com/package/guarden)
|
|
9
|
+
[](https://github.com/Avinashvelu03/guarden/blob/main/LICENSE)
|
|
10
|
+
[](https://github.com/Avinashvelu03/guarden/actions)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://www.npmjs.com/package/guarden)
|
|
13
|
+
|
|
14
|
+
**Guard your runtime. Harden your types.**
|
|
15
|
+
|
|
16
|
+
Blazing-fast, tree-shakeable utilities for bridging TypeScript's compile-time safety with real-world runtime guarantees. Type guards, assertions, Result/Option monads, data pipelines, and environment validation — all in one lightweight, zero-dependency package.
|
|
17
|
+
|
|
18
|
+
[Getting Started](#-getting-started) · [API Reference](#-api-reference) · [Why Guarden?](#-why-guarden) · [Contributing](CONTRIBUTING.md)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ✨ Highlights
|
|
25
|
+
|
|
26
|
+
- 🔒 **60+ type guards** with automatic TypeScript narrowing
|
|
27
|
+
- ⚡ **Zero dependencies** — nothing to audit, nothing to break
|
|
28
|
+
- 🌳 **Tree-shakeable** — import only what you use
|
|
29
|
+
- 📦 **Dual ESM/CJS** — works everywhere
|
|
30
|
+
- 🧪 **298 tests** — battle-tested reliability
|
|
31
|
+
- 🎯 **TypeScript-first** — written in TypeScript, for TypeScript
|
|
32
|
+
- 🏗️ **5 modules** — Guards, Assert, Result/Option, Transform, Env
|
|
33
|
+
|
|
34
|
+
## 📦 Getting Started
|
|
35
|
+
|
|
36
|
+
### Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install guarden
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
yarn add guarden
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm add guarden
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Quick Example
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import {
|
|
54
|
+
isString, isNumber, shape, // Guards
|
|
55
|
+
assert, invariant, // Assertions
|
|
56
|
+
Ok, Err, type Result, // Result monad
|
|
57
|
+
pipe, slugify, escapeHtml, // Transform
|
|
58
|
+
createEnv, envString, envNumber // Env validation
|
|
59
|
+
} from 'guarden';
|
|
60
|
+
|
|
61
|
+
// 🛡️ Type Guards — automatic narrowing
|
|
62
|
+
const input: unknown = getApiResponse();
|
|
63
|
+
if (isString(input)) {
|
|
64
|
+
input.toUpperCase(); // ✅ TypeScript knows it's a string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 🏗️ Object Shape Validation
|
|
68
|
+
const isUser = shape({
|
|
69
|
+
name: isString,
|
|
70
|
+
age: isNumber,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (isUser(data)) {
|
|
74
|
+
console.log(data.name); // ✅ Fully typed
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ⚡ Result Monad — no more try/catch
|
|
78
|
+
function divide(a: number, b: number): Result<number, string> {
|
|
79
|
+
if (b === 0) return Err('Division by zero');
|
|
80
|
+
return Ok(a / b);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
divide(10, 2).match({
|
|
84
|
+
ok: (value) => console.log(`Result: ${value}`),
|
|
85
|
+
err: (error) => console.error(`Error: ${error}`),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// 🔄 Data Pipelines
|
|
89
|
+
const slug = pipe(
|
|
90
|
+
' Hello, World! 🌍 ',
|
|
91
|
+
(s) => s.trim(),
|
|
92
|
+
slugify,
|
|
93
|
+
);
|
|
94
|
+
// → "hello-world"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 🔌 Modular Imports
|
|
98
|
+
|
|
99
|
+
Import the entire library or just the modules you need:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Full import
|
|
103
|
+
import { isString, Ok, pipe } from 'guarden';
|
|
104
|
+
|
|
105
|
+
// Module-specific imports (optimal tree-shaking)
|
|
106
|
+
import { isString, shape, arrayOf } from 'guarden/guards';
|
|
107
|
+
import { assert, invariant } from 'guarden/assert';
|
|
108
|
+
import { Ok, Err, Some, None } from 'guarden/result';
|
|
109
|
+
import { pipe, slugify, escapeHtml } from 'guarden/transform';
|
|
110
|
+
import { createEnv, envString } from 'guarden/env';
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 📖 API Reference
|
|
114
|
+
|
|
115
|
+
### 🛡️ Guards
|
|
116
|
+
|
|
117
|
+
Type guards that automatically narrow TypeScript types:
|
|
118
|
+
|
|
119
|
+
#### Primitives
|
|
120
|
+
```typescript
|
|
121
|
+
isString(value) // value is string
|
|
122
|
+
isNumber(value) // value is number (excludes NaN)
|
|
123
|
+
isBoolean(value) // value is boolean
|
|
124
|
+
isBigInt(value) // value is bigint
|
|
125
|
+
isSymbol(value) // value is symbol
|
|
126
|
+
isNull(value) // value is null
|
|
127
|
+
isUndefined(value) // value is undefined
|
|
128
|
+
isNullish(value) // value is null | undefined
|
|
129
|
+
isNonNullish(value) // value is NonNullable<T>
|
|
130
|
+
isFunction(value) // value is Function
|
|
131
|
+
isPrimitive(value) // value is string | number | boolean | ...
|
|
132
|
+
isFiniteNumber(value) // value is number (excludes NaN & Infinity)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### Structures
|
|
136
|
+
```typescript
|
|
137
|
+
isArray(value) // value is unknown[]
|
|
138
|
+
isObject(value) // value is Record<string, unknown>
|
|
139
|
+
isPlainObject(value) // value is plain {} (not class instance)
|
|
140
|
+
isMap(value) // value is Map
|
|
141
|
+
isSet(value) // value is Set
|
|
142
|
+
isDate(value) // value is Date
|
|
143
|
+
isValidDate(value) // value is Date (valid, not NaN)
|
|
144
|
+
isRegExp(value) // value is RegExp
|
|
145
|
+
isError(value) // value is Error
|
|
146
|
+
isPromise(value) // value is Promise (or thenable)
|
|
147
|
+
isTypedArray(value) // value is Uint8Array, Float32Array, etc.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Advanced
|
|
151
|
+
```typescript
|
|
152
|
+
isNonEmptyString(value) // non-empty string
|
|
153
|
+
isEmail(value) // valid email format
|
|
154
|
+
isURL(value) // valid URL
|
|
155
|
+
isUUID(value) // valid UUID v1-v5
|
|
156
|
+
isISO8601(value) // valid ISO 8601 date string
|
|
157
|
+
isJSONString(value) // valid JSON string
|
|
158
|
+
isHexColor(value) // valid hex color (#fff, #ffffff)
|
|
159
|
+
isPositiveNumber(value) // number > 0
|
|
160
|
+
isNegativeNumber(value) // number < 0
|
|
161
|
+
isInteger(value) // integer number
|
|
162
|
+
isSafeInteger(value) // safe integer
|
|
163
|
+
isNonEmptyArray(value) // array with 1+ elements
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Factory Guards
|
|
167
|
+
```typescript
|
|
168
|
+
isInRange(0, 100)(value) // number in range [0, 100]
|
|
169
|
+
isOneOf(['a', 'b', 'c'] as const) // value is 'a' | 'b' | 'c'
|
|
170
|
+
isMatch(/^[a-z]+$/)(value) // string matches regex
|
|
171
|
+
isMinLength(3)(value) // string.length >= 3
|
|
172
|
+
isMaxLength(100)(value) // string.length <= 100
|
|
173
|
+
isInstanceOf(TypeError)(value) // value instanceof TypeError
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Combinators
|
|
177
|
+
```typescript
|
|
178
|
+
// Compose guards for complex validation
|
|
179
|
+
const isPositiveInt = and(isInteger, isPositiveNumber);
|
|
180
|
+
const isStringOrNum = or(isString, isNumber);
|
|
181
|
+
const isNotNull = not(isNull);
|
|
182
|
+
|
|
183
|
+
// Object shape validation
|
|
184
|
+
const isUser = shape({
|
|
185
|
+
name: isString,
|
|
186
|
+
age: isNumber,
|
|
187
|
+
email: isEmail,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Collection guards
|
|
191
|
+
const isStringArray = arrayOf(isString);
|
|
192
|
+
const isCoord = tuple(isNumber, isNumber);
|
|
193
|
+
const isScoreMap = mapOf(isString, isNumber);
|
|
194
|
+
const isConfig = recordOf(isString);
|
|
195
|
+
|
|
196
|
+
// Refinement
|
|
197
|
+
const isEven = refine(isNumber, n => n % 2 === 0);
|
|
198
|
+
|
|
199
|
+
// Optional/Nullable
|
|
200
|
+
const isOptName = optional(isString); // string | undefined
|
|
201
|
+
const isNullName = nullable(isString); // string | null
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### ✅ Assert
|
|
207
|
+
|
|
208
|
+
Runtime assertions with TypeScript type narrowing:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// Basic assertion
|
|
212
|
+
assert(condition, 'Something went wrong');
|
|
213
|
+
|
|
214
|
+
// Narrowing assertions
|
|
215
|
+
assertDefined(value); // narrows away undefined
|
|
216
|
+
assertNonNull(value); // narrows away null | undefined
|
|
217
|
+
assertType(value, isString); // narrows to string
|
|
218
|
+
assertTruthy(value); // narrows away falsy values
|
|
219
|
+
|
|
220
|
+
// Invariant (for bug detection)
|
|
221
|
+
invariant(items.length > 0, 'items should never be empty here');
|
|
222
|
+
|
|
223
|
+
// Exhaustive checking
|
|
224
|
+
type Dir = 'up' | 'down';
|
|
225
|
+
function move(dir: Dir) {
|
|
226
|
+
switch (dir) {
|
|
227
|
+
case 'up': return goUp();
|
|
228
|
+
case 'down': return goDown();
|
|
229
|
+
default: unreachable(dir); // compile error if case missing!
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### 🎯 Result & Option
|
|
237
|
+
|
|
238
|
+
Rust-inspired monadic error handling:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// Result<T, E> — explicit success/failure
|
|
242
|
+
const result = Ok(42);
|
|
243
|
+
result.map(v => v * 2); // Ok(84)
|
|
244
|
+
result.andThen(v => Ok(v + 1)); // Ok(43)
|
|
245
|
+
result.unwrapOr(0); // 42
|
|
246
|
+
result.match({
|
|
247
|
+
ok: v => `Got ${v}`,
|
|
248
|
+
err: e => `Failed: ${e}`,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Catch exceptions safely
|
|
252
|
+
const parsed = ResultUtils.from(() => JSON.parse(input));
|
|
253
|
+
|
|
254
|
+
// Async support
|
|
255
|
+
const data = await ResultAsync.from(fetch('/api'))
|
|
256
|
+
.map(res => res.json())
|
|
257
|
+
.unwrapOr(defaultData);
|
|
258
|
+
|
|
259
|
+
// Option<T> — explicit null handling
|
|
260
|
+
const user = OptionUtils.from(getUserById(id));
|
|
261
|
+
const name = user.map(u => u.name).unwrapOr('Anonymous');
|
|
262
|
+
|
|
263
|
+
// Collect multiple Results
|
|
264
|
+
const all = ResultUtils.all([Ok(1), Ok(2), Ok(3)]); // Ok([1, 2, 3])
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### 🔄 Transform
|
|
270
|
+
|
|
271
|
+
Type-safe data pipelines and string utilities:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// Pipe — chain transformations
|
|
275
|
+
const result = pipe(
|
|
276
|
+
' Hello World ',
|
|
277
|
+
trim,
|
|
278
|
+
lowercase,
|
|
279
|
+
slugify,
|
|
280
|
+
); // → "hello-world"
|
|
281
|
+
|
|
282
|
+
// Flow — create reusable pipelines
|
|
283
|
+
const processTitle = flow(trim, capitalize, (s) => truncate(s, 50));
|
|
284
|
+
processTitle(' hello world '); // → "Hello world"
|
|
285
|
+
|
|
286
|
+
// Type coercion (returns Result!)
|
|
287
|
+
toNumber('42'); // Ok(42)
|
|
288
|
+
toNumber('abc'); // Err(CoercionError)
|
|
289
|
+
toBoolean('yes'); // true
|
|
290
|
+
toDate('2024-01-15'); // Ok(Date)
|
|
291
|
+
toArray(42); // [42]
|
|
292
|
+
toArray([1, 2]); // [1, 2]
|
|
293
|
+
toArray(null); // []
|
|
294
|
+
|
|
295
|
+
// String sanitization
|
|
296
|
+
camelCase('hello world'); // "helloWorld"
|
|
297
|
+
kebabCase('helloWorld'); // "hello-world"
|
|
298
|
+
snakeCase('helloWorld'); // "hello_world"
|
|
299
|
+
slugify('Hello, World! 🌍'); // "hello-world"
|
|
300
|
+
escapeHtml('<script>alert(1)'); // "<script>alert(1)"
|
|
301
|
+
truncate('Long text...', 10); // "Long te..."
|
|
302
|
+
stripHtml('<p>Hello</p>'); // "Hello"
|
|
303
|
+
collapseWhitespace('a b c'); // "a b c"
|
|
304
|
+
reverse('hello'); // "olleh"
|
|
305
|
+
countOccurrences('aaa', 'a'); // 3 (non-overlapping: 3)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### 🌍 Env
|
|
311
|
+
|
|
312
|
+
Validated, type-safe environment variables:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { createEnv, envString, envNumber, envBoolean, envEnum } from 'guarden/env';
|
|
316
|
+
|
|
317
|
+
export const env = createEnv({
|
|
318
|
+
DATABASE_URL: envString().url().required(),
|
|
319
|
+
PORT: envNumber().port().default(3000),
|
|
320
|
+
DEBUG: envBoolean().default(false),
|
|
321
|
+
NODE_ENV: envEnum(['development', 'production', 'test'] as const),
|
|
322
|
+
API_KEY: envString().minLength(16),
|
|
323
|
+
ALLOWED_ORIGINS: envString().matches(/^https?:\/\//),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Fully typed! 🎉
|
|
327
|
+
env.DATABASE_URL // string
|
|
328
|
+
env.PORT // number
|
|
329
|
+
env.DEBUG // boolean
|
|
330
|
+
env.NODE_ENV // 'development' | 'production' | 'test'
|
|
331
|
+
|
|
332
|
+
// Throws descriptive errors at startup if invalid:
|
|
333
|
+
// EnvConfigError: Environment configuration is invalid:
|
|
334
|
+
// DATABASE_URL: is required but not set
|
|
335
|
+
// PORT: must be a valid port (1-65535)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 🤔 Why Guarden?
|
|
341
|
+
|
|
342
|
+
| Feature | Guarden | Zod | neverthrow | ts-pattern |
|
|
343
|
+
|---|---|---|---|---|
|
|
344
|
+
| Type Guards | ✅ 60+ | ❌ | ❌ | ❌ |
|
|
345
|
+
| Assertions | ✅ | ❌ | ❌ | ❌ |
|
|
346
|
+
| Result/Option | ✅ | ❌ | ✅ | ❌ |
|
|
347
|
+
| Data Transform | ✅ | ✅ (parse) | ❌ | ❌ |
|
|
348
|
+
| Env Validation | ✅ | 🔶 (manual) | ❌ | ❌ |
|
|
349
|
+
| Zero Dependencies | ✅ | ✅ | ✅ | ✅ |
|
|
350
|
+
| Tree-Shakeable | ✅ | 🔶 | ✅ | ✅ |
|
|
351
|
+
| Bundle Size | ~3KB | ~14KB | ~5KB | ~3KB |
|
|
352
|
+
|
|
353
|
+
**Guarden** is the only library that provides a **complete runtime safety toolkit** in one package. Instead of installing 4+ separate libraries, get everything you need from one dependency.
|
|
354
|
+
|
|
355
|
+
## 🔧 Requirements
|
|
356
|
+
|
|
357
|
+
- **Node.js** >= 18.0.0
|
|
358
|
+
- **TypeScript** >= 5.0 (recommended 5.5+)
|
|
359
|
+
|
|
360
|
+
## 📄 License
|
|
361
|
+
|
|
362
|
+
[MIT](LICENSE) © [Avinashvelu03](https://github.com/Avinashvelu03)
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
<div align="center">
|
|
367
|
+
|
|
368
|
+
**If Guarden helps your project, consider giving it a ⭐ on [GitHub](https://github.com/Avinashvelu03/guarden)!**
|
|
369
|
+
|
|
370
|
+
</div>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Guarden — Runtime Assertions
|
|
4
|
+
// ============================================================================
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.assert = assert;
|
|
7
|
+
exports.assertDefined = assertDefined;
|
|
8
|
+
exports.assertNonNull = assertNonNull;
|
|
9
|
+
exports.assertType = assertType;
|
|
10
|
+
exports.assertTruthy = assertTruthy;
|
|
11
|
+
const errors_js_1 = require("../utils/errors.js");
|
|
12
|
+
/**
|
|
13
|
+
* Assert that a condition is true. Throws `AssertionError` if false.
|
|
14
|
+
* Narrows the condition to `true` in the subsequent code.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* assert(user !== null, 'User must exist');
|
|
19
|
+
* user.name; // OK — TypeScript knows user is not null
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
function assert(condition, message) {
|
|
23
|
+
if (!condition) {
|
|
24
|
+
throw new errors_js_1.AssertionError(message ?? 'Assertion failed');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Assert that a value is defined (not undefined).
|
|
29
|
+
* Narrows away `undefined`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const val: string | undefined = getConfig('key');
|
|
34
|
+
* assertDefined(val, 'Config key is required');
|
|
35
|
+
* val.toUpperCase(); // OK
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function assertDefined(value, message) {
|
|
39
|
+
if (value === undefined) {
|
|
40
|
+
throw new errors_js_1.AssertionError(message ?? 'Expected value to be defined, but got undefined');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Assert that a value is not null and not undefined.
|
|
45
|
+
* Narrows away `null | undefined`.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* assertNonNull(result);
|
|
50
|
+
* result.doSomething(); // OK
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function assertNonNull(value, message) {
|
|
54
|
+
if (value === null || value === undefined) {
|
|
55
|
+
throw new errors_js_1.AssertionError(message ?? `Expected non-null value, but got ${value === null ? 'null' : 'undefined'}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Assert that a value passes a type guard.
|
|
60
|
+
* Narrows to the guarded type.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* assertType(input, isString, 'Expected string input');
|
|
65
|
+
* input.toUpperCase(); // OK — narrowed to string
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
function assertType(value, guard, message) {
|
|
69
|
+
if (!guard(value)) {
|
|
70
|
+
throw new errors_js_1.AssertionError(message ?? `Type assertion failed: value ${JSON.stringify(value)} did not pass guard`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Assert that a value is truthy.
|
|
75
|
+
* Narrows away falsy values (false, 0, '', null, undefined, NaN).
|
|
76
|
+
*/
|
|
77
|
+
function assertTruthy(value, message) {
|
|
78
|
+
if (!value) {
|
|
79
|
+
throw new errors_js_1.AssertionError(message ?? `Expected truthy value, but got ${JSON.stringify(value)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=assertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../../../src/assert/assertions.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,+BAA+B;AAC/B,+EAA+E;;AAe/E,wBAIC;AAaD,sCAOC;AAYD,sCASC;AAYD,gCAUC;AAMD,oCASC;AA/FD,kDAAoD;AAGpD;;;;;;;;;GASG;AACH,SAAgB,MAAM,CAAC,SAAkB,EAAE,OAAgB;IACzD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,0BAAc,CAAC,OAAO,IAAI,kBAAkB,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,aAAa,CAC3B,KAAQ,EACR,OAAgB;IAEhB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,0BAAc,CAAC,OAAO,IAAI,iDAAiD,CAAC,CAAC;IACzF,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,aAAa,CAC3B,KAAQ,EACR,OAAgB;IAEhB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,MAAM,IAAI,0BAAc,CACtB,OAAO,IAAI,oCAAoC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CACvF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,UAAU,CACxB,KAAc,EACd,KAAe,EACf,OAAgB;IAEhB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,0BAAc,CACtB,OAAO,IAAI,gCAAgC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,qBAAqB,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAC1B,KAAQ,EACR,OAAgB;IAEhB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,0BAAc,CACtB,OAAO,IAAI,kCAAkC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACrE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unreachable = exports.invariant = exports.assertTruthy = exports.assertType = exports.assertNonNull = exports.assertDefined = exports.assert = void 0;
|
|
4
|
+
var assertions_js_1 = require("./assertions.js");
|
|
5
|
+
Object.defineProperty(exports, "assert", { enumerable: true, get: function () { return assertions_js_1.assert; } });
|
|
6
|
+
Object.defineProperty(exports, "assertDefined", { enumerable: true, get: function () { return assertions_js_1.assertDefined; } });
|
|
7
|
+
Object.defineProperty(exports, "assertNonNull", { enumerable: true, get: function () { return assertions_js_1.assertNonNull; } });
|
|
8
|
+
Object.defineProperty(exports, "assertType", { enumerable: true, get: function () { return assertions_js_1.assertType; } });
|
|
9
|
+
Object.defineProperty(exports, "assertTruthy", { enumerable: true, get: function () { return assertions_js_1.assertTruthy; } });
|
|
10
|
+
var invariant_js_1 = require("./invariant.js");
|
|
11
|
+
Object.defineProperty(exports, "invariant", { enumerable: true, get: function () { return invariant_js_1.invariant; } });
|
|
12
|
+
Object.defineProperty(exports, "unreachable", { enumerable: true, get: function () { return invariant_js_1.unreachable; } });
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/assert/index.ts"],"names":[],"mappings":";;;AAAA,iDAMyB;AALvB,uGAAA,MAAM,OAAA;AACN,8GAAA,aAAa,OAAA;AACb,8GAAA,aAAa,OAAA;AACb,2GAAA,UAAU,OAAA;AACV,6GAAA,YAAY,OAAA;AAGd,+CAAwD;AAA/C,yGAAA,SAAS,OAAA;AAAE,2GAAA,WAAW,OAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Guarden — Invariants & Unreachable
|
|
4
|
+
// ============================================================================
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.invariant = invariant;
|
|
7
|
+
exports.unreachable = unreachable;
|
|
8
|
+
const errors_js_1 = require("../utils/errors.js");
|
|
9
|
+
/**
|
|
10
|
+
* Assert an invariant condition. Throws `InvariantError` if violated.
|
|
11
|
+
* Use this for conditions that indicate a bug in your code if broken.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* invariant(items.length > 0, 'items should never be empty at this point');
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
function invariant(condition, message) {
|
|
19
|
+
if (!condition) {
|
|
20
|
+
throw new errors_js_1.InvariantError(message);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Mark code as unreachable. Useful for exhaustive switch/if checks.
|
|
25
|
+
* Throws `UnreachableError` at runtime if reached.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* type Direction = 'up' | 'down';
|
|
30
|
+
*
|
|
31
|
+
* function move(dir: Direction) {
|
|
32
|
+
* switch (dir) {
|
|
33
|
+
* case 'up': return goUp();
|
|
34
|
+
* case 'down': return goDown();
|
|
35
|
+
* default: unreachable(dir); // TypeScript error if case missing
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
function unreachable(value) {
|
|
41
|
+
throw new errors_js_1.UnreachableError(value);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=invariant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant.js","sourceRoot":"","sources":["../../../src/assert/invariant.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,qCAAqC;AACrC,+EAA+E;;AAa/E,8BAIC;AAmBD,kCAEC;AApCD,kDAAsE;AAEtE;;;;;;;;GAQG;AACH,SAAgB,SAAS,CAAC,SAAkB,EAAE,OAAe;IAC3D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,0BAAc,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,WAAW,CAAC,KAAY;IACtC,MAAM,IAAI,4BAAgB,CAAC,KAAK,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EnvField = exports.envEnum = exports.envBoolean = exports.envNumber = exports.envString = exports.createEnv = void 0;
|
|
4
|
+
var schema_js_1 = require("./schema.js");
|
|
5
|
+
Object.defineProperty(exports, "createEnv", { enumerable: true, get: function () { return schema_js_1.createEnv; } });
|
|
6
|
+
Object.defineProperty(exports, "envString", { enumerable: true, get: function () { return schema_js_1.envString; } });
|
|
7
|
+
Object.defineProperty(exports, "envNumber", { enumerable: true, get: function () { return schema_js_1.envNumber; } });
|
|
8
|
+
Object.defineProperty(exports, "envBoolean", { enumerable: true, get: function () { return schema_js_1.envBoolean; } });
|
|
9
|
+
Object.defineProperty(exports, "envEnum", { enumerable: true, get: function () { return schema_js_1.envEnum; } });
|
|
10
|
+
Object.defineProperty(exports, "EnvField", { enumerable: true, get: function () { return schema_js_1.EnvField; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/env/index.ts"],"names":[],"mappings":";;;AAAA,yCAOqB;AANnB,sGAAA,SAAS,OAAA;AACT,sGAAA,SAAS,OAAA;AACT,sGAAA,SAAS,OAAA;AACT,uGAAA,UAAU,OAAA;AACV,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA"}
|