@typed/guard 0.7.2 → 1.0.0-beta.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/README.md +98 -0
- package/dist/index.d.ts +247 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +176 -4
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +314 -0
- package/package.json +18 -24
- package/src/index.test.ts +370 -0
- package/src/index.ts +661 -3
- package/tsconfig.json +4 -26
- package/.nvmrc +0 -1
- package/biome.json +0 -36
- package/dist/ExtensibleFunction.d.ts +0 -1
- package/dist/ExtensibleFunction.js +0 -7
- package/dist/ExtensibleFunction.js.map +0 -1
- package/dist/Guard.d.ts +0 -143
- package/dist/Guard.js +0 -95
- package/dist/Guard.js.map +0 -1
- package/dist/Guardable.d.ts +0 -10
- package/dist/Guardable.js +0 -13
- package/dist/Guardable.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/readme.md +0 -199
- package/src/ExtensibleFunction.test.ts +0 -102
- package/src/ExtensibleFunction.ts +0 -8
- package/src/Guard.test.ts +0 -189
- package/src/Guard.ts +0 -536
- package/src/Guardable.test.ts +0 -18
- package/src/Guardable.ts +0 -22
package/src/Guard.test.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { Effect, Option, pipe, Predicate, Schema } from 'effect'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
import * as Guard from './Guard.js'
|
|
4
|
-
|
|
5
|
-
describe('Guard', () => {
|
|
6
|
-
// Helper function to run a guard and get the result
|
|
7
|
-
const runGuard = <I, O, E>(guard: Guard.Guard<I, O, E>, input: I) => Effect.runSync(guard(input))
|
|
8
|
-
|
|
9
|
-
describe('basic functionality', () => {
|
|
10
|
-
it('should create a basic guard that succeeds', () => {
|
|
11
|
-
const guard = (input: number): Effect.Effect<Option.Option<string>, never, never> =>
|
|
12
|
-
Effect.succeed(input > 5 ? Option.some(input.toString()) : Option.none())
|
|
13
|
-
|
|
14
|
-
expect(runGuard(guard, 10)).toEqual(Option.some('10'))
|
|
15
|
-
expect(runGuard(guard, 3)).toEqual(Option.none())
|
|
16
|
-
})
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
describe('compose', () => {
|
|
20
|
-
it('should compose two guards', () => {
|
|
21
|
-
const numberToString = (n: number) =>
|
|
22
|
-
Effect.succeed(n > 5 ? Option.some(n.toString()) : Option.none())
|
|
23
|
-
const stringToLength = (s: string) =>
|
|
24
|
-
Effect.succeed(s.length > 1 ? Option.some(s.length) : Option.none())
|
|
25
|
-
|
|
26
|
-
const composed = Guard.compose(numberToString, stringToLength)
|
|
27
|
-
|
|
28
|
-
expect(runGuard(composed, 10)).toEqual(Option.some(2))
|
|
29
|
-
expect(runGuard(composed, 3)).toEqual(Option.none())
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
describe('mapEffect and map', () => {
|
|
34
|
-
it('should map over guard results with Effect', () => {
|
|
35
|
-
const guard = (n: number) => Effect.succeed(n > 5 ? Option.some(n) : Option.none())
|
|
36
|
-
const mapped = Guard.mapEffect(guard, (n) => Effect.succeed(n * 2))
|
|
37
|
-
|
|
38
|
-
expect(runGuard(mapped, 10)).toEqual(Option.some(20))
|
|
39
|
-
expect(runGuard(mapped, 3)).toEqual(Option.none())
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('should map over guard results synchronously', () => {
|
|
43
|
-
const guard = (n: number) => Effect.succeed(n > 5 ? Option.some(n) : Option.none())
|
|
44
|
-
const mapped = Guard.map(guard, (n) => n * 2)
|
|
45
|
-
|
|
46
|
-
expect(runGuard(mapped, 10)).toEqual(Option.some(20))
|
|
47
|
-
expect(runGuard(mapped, 3)).toEqual(Option.none())
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
describe('tap', () => {
|
|
52
|
-
it('should perform side effects without modifying the result', () => {
|
|
53
|
-
let sideEffect = 0
|
|
54
|
-
const guard = (n: number) => Effect.succeed(n > 5 ? Option.some(n) : Option.none())
|
|
55
|
-
const tapped = Guard.tap(guard, (n) =>
|
|
56
|
-
Effect.sync(() => {
|
|
57
|
-
sideEffect = n
|
|
58
|
-
}),
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
expect(runGuard(tapped, 10)).toEqual(Option.some(10))
|
|
62
|
-
expect(sideEffect).toBe(10)
|
|
63
|
-
|
|
64
|
-
expect(runGuard(tapped, 3)).toEqual(Option.none())
|
|
65
|
-
expect(sideEffect).toBe(10) // Unchanged because guard returned None
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
describe('filterMap and filter', () => {
|
|
70
|
-
it('should filter and map values', () => {
|
|
71
|
-
const filtered = Guard.filterMap(Guard.identity<number>, (n) =>
|
|
72
|
-
n > 5 ? Option.some(n.toString()) : Option.none(),
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
expect(runGuard(filtered, 10)).toEqual(Option.some('10'))
|
|
76
|
-
expect(runGuard(filtered, 3)).toEqual(Option.none())
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('should filter values', () => {
|
|
80
|
-
const filtered = Guard.filter(Guard.identity<number>, (n) => n > 5)
|
|
81
|
-
|
|
82
|
-
expect(runGuard(filtered, 10)).toEqual(Option.some(10))
|
|
83
|
-
expect(runGuard(filtered, 3)).toEqual(Option.none())
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
describe('any', () => {
|
|
88
|
-
it('should match against multiple guards', () => {
|
|
89
|
-
const anyGuard = Guard.any({
|
|
90
|
-
number: Guard.liftPredicate(Predicate.isNumber),
|
|
91
|
-
string: Guard.liftPredicate(Predicate.isString),
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
expect(runGuard(anyGuard, 123)).toEqual(Option.some({ _tag: 'number', value: 123 }))
|
|
95
|
-
expect(runGuard(anyGuard, 'test')).toEqual(Option.some({ _tag: 'string', value: 'test' }))
|
|
96
|
-
expect(runGuard(anyGuard, true)).toEqual(Option.none())
|
|
97
|
-
})
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
describe('liftPredicate', () => {
|
|
101
|
-
it('should lift a predicate into a guard', () => {
|
|
102
|
-
const guard = Guard.liftPredicate(Predicate.isNumber)
|
|
103
|
-
|
|
104
|
-
expect(runGuard(guard, 123)).toEqual(Option.some(123))
|
|
105
|
-
expect(runGuard(guard, 'test')).toEqual(Option.none())
|
|
106
|
-
})
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
describe('error handling', () => {
|
|
110
|
-
describe('catchAll and catchAllCause', () => {
|
|
111
|
-
it('should catch errors', () => {
|
|
112
|
-
const failingGuard = (_: unknown) => Effect.fail('error')
|
|
113
|
-
const recovered = Guard.catchAll(failingGuard, (e) =>
|
|
114
|
-
Effect.succeed(['recovered:', e].join(' ')),
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
expect(runGuard(recovered, 123)).toEqual(Option.some('recovered: error'))
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('should catch error causes', () => {
|
|
121
|
-
const failingGuard = (_: unknown) => Effect.fail('error')
|
|
122
|
-
const recovered = Guard.catchAllCause(failingGuard, () =>
|
|
123
|
-
Effect.succeed('recovered from cause'),
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
expect(runGuard(recovered, 123)).toEqual(Option.some('recovered from cause'))
|
|
127
|
-
})
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
describe('catchTag', () => {
|
|
131
|
-
it('should catch specific error tags', () => {
|
|
132
|
-
type MyError = { _tag: 'MyError'; message: string }
|
|
133
|
-
const failingGuard = (_: unknown) =>
|
|
134
|
-
Effect.fail({ _tag: 'MyError', message: 'test error' } as MyError)
|
|
135
|
-
const recovered = Guard.catchTag(failingGuard, 'MyError', (e) =>
|
|
136
|
-
Effect.succeed(['recovered:', e.message].join(' ')),
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
expect(runGuard(recovered, 123)).toEqual(Option.some('recovered: test error'))
|
|
140
|
-
})
|
|
141
|
-
})
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
describe('schema integration', () => {
|
|
145
|
-
describe('fromSchemaDecode', () => {
|
|
146
|
-
it('should create a guard from a schema decoder', () => {
|
|
147
|
-
const guard = Guard.fromSchemaDecodeUnknown(Schema.Number)
|
|
148
|
-
|
|
149
|
-
expect(runGuard(guard, 123)).toEqual(Option.some(123))
|
|
150
|
-
expect(runGuard(guard, 'test')).toEqual(Option.none())
|
|
151
|
-
})
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
describe('fromSchemaEncode', () => {
|
|
155
|
-
it('should create a guard from a schema encoder', () => {
|
|
156
|
-
const guard = Guard.fromSchemaEncode(Schema.Number)
|
|
157
|
-
|
|
158
|
-
expect(runGuard(guard, 123)).toEqual(Option.some(123))
|
|
159
|
-
expect(runGuard(guard, 'test' as any)).toEqual(Option.none())
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
describe('property attachment', () => {
|
|
165
|
-
describe('attachProperty', () => {
|
|
166
|
-
it('should attach a property to the guarded value', () => {
|
|
167
|
-
const guard = (n: number) => Effect.succeed(Option.some({ value: n }))
|
|
168
|
-
const withProp = Guard.addTag(guard, 'number')
|
|
169
|
-
|
|
170
|
-
expect(runGuard(withProp, 123)).toEqual(Option.some({ _tag: 'number', value: 123 }))
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
describe('bind', () => {
|
|
175
|
-
it('should bind a new property based on the guarded value', () => {
|
|
176
|
-
const guard = pipe(
|
|
177
|
-
Guard.identity<number>,
|
|
178
|
-
Guard.bindTo('value'),
|
|
179
|
-
Guard.bind('asString', ({ value }) => Effect.succeedSome(value.toString())),
|
|
180
|
-
Guard.let('asBigInt', ({ asString }) => BigInt(asString)),
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
expect(runGuard(guard, 123)).toEqual(
|
|
184
|
-
Option.some({ value: 123, asString: '123', asBigInt: 123n }),
|
|
185
|
-
)
|
|
186
|
-
})
|
|
187
|
-
})
|
|
188
|
-
})
|
|
189
|
-
})
|