@typed/guard 1.0.0-beta.1 → 1.0.0-beta.11
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 +43 -23
- package/dist/getGuard.d.ts +24 -0
- package/dist/getGuard.d.ts.map +1 -0
- package/dist/getGuard.js +37 -0
- package/dist/index.d.ts +510 -45
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +376 -22
- package/examples/basic.ts +23 -0
- package/package.json +34 -16
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -314
- package/src/index.test.ts +0 -370
- package/src/index.ts +0 -661
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-present The Contributors
|
|
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
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# @typed/guard
|
|
2
2
|
|
|
3
|
-
> **Beta:** This package is in beta; APIs may change.
|
|
3
|
+
> **Beta:** This package is in beta; APIs may change between beta releases. Review the
|
|
4
|
+
> [repository releases](https://github.com/TylorS/typed/releases) before upgrading.
|
|
4
5
|
|
|
5
6
|
`@typed/guard` provides **Effect-based guards**: functions that take an input and produce `Effect<Option<O>, E, R>`. Guards can be composed with `pipe`, `map`, `filter`, `bind`, and integrated with Effect Schema (`fromSchemaDecode` / `fromSchemaEncode`, `decode` / `encode`). Use them for validation, parsing, and route matching when you want a composable “maybe this input becomes this output” with effects.
|
|
6
7
|
|
|
@@ -8,6 +9,16 @@
|
|
|
8
9
|
|
|
9
10
|
- `effect`
|
|
10
11
|
|
|
12
|
+
## Outcome model
|
|
13
|
+
|
|
14
|
+
A guard returns `Effect.Effect<Option.Option<O>, E, R>`. Its three outcomes are distinct:
|
|
15
|
+
|
|
16
|
+
- `Some(output)` means the input matched and produced `output`.
|
|
17
|
+
- `None` means the input did not match. It is a successful Effect, so recovery combinators do not run.
|
|
18
|
+
- An Effect failure means evaluation failed in the typed `E` channel. Defects and interruption remain in the Effect cause.
|
|
19
|
+
|
|
20
|
+
Guard combinators preserve this distinction. Sequential composition stops on `None`, propagates failures, and runs the next guard only for `Some`.
|
|
21
|
+
|
|
11
22
|
## API overview
|
|
12
23
|
|
|
13
24
|
- **Type:** `Guard<I, O, E, R>` — `(input: I) => Effect.Effect<Option.Option<O>, E, R>`; `GuardInput` = Guard | AsGuard.
|
|
@@ -29,7 +40,9 @@
|
|
|
29
40
|
|
|
30
41
|
### Core
|
|
31
42
|
|
|
32
|
-
- **`getGuard(guard)`** — Normalizes a `GuardInput` to a `Guard` (
|
|
43
|
+
- **`getGuard(guard)`** — Normalizes a `GuardInput` to a `Guard`. Functions are always used directly. An `AsGuard` object must have an own callable `asGuard` property, and `asGuard()` must return a function; invalid adapters throw `TypeError` during normalization.
|
|
44
|
+
|
|
45
|
+
Class adapters must define `asGuard` as an own arrow field, such as `readonly asGuard = () => guard`. A prototype method is not a runtime adapter; use an own arrow field or a plain `{ asGuard: () => guard }` wrapper.
|
|
33
46
|
|
|
34
47
|
### Composition
|
|
35
48
|
|
|
@@ -44,6 +57,8 @@
|
|
|
44
57
|
|
|
45
58
|
- **`liftPredicate(predicate)`** — Builds a guard from a predicate. With a refinement `(a: A) => a is B`, output is narrowed to `B`; otherwise `Guard<A, A>`.
|
|
46
59
|
|
|
60
|
+
The predicate is deferred until the returned Effect runs. If it throws, the exception is an Effect defect rather than a typed error. Use an effectful `Guard` when failure belongs in the `E` channel.
|
|
61
|
+
|
|
47
62
|
```ts
|
|
48
63
|
liftPredicate<A, B extends A>(predicate: (a: A) => a is B): Guard<A, B>;
|
|
49
64
|
liftPredicate<A>(predicate: (a: A) => boolean): Guard<A, A>;
|
|
@@ -51,6 +66,8 @@ liftPredicate<A>(predicate: (a: A) => boolean): Guard<A, A>;
|
|
|
51
66
|
|
|
52
67
|
- **`any(guards)`** — Takes an object of named guards and returns a guard whose input is the intersection of all guard inputs and whose output is the tagged union `{ _tag: key; value: output }`. Tries each guard in order and returns the first match.
|
|
53
68
|
|
|
69
|
+
`any` snapshots own enumerable string and symbol keys when it is called. Inherited and non-enumerable properties are ignored. ECMAScript own-key order applies: integer-index strings in ascending order, other strings in insertion order, then symbols in insertion order. Candidate Effects run sequentially and evaluation stops after the first `Some`.
|
|
70
|
+
|
|
54
71
|
### Schema
|
|
55
72
|
|
|
56
73
|
- **`fromSchemaDecode(schema)`** — Builds a guard from an Effect Schema: input is the schema’s encoded type, output is the schema’s type. Uses `Schema.decodeEffect`.
|
|
@@ -60,7 +77,7 @@ liftPredicate<A>(predicate: (a: A) => boolean): Guard<A, A>;
|
|
|
60
77
|
|
|
61
78
|
### Effect integration
|
|
62
79
|
|
|
63
|
-
- **`provide(guard, provided)`** — Provides a `
|
|
80
|
+
- **`provide(guard, provided)`** — Provides a `Context` or `Layer` to the guard’s environment.
|
|
64
81
|
- **`provideService(guard, tag, service)`** — Provides a single service to the guard’s environment.
|
|
65
82
|
- **`provideServiceEffect(guard, tag, effect)`** — Provides a service via an Effect to the guard’s environment.
|
|
66
83
|
- **`catchAll(guard, f)`** — Recovers from any error by running `f` and treating its result as a successful match. Alias: **`catch`**.
|
|
@@ -69,34 +86,37 @@ liftPredicate<A>(predicate: (a: A) => boolean): Guard<A, A>;
|
|
|
69
86
|
|
|
70
87
|
### Struct helpers
|
|
71
88
|
|
|
72
|
-
- **`addTag(guard, value)`** — Adds a readonly `_tag` property to
|
|
73
|
-
- **`bindTo(guard, key)`** — Wraps
|
|
74
|
-
- **`bind(guard, key, f)`** — Runs a second guard on the first’s output and
|
|
75
|
-
- **`let(guard, key, value)`** — Adds a fixed property to
|
|
89
|
+
- **`addTag(guard, value)`** — Adds a readonly `_tag` property to an object output that does not already have one. Dual.
|
|
90
|
+
- **`bindTo(guard, key)`** — Wraps any guard output in an object under the given key: `{ [key]: O }`. Dual. Use this to enter the record-building workflow from a primitive, array, or class instance.
|
|
91
|
+
- **`bind(guard, key, f)`** — Runs a second guard on the first’s object output and adds the result under a new `key`. Dual.
|
|
92
|
+
- **`let(guard, key, value)`** — Adds a fixed property under a new key to an object output. Dual.
|
|
93
|
+
|
|
94
|
+
`let`, `addTag`, and `bind` require object outputs and reject statically known key collisions. They use object spread and produce a new plain object. They copy own enumerable string and symbol properties; they do not preserve prototypes, inherited properties, or non-enumerable properties. Enumerable getters and proxy traps may run during the copy. Use `bindTo` to enter this record-building workflow from a primitive output.
|
|
76
95
|
|
|
77
96
|
## Example
|
|
78
97
|
|
|
98
|
+
The [basic example](./examples/basic.ts) is runnable and checked by `test:types`.
|
|
99
|
+
|
|
79
100
|
```ts
|
|
80
101
|
import { Effect, Option } from "effect";
|
|
81
102
|
import * as Guard from "@typed/guard";
|
|
82
103
|
import * as Schema from "effect/Schema";
|
|
83
104
|
|
|
84
|
-
const Positive = Schema.
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// result === "ok: 42"
|
|
105
|
+
const Positive = Schema.Finite.check(Schema.isGreaterThan(0));
|
|
106
|
+
const positive = Guard.fromSchemaDecode(Positive);
|
|
107
|
+
|
|
108
|
+
const program = positive(42).pipe(
|
|
109
|
+
Effect.map(
|
|
110
|
+
Option.match({
|
|
111
|
+
onNone: () => "not a positive number",
|
|
112
|
+
onSome: (n) => `ok: ${n}`,
|
|
113
|
+
}),
|
|
114
|
+
),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const result = await Effect.runPromise(program);
|
|
118
|
+
console.log(result); // "ok: 42"
|
|
99
119
|
|
|
100
120
|
const even = Guard.liftPredicate((n: number) => n % 2 === 0);
|
|
101
|
-
const positiveEven = Guard.pipe(
|
|
121
|
+
const positiveEven = Guard.pipe(positive, even);
|
|
102
122
|
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Guard, GuardInput } from "./index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns a callable Guard unchanged or obtains one from an own callable
|
|
4
|
+
* `asGuard` property. Invalid adapter objects throw `TypeError` immediately.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* ## Why
|
|
8
|
+
* Central normalization makes invalid adapter shapes fail at construction instead of later during Effect execution.
|
|
9
|
+
*
|
|
10
|
+
* ## Ownership and lifetime
|
|
11
|
+
* Normalization acquires no resources and returns the existing Guard function or the adapter's result.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { getGuard } from "@typed/guard/getGuard"
|
|
16
|
+
* import { liftPredicate } from "@typed/guard"
|
|
17
|
+
* const guard = getGuard(liftPredicate((value: unknown): value is string => typeof value === "string"))
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
* @category Library adapters
|
|
22
|
+
*/
|
|
23
|
+
export declare const getGuard: <I, O, E = never, R = never>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
|
|
24
|
+
//# sourceMappingURL=getGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getGuard.d.ts","sourceRoot":"","sources":["../src/getGuard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,SAC1C,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC5B,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAsBlB,CAAC"}
|
package/dist/getGuard.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a callable Guard unchanged or obtains one from an own callable
|
|
3
|
+
* `asGuard` property. Invalid adapter objects throw `TypeError` immediately.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* ## Why
|
|
7
|
+
* Central normalization makes invalid adapter shapes fail at construction instead of later during Effect execution.
|
|
8
|
+
*
|
|
9
|
+
* ## Ownership and lifetime
|
|
10
|
+
* Normalization acquires no resources and returns the existing Guard function or the adapter's result.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { getGuard } from "@typed/guard/getGuard"
|
|
15
|
+
* import { liftPredicate } from "@typed/guard"
|
|
16
|
+
* const guard = getGuard(liftPredicate((value: unknown): value is string => typeof value === "string"))
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @since 1.0.0
|
|
20
|
+
* @category Library adapters
|
|
21
|
+
*/
|
|
22
|
+
export const getGuard = (guard) => {
|
|
23
|
+
if (typeof guard === "function")
|
|
24
|
+
return guard;
|
|
25
|
+
if (typeof guard !== "object" || guard === null || !Object.hasOwn(guard, "asGuard")) {
|
|
26
|
+
throw new TypeError("Expected a Guard function or an object with an own callable asGuard property");
|
|
27
|
+
}
|
|
28
|
+
const asGuard = guard.asGuard;
|
|
29
|
+
if (typeof asGuard !== "function") {
|
|
30
|
+
throw new TypeError("Expected a Guard function or an object with an own callable asGuard property");
|
|
31
|
+
}
|
|
32
|
+
const normalized = asGuard.call(guard);
|
|
33
|
+
if (typeof normalized !== "function") {
|
|
34
|
+
throw new TypeError("Expected asGuard() to return a Guard function");
|
|
35
|
+
}
|
|
36
|
+
return normalized;
|
|
37
|
+
};
|