@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/dist/index.d.ts CHANGED
@@ -1,14 +1,40 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
+ import type * as Arr from "effect/Array";
4
5
  import type * as Cause from "effect/Cause";
5
6
  import * as Effect from "effect/Effect";
6
7
  import type * as Layer from "effect/Layer";
7
8
  import * as Option from "effect/Option";
8
9
  import type * as Predicate from "effect/Predicate";
9
10
  import * as Schema from "effect/Schema";
10
- import type * as ServiceMap from "effect/ServiceMap";
11
+ import type * as Context from "effect/Context";
12
+ import type { ExcludeTag, ExtractTag, NoInfer, Tags } from "effect/Types";
13
+ export { getGuard } from "./getGuard.js";
11
14
  /**
15
+ * An effectful partial transformation.
16
+ *
17
+ * A successful `Some` contains a match, a successful `None` means the input did
18
+ * not match, and an Effect failure remains in the `E` channel. Required
19
+ * services remain in `R`.
20
+ *
21
+ * @remarks
22
+ * ## Why
23
+ * `Guard` models non-match as successful `None`, keeping ordinary dispatch separate from typed failure, defects, and interruption in Effect's Cause.
24
+ *
25
+ * ## Ownership and lifetime
26
+ * A Guard acquires no resources by itself; each invocation has the lifetime and service requirements of the returned Effect.
27
+ *
28
+ * See [Effect Option](https://effect.website/docs/data-types/option/) and [Effect error management](https://effect.website/docs/v4/error-management/expected-errors/).
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import type { Guard } from "@typed/guard"
33
+ * import { Effect, Option } from "effect"
34
+ * const string: Guard<unknown, string> = (input) => Effect.succeed(typeof input === "string" ? Option.some(input) : Option.none())
35
+ * ```
36
+ *
37
+ * @category Guard contracts
12
38
  * @since 1.0.0
13
39
  */
14
40
  export type Guard<in I, out O, out E = never, out R = never> = (input: I) => Effect.Effect<Option.Option<O>, E, R>;
@@ -17,37 +43,144 @@ export type Guard<in I, out O, out E = never, out R = never> = (input: I) => Eff
17
43
  */
18
44
  export declare namespace Guard {
19
45
  /**
46
+ * Extracts the accepted input type from a Guard or Guard adapter.
47
+ * @remarks
48
+ * ## Why
49
+ * The extractor keeps generic APIs aligned with the exact input contract without repeating inference logic.
50
+ * ## Ownership and lifetime
51
+ * This compile-time type acquires no resources and has no runtime lifetime.
52
+ * @example
53
+ * ```ts
54
+ * import type { Guard } from "@typed/guard"
55
+ * type Input = Guard.Input<Guard<string, number>>
56
+ * ```
57
+ * @category Type utilities
20
58
  * @since 1.0.0
21
59
  */
22
60
  type Input<T> = [T] extends [Guard<infer I, infer _R, infer _E, infer _O>] ? I : [T] extends [AsGuard<infer I, infer _R, infer _E, infer _O>] ? I : never;
23
61
  /**
62
+ * Extracts the Effect service requirements from a Guard or Guard adapter.
63
+ * @remarks
64
+ * ## Why
65
+ * Service extraction makes environment composition visible in combinator signatures.
66
+ * ## Ownership and lifetime
67
+ * This compile-time type acquires no resources; service lifetime is governed by the resulting Effect or Layer.
68
+ * @example
69
+ * ```ts
70
+ * import type { Guard } from "@typed/guard"
71
+ * type Services = Guard.Services<Guard<string, number, never, { readonly Db: unique symbol }>>
72
+ * ```
73
+ * @category Type utilities
24
74
  * @since 1.0.0
25
75
  */
26
76
  type Services<T> = [T] extends [Guard<infer _I, infer _O, infer _E, infer R>] ? R : [T] extends [AsGuard<infer _I, infer _O, infer _E, infer R>] ? R : never;
27
77
  /**
78
+ * Extracts the typed error channel from a Guard or Guard adapter.
79
+ * @remarks
80
+ * ## Why
81
+ * Typed failure remains distinct from `None`, defects, and interruption throughout composition.
82
+ * ## Ownership and lifetime
83
+ * This compile-time type acquires no resources and has no runtime lifetime.
84
+ * @example
85
+ * ```ts
86
+ * import type { Guard } from "@typed/guard"
87
+ * type Error = Guard.Error<Guard<string, number, "Invalid">>
88
+ * ```
89
+ * @category Type utilities
28
90
  * @since 1.0.0
29
91
  */
30
92
  type Error<T> = [T] extends [Guard<infer _I, infer _O, infer E, infer _R>] ? E : [T] extends [AsGuard<infer _I, infer _O, infer E, infer _R>] ? E : never;
31
93
  /**
94
+ * Extracts the matched output type from a Guard or Guard adapter.
95
+ * @remarks
96
+ * ## Why
97
+ * The extractor lets record and dispatch combinators derive their output without duplicating conditional types.
98
+ * ## Ownership and lifetime
99
+ * This compile-time type acquires no resources and has no runtime lifetime.
100
+ * @example
101
+ * ```ts
102
+ * import type { Guard } from "@typed/guard"
103
+ * type Output = Guard.Output<Guard<string, number>>
104
+ * ```
105
+ * @category Type utilities
32
106
  * @since 1.0.0
33
107
  */
34
108
  type Output<T> = [T] extends [Guard<infer _I, infer O, infer _E, infer _R>] ? O : [T] extends [AsGuard<infer _I, infer O, infer _E, infer _R>] ? O : never;
35
109
  }
36
110
  /**
111
+ * An object that supplies a Guard through an own callable `asGuard` property.
112
+ * Use an instance field rather than a prototype method.
113
+ *
114
+ * @remarks
115
+ * ## Why
116
+ * An explicit adapter protocol lets domain objects participate in Guard composition without inheritance or wrapper allocation.
117
+ *
118
+ * ## Ownership and lifetime
119
+ * The adapter acquires no resources; the returned Guard owns no lifetime beyond each returned Effect.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * import type { AsGuard, Guard } from "@typed/guard"
124
+ * import { Effect } from "effect"
125
+ * const adapter: AsGuard<string, string> = { asGuard: () => ((input) => Effect.succeedSome(input)) as Guard<string, string> }
126
+ * ```
127
+ *
128
+ * @category Guard contracts
37
129
  * @since 1.0.0
38
130
  */
39
131
  export interface AsGuard<in I, out O, out E = never, out R = never> {
132
+ /**
133
+ * Returns the Guard represented by this adapter.
134
+ * @remarks
135
+ * ## Why
136
+ * Requiring an own callable property avoids ambiguous prototype behavior when adapters cross object boundaries.
137
+ * ## Ownership and lifetime
138
+ * Calling this property acquires no resources; the returned Guard follows its own Effect lifetime.
139
+ * @category Guard contracts
140
+ * @since 1.0.0
141
+ */
40
142
  readonly asGuard: () => Guard<I, O, E, R>;
41
143
  }
42
144
  /**
145
+ * A Guard or an object that supplies one. Guard combinators accept either form.
146
+ *
147
+ * @remarks
148
+ * ## Why
149
+ * A single input contract lets every combinator accept direct functions and domain adapters consistently.
150
+ *
151
+ * ## Ownership and lifetime
152
+ * This union acquires no resources; normalization does not extend the lifetime of either alternative.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * import type { GuardInput } from "@typed/guard"
157
+ * import { liftPredicate } from "@typed/guard"
158
+ * const input: GuardInput<unknown, string> = liftPredicate((value: unknown): value is string => typeof value === "string")
159
+ * ```
160
+ *
161
+ * @category Guard contracts
43
162
  * @since 1.0.0
44
163
  */
45
164
  export type GuardInput<I, O, E = never, R = never> = Guard<I, O, E, R> | AsGuard<I, O, E, R>;
165
+ type RecordOutputConstraint<O> = O extends object ? O extends ReadonlyArray<unknown> ? never : unknown : never;
46
166
  /**
47
- * @since 1.0.0
48
- */
49
- export declare const getGuard: <I, O, E = never, R = never>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
50
- /**
167
+ * Runs `output` only when `input` matches. `None` short-circuits successfully,
168
+ * while failures and service requirements are preserved from both Guards.
169
+ *
170
+ * @remarks
171
+ * ## Why
172
+ * Sequential Guard composition must preserve ordinary non-match while unioning the typed error and service channels of both stages.
173
+ *
174
+ * ## Ownership and lifetime
175
+ * Construction acquires no resources; each invocation runs the first Effect and only starts the second after `Some`.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * import { liftPredicate, pipe } from "@typed/guard"
180
+ * const nonEmpty = pipe(liftPredicate((u: unknown): u is string => typeof u === "string"), liftPredicate((s) => s.length > 0))
181
+ * ```
182
+ *
183
+ * @category Sequential composition
51
184
  * @since 1.0.0
52
185
  */
53
186
  export declare const pipe: {
@@ -55,6 +188,19 @@ export declare const pipe: {
55
188
  <I, O, E, R, B, E2, R2>(input: GuardInput<I, O, E, R>, output: GuardInput<O, B, E2, R2>): Guard<I, B, E | E2, R | R2>;
56
189
  };
57
190
  /**
191
+ * Maps matched output with an Effect while preserving `None`.
192
+ * @remarks
193
+ * ## Why
194
+ * Effectful mapping can add typed errors and services without changing non-match into failure.
195
+ * ## Ownership and lifetime
196
+ * Construction acquires no resources; the mapping Effect starts only after the source Guard produces `Some`.
197
+ * @example
198
+ * ```ts
199
+ * import { liftPredicate, mapEffect } from "@typed/guard"
200
+ * import { Effect } from "effect"
201
+ * const length = mapEffect(liftPredicate((u: unknown): u is string => typeof u === "string"), (s) => Effect.succeed(s.length))
202
+ * ```
203
+ * @category Value transformations
58
204
  * @since 1.0.0
59
205
  */
60
206
  export declare const mapEffect: {
@@ -62,6 +208,18 @@ export declare const mapEffect: {
62
208
  <I, O, E, R, B, E2, R2>(guard: GuardInput<I, O, E, R>, f: (o: O) => Effect.Effect<B, E2, R2>): Guard<I, B, E | E2, R | R2>;
63
209
  };
64
210
  /**
211
+ * Maps matched output synchronously while preserving non-match and Effect channels.
212
+ * @remarks
213
+ * ## Why
214
+ * Pure output adaptation should not add errors or services and should never run for `None`.
215
+ * ## Ownership and lifetime
216
+ * Construction acquires no resources; the callback runs once for each `Some` during Effect execution.
217
+ * @example
218
+ * ```ts
219
+ * import { liftPredicate, map } from "@typed/guard"
220
+ * const length = map(liftPredicate((u: unknown): u is string => typeof u === "string"), (s) => s.length)
221
+ * ```
222
+ * @category Value transformations
65
223
  * @since 1.0.0
66
224
  */
67
225
  export declare const map: {
@@ -69,13 +227,40 @@ export declare const map: {
69
227
  <I, O, E, R, B>(guard: GuardInput<I, O, E, R>, f: (o: O) => B): Guard<I, B, E, R>;
70
228
  };
71
229
  /**
230
+ * Runs a synchronous or Effectful observation for each matched value and returns the value unchanged.
231
+ * @remarks
232
+ * ## Why
233
+ * Observation composes without changing output, while Effect callbacks correctly contribute their errors and services.
234
+ * ## Ownership and lifetime
235
+ * Construction acquires no resources; callback lifetime is bounded by each matching Guard invocation.
236
+ * @example
237
+ * ```ts
238
+ * import { liftPredicate, tap } from "@typed/guard"
239
+ * const observed = tap(liftPredicate((u: unknown): u is string => typeof u === "string"), console.log)
240
+ * ```
241
+ * @category Effectful observation
72
242
  * @since 1.0.0
73
243
  */
74
244
  export declare const tap: {
75
- <O, B, E2 = never, R2 = never>(f: (o: O) => void | Effect.Effect<B, E2, R2>): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E | E2, R | R2>;
76
- <I, O, E, R, B, E2, R2>(guard: GuardInput<I, O, E, R>, f: (o: O) => void | Effect.Effect<B, E2, R2>): Guard<I, O, E | E2, R | R2>;
245
+ <O>(f: (o: O) => void): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
246
+ <O, B, E2, R2>(f: (o: O) => Effect.Effect<B, E2, R2>): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E | E2, R | R2>;
247
+ <I, O, E, R>(guard: GuardInput<I, O, E, R>, f: (o: O) => void): Guard<I, O, E, R>;
248
+ <I, O, E, R, B, E2, R2>(guard: GuardInput<I, O, E, R>, f: (o: O) => Effect.Effect<B, E2, R2>): Guard<I, O, E | E2, R | R2>;
77
249
  };
78
250
  /**
251
+ * Refines and maps a matched value with an Option-returning function.
252
+ * @remarks
253
+ * ## Why
254
+ * `Option.none` provides a second ordinary non-match stage without introducing typed failure.
255
+ * ## Ownership and lifetime
256
+ * This combinator acquires no resources; the callback runs only for source matches.
257
+ * @example
258
+ * ```ts
259
+ * import { filterMap, liftPredicate } from "@typed/guard"
260
+ * import { Option } from "effect"
261
+ * const parsed = filterMap(liftPredicate((u: unknown): u is string => typeof u === "string"), (s) => s ? Option.some(Number(s)) : Option.none())
262
+ * ```
263
+ * @category Input selection
79
264
  * @since 1.0.0
80
265
  */
81
266
  export declare const filterMap: {
@@ -83,24 +268,79 @@ export declare const filterMap: {
83
268
  <I, O, E, R, B>(guard: GuardInput<I, O, E, R>, f: (o: O) => Option.Option<B>): Guard<I, B, E, R>;
84
269
  };
85
270
  /**
271
+ * Keeps matched values that satisfy a predicate or refinement.
272
+ * @remarks
273
+ * ## Why
274
+ * Predicate failure remains successful `None`, preserving Guard dispatch semantics and type refinement.
275
+ * ## Ownership and lifetime
276
+ * This combinator acquires no resources; the predicate runs only for source matches.
277
+ * @example
278
+ * ```ts
279
+ * import { filter, liftPredicate } from "@typed/guard"
280
+ * const positive = filter(liftPredicate((u: unknown): u is number => typeof u === "number"), (n) => n > 0)
281
+ * ```
282
+ * @category Input selection
86
283
  * @since 1.0.0
87
284
  */
88
285
  export declare const filter: {
89
- <O, O2 extends O>(predicate: (o: O) => o is O2): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
286
+ <O, O2 extends O>(predicate: (o: O) => o is O2): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O2, E, R>;
90
287
  <O>(predicate: (o: O) => boolean): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
91
- <I, O, E, R, O2 extends O>(guard: GuardInput<I, O, E, R>, predicate: (o: O) => o is O2): Guard<I, O, E, R>;
288
+ <I, O, E, R, O2 extends O>(guard: GuardInput<I, O, E, R>, predicate: (o: O) => o is O2): Guard<I, O2, E, R>;
92
289
  <I, O, E, R>(guard: GuardInput<I, O, E, R>, predicate: (o: O) => boolean): Guard<I, O, E, R>;
93
290
  };
94
291
  /**
292
+ * Runs candidates sequentially and returns the first match tagged with its key.
293
+ * Candidates are snapshotted from own enumerable keys when `any` is called.
294
+ * ECMAScript own-key order applies: integer-index strings, other strings, then
295
+ * symbols.
296
+ *
297
+ * @remarks
298
+ * ## Why
299
+ * Ordered first-match dispatch turns independently composable Guards into a deterministic tagged union without treating `None` as failure.
300
+ *
301
+ * ## Ownership and lifetime
302
+ * Construction snapshots own enumerable entries and acquires no resources; each run executes candidates sequentially until the first `Some`.
303
+ *
304
+ * @example
305
+ * ```ts
306
+ * import { any, liftPredicate } from "@typed/guard"
307
+ * const classify = any({ text: liftPredicate((u: unknown): u is string => typeof u === "string") })
308
+ * ```
309
+ *
310
+ * @category Alternative selection
95
311
  * @since 1.0.0
96
312
  */
97
313
  export declare function any<const GS extends Readonly<Record<string, GuardInput<any, any, any, any>>>>(guards: GS): Guard<AnyInput<GS>, AnyOutput<GS>, Guard.Error<GS[keyof GS]>, Guard.Services<GS[keyof GS]>>;
98
314
  /**
315
+ * Computes the intersection of inputs accepted by an `any` Guard record.
316
+ * @remarks
317
+ * ## Why
318
+ * Every candidate receives the same runtime value, so the input must satisfy all candidate input contracts.
319
+ * ## Ownership and lifetime
320
+ * This compile-time type acquires no resources and has no runtime lifetime.
321
+ * @example
322
+ * ```ts
323
+ * import type { AnyInput, Guard } from "@typed/guard"
324
+ * type Input = AnyInput<{ text: Guard<unknown, string>; count: Guard<unknown, number> }>
325
+ * ```
326
+ * @category Type utilities
99
327
  * @since 1.0.0
100
328
  */
101
329
  export type AnyInput<GS extends Readonly<Record<string, GuardInput<any, any, any, any>>>> = UnionToIntersection<Guard.Input<GS[keyof GS]>>;
102
330
  type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
103
331
  /**
332
+ * Builds the tagged output union produced by `any`.
333
+ * @remarks
334
+ * ## Why
335
+ * Preserving each record key in `_tag` lets downstream code narrow the winning Guard's value exhaustively.
336
+ * ## Ownership and lifetime
337
+ * This compile-time type acquires no resources and has no runtime lifetime.
338
+ * @example
339
+ * ```ts
340
+ * import type { AnyOutput, Guard } from "@typed/guard"
341
+ * type Output = AnyOutput<{ text: Guard<unknown, string>; count: Guard<unknown, number> }>
342
+ * ```
343
+ * @category Type utilities
104
344
  * @since 1.0.0
105
345
  */
106
346
  export type AnyOutput<GS extends Readonly<Record<string, GuardInput<any, any, any, any>>>> = [
@@ -112,11 +352,44 @@ export type AnyOutput<GS extends Readonly<Record<string, GuardInput<any, any, an
112
352
  }[keyof GS]
113
353
  ] extends [infer R] ? R : never;
114
354
  /**
355
+ * Builds a Guard from a predicate or refinement. The predicate is evaluated
356
+ * only when the returned Effect runs. A thrown exception becomes an Effect
357
+ * defect; use an effectful Guard when failure belongs in the typed error channel.
358
+ *
359
+ * @remarks
360
+ * ## Why
361
+ * Predicate lifting is the bridge from synchronous refinements into deferred Guard composition while preserving the `None` versus failure distinction.
362
+ *
363
+ * ## Ownership and lifetime
364
+ * Construction acquires no resources; the predicate runs once per invocation when the returned Effect executes.
365
+ *
366
+ * @example
367
+ * ```ts
368
+ * import { liftPredicate } from "@typed/guard"
369
+ * const string = liftPredicate((value: unknown): value is string => typeof value === "string")
370
+ * ```
371
+ *
372
+ * @category Input selection
115
373
  * @since 1.0.0
116
374
  */
117
375
  export declare function liftPredicate<A, B extends A>(predicate: Predicate.Refinement<A, B>): Guard<A, B>;
118
376
  export declare function liftPredicate<A>(predicate: Predicate.Predicate<A>): Guard<A, A>;
119
377
  /**
378
+ * Recovers from the complete Effect Cause and lifts the recovery result into `Some`.
379
+ * @remarks
380
+ * ## Why
381
+ * Cause-aware recovery can deliberately handle typed failures, defects, and interruption; `None` remains an unrecovered non-match.
382
+ * ## Ownership and lifetime
383
+ * Construction acquires no resources; recovery starts only when the source Effect fails and follows that invocation's lifetime.
384
+ * @example
385
+ * ```ts
386
+ * import { catchCause } from "@typed/guard"
387
+ * import type { Guard } from "@typed/guard"
388
+ * import { Effect } from "effect"
389
+ * const source: Guard<string, string, string> = (input) => input ? Effect.succeedSome(input) : Effect.fail("empty")
390
+ * const recovered = catchCause(source, () => Effect.succeed("fallback"))
391
+ * ```
392
+ * @category Error recovery
120
393
  * @since 1.0.0
121
394
  */
122
395
  export declare const catchCause: {
@@ -124,6 +397,21 @@ export declare const catchCause: {
124
397
  <I, O, E, R, O2, E2, R2>(guard: GuardInput<I, O, E, R>, f: (e: Cause.Cause<E>) => Effect.Effect<O2, E2, R2>): Guard<I, O | O2, E2, R | R2>;
125
398
  };
126
399
  /**
400
+ * Recovers typed failures and lifts the recovery result into `Some`.
401
+ * @remarks
402
+ * ## Why
403
+ * Typed recovery leaves defects and interruption untouched and does not confuse successful `None` with failure. The exported `catch` name is an alias of this declaration.
404
+ * ## Ownership and lifetime
405
+ * Construction acquires no resources; recovery starts only for typed failure during the source invocation.
406
+ * @example
407
+ * ```ts
408
+ * import { catchAll } from "@typed/guard"
409
+ * import type { Guard } from "@typed/guard"
410
+ * import { Effect } from "effect"
411
+ * const source: Guard<string, string, string> = (input) => input ? Effect.succeedSome(input) : Effect.fail("empty")
412
+ * const recovered = catchAll(source, (error) => Effect.succeed(error.length))
413
+ * ```
414
+ * @category Error recovery
127
415
  * @since 1.0.0
128
416
  */
129
417
  export declare const catchAll: {
@@ -132,97 +420,254 @@ export declare const catchAll: {
132
420
  };
133
421
  export { catchAll as catch };
134
422
  /**
423
+ * Recovers selected tagged typed failures and leaves unmatched tags in the error channel.
424
+ * @remarks
425
+ * ## Why
426
+ * Tag-specific recovery preserves type-safe residual errors while lifting recovered output into `Some`; `None`, defects, and interruption are unchanged.
427
+ * ## Ownership and lifetime
428
+ * Construction acquires no resources; the handler runs only for matching tagged failures during an invocation.
429
+ * @example
430
+ * ```ts
431
+ * import { catchTag } from "@typed/guard"
432
+ * import type { Guard } from "@typed/guard"
433
+ * import { Effect } from "effect"
434
+ * type NotFound = { readonly _tag: "NotFound" }
435
+ * const source: Guard<string, string, NotFound> = (input) => input ? Effect.succeedSome(input) : Effect.fail({ _tag: "NotFound" })
436
+ * const recovered = catchTag(source, "NotFound", () => Effect.succeed("fallback"))
437
+ * ```
438
+ * @category Error recovery
135
439
  * @since 1.0.0
136
440
  */
137
441
  export declare const catchTag: {
138
- <E, K extends E extends {
139
- _tag: string;
140
- } ? E["_tag"] : never, O2, E2, R2>(tag: K, f: (e: Extract<E, {
141
- _tag: K;
142
- }>) => Effect.Effect<O2, E2, R2>): <I, O, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O | O2, E2 | Exclude<E, {
143
- _tag: K;
144
- }>, R | R2>;
145
- <I, O, E, R, K extends E extends {
146
- _tag: string;
147
- } ? E["_tag"] : never, O2, E2, R2>(guard: GuardInput<I, O, E, R>, tag: K, f: (e: Extract<E, {
148
- _tag: K;
149
- }>) => Effect.Effect<O2, E2, R2>): Guard<I, O | O2, E2 | Exclude<E, {
150
- _tag: K;
151
- }>, R | R2>;
442
+ <const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>, E, O2, E2, R2>(tag: K, f: (e: ExtractTag<NoInfer<E>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Effect.Effect<O2, E2, R2>): <I, O, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O | O2, E2 | ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>, R | R2>;
443
+ <I, O, E, R, const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>, O2, E2, R2>(guard: GuardInput<I, O, E, R>, tag: K, f: (e: ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Effect.Effect<O2, E2, R2>): Guard<I, O | O2, E2 | ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>, R | R2>;
152
444
  };
153
445
  /**
446
+ * Provides a Context or Layer to a Guard's Effect.
447
+ * @remarks
448
+ * ## Why
449
+ * Provision removes supplied services from `R`; Layers may also contribute acquisition errors and their own requirements exactly as Effect does.
450
+ * ## Ownership and lifetime
451
+ * A Context acquires no resources here. Layer resources are acquired and released according to the Effect Scope that runs the Guard.
452
+ * @example
453
+ * ```ts
454
+ * import { provide } from "@typed/guard"
455
+ * import type { Guard } from "@typed/guard"
456
+ * import { Context, Effect, Option } from "effect"
457
+ * const Flag = Context.Service<{ readonly enabled: boolean }>("Flag")
458
+ * const requiresFlag: Guard<boolean, boolean, never, Context.Service.Identifier<typeof Flag>> = (input) => Effect.map(Effect.service(Flag), ({ enabled }) => enabled && input ? Option.some(input) : Option.none())
459
+ * const guard = provide(requiresFlag, Context.make(Flag, { enabled: true }))
460
+ * ```
461
+ * See [Effect services and Layers](https://effect.website/docs/requirements-management/layers/).
462
+ * @category Services
154
463
  * @since 1.0.0
155
464
  */
156
465
  export declare const provide: {
157
- <R2>(provided: ServiceMap.ServiceMap<R2>): <I, O, E, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, Exclude<R, R2>>;
466
+ <R2>(provided: Context.Context<R2>): <I, O, E, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, Exclude<R, R2>>;
158
467
  <R2, E2, R3>(provided: Layer.Layer<R2, E2, R3>): <I, O, E, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E | E2, Exclude<R, R2> | R3>;
159
- <I, O, E, R, R2>(guard: GuardInput<I, O, E, R>, provided: ServiceMap.ServiceMap<R2>): Guard<I, O, E, Exclude<R, R2>>;
468
+ <I, O, E, R, R2>(guard: GuardInput<I, O, E, R>, provided: Context.Context<R2>): Guard<I, O, E, Exclude<R, R2>>;
160
469
  <I, O, E, R, R2, E2, R3>(guard: GuardInput<I, O, E, R>, provided: Layer.Layer<R2, E2, R3>): Guard<I, O, E | E2, Exclude<R, R2> | R3>;
161
470
  };
162
471
  /**
472
+ * Provides one concrete service to a Guard.
473
+ * @remarks
474
+ * ## Why
475
+ * Targeted service provision removes only the selected identifier from the Guard environment.
476
+ * ## Ownership and lifetime
477
+ * This combinator acquires no resources and reuses the supplied service for each invocation.
478
+ * @example
479
+ * ```ts
480
+ * import { provideService } from "@typed/guard"
481
+ * import type { Guard } from "@typed/guard"
482
+ * import { Context, Effect, Option } from "effect"
483
+ * const Flag = Context.Service<{ readonly enabled: boolean }>("Flag")
484
+ * const requiresFlag: Guard<boolean, boolean, never, Context.Service.Identifier<typeof Flag>> = (input) => Effect.map(Effect.service(Flag), ({ enabled }) => enabled && input ? Option.some(input) : Option.none())
485
+ * const guard = provideService(requiresFlag, Flag, { enabled: true })
486
+ * ```
487
+ * @category Services
163
488
  * @since 1.0.0
164
489
  */
165
490
  export declare const provideService: {
166
- <Id, S>(tag: ServiceMap.Service<Id, S>, service: S): <I, O, E, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, Exclude<R, Id>>;
167
- <I, O, E, R, Id, S>(guard: GuardInput<I, O, E, R>, tag: ServiceMap.Service<Id, S>, service: S): Guard<I, O, E, Exclude<R, Id>>;
491
+ <Id, S>(tag: Context.Service<Id, S>, service: S): <I, O, E, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, Exclude<R, Id>>;
492
+ <I, O, E, R, Id, S>(guard: GuardInput<I, O, E, R>, tag: Context.Service<Id, S>, service: S): Guard<I, O, E, Exclude<R, Id>>;
168
493
  };
169
494
  /**
495
+ * Provides a service produced by an Effect to a Guard.
496
+ * @remarks
497
+ * ## Why
498
+ * Effectful provision makes acquisition errors and required services explicit in the composed Guard channels.
499
+ * ## Ownership and lifetime
500
+ * The service Effect runs for each Guard invocation and is interrupted with that invocation; scoped resources follow the surrounding Scope.
501
+ * @example
502
+ * ```ts
503
+ * import { provideServiceEffect } from "@typed/guard"
504
+ * import type { Guard } from "@typed/guard"
505
+ * import { Context, Effect, Option } from "effect"
506
+ * const Flag = Context.Service<{ readonly enabled: boolean }>("Flag")
507
+ * const requiresFlag: Guard<boolean, boolean, never, Context.Service.Identifier<typeof Flag>> = (input) => Effect.map(Effect.service(Flag), ({ enabled }) => enabled && input ? Option.some(input) : Option.none())
508
+ * const guard = provideServiceEffect(requiresFlag, Flag, Effect.succeed({ enabled: true }))
509
+ * ```
510
+ * @category Services
170
511
  * @since 1.0.0
171
512
  */
172
513
  export declare const provideServiceEffect: {
173
- <Id, S, E2, R2>(tag: ServiceMap.Service<Id, S>, service: Effect.Effect<S, E2, R2>): <I, O, E, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E | E2, Exclude<R, Id> | R2>;
174
- <I, O, E, R, Id, S, E2, R2>(guard: GuardInput<I, O, E, R>, tag: ServiceMap.Service<Id, S>, service: Effect.Effect<S, E2, R2>): Guard<I, O, E | E2, Exclude<R, Id> | R2>;
514
+ <Id, S, E2, R2>(tag: Context.Service<Id, S>, service: Effect.Effect<S, E2, R2>): <I, O, E, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E | E2, Exclude<R, Id> | R2>;
515
+ <I, O, E, R, Id, S, E2, R2>(guard: GuardInput<I, O, E, R>, tag: Context.Service<Id, S>, service: Effect.Effect<S, E2, R2>): Guard<I, O, E | E2, Exclude<R, Id> | R2>;
175
516
  };
176
517
  /**
518
+ * Creates a Guard that decodes a schema's Encoded input to its Type.
519
+ * @remarks
520
+ * ## Why
521
+ * Schema decoding reports all issues, ignores excess properties, retains `SchemaError` in the typed channel, and carries decoding services in `R`.
522
+ * ## Ownership and lifetime
523
+ * Construction acquires no resources; schema services and any scoped work follow each returned Effect invocation.
524
+ * @example
525
+ * ```ts
526
+ * import { fromSchemaDecode } from "@typed/guard"
527
+ * import { Schema } from "effect"
528
+ * const number = fromSchemaDecode(Schema.NumberFromString)
529
+ * ```
530
+ * See [Effect Schema transformations](https://effect.website/docs/schema/transformations/).
531
+ * @category Schema decoding and encoding
177
532
  * @since 1.0.0
178
533
  */
179
534
  export declare function fromSchemaDecode<S extends Schema.Top>(schema: S): Guard<S["Encoded"], S["Type"], Schema.SchemaError, S["DecodingServices"]>;
180
535
  /**
536
+ * Creates a Guard that encodes a schema's Type to its Encoded representation.
537
+ * @remarks
538
+ * ## Why
539
+ * Schema encoding is the reverse direction of decoding and keeps encoding services and `SchemaError` visible in the Guard type.
540
+ * ## Ownership and lifetime
541
+ * Construction acquires no resources; schema services and any scoped work follow each returned Effect invocation.
542
+ * @example
543
+ * ```ts
544
+ * import { fromSchemaEncode } from "@typed/guard"
545
+ * import { Schema } from "effect"
546
+ * const encoded = fromSchemaEncode(Schema.NumberFromString)
547
+ * ```
548
+ * @category Schema decoding and encoding
181
549
  * @since 1.0.0
182
550
  */
183
551
  export declare function fromSchemaEncode<S extends Schema.Top>(schema: S): Guard<S["Type"], S["Encoded"], Schema.SchemaError, S["EncodingServices"]>;
184
552
  /**
553
+ * Decodes each matched encoded value through an Effect Schema.
554
+ * @remarks
555
+ * ## Why
556
+ * The composed Guard preserves source non-match and errors while adding schema failures and decoding service requirements.
557
+ * ## Ownership and lifetime
558
+ * Construction acquires no resources; decoding starts only for `Some` and follows that invocation's lifetime.
559
+ * @example
560
+ * ```ts
561
+ * import { decode, liftPredicate } from "@typed/guard"
562
+ * import { Schema } from "effect"
563
+ * const number = decode(liftPredicate((u: unknown): u is string => typeof u === "string"), Schema.NumberFromString)
564
+ * ```
565
+ * @category Schema decoding and encoding
185
566
  * @since 1.0.0
186
567
  */
187
568
  export declare const decode: {
188
- <S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["Type"], E, R>) => Guard<I, S["Type"], Schema.SchemaError | E, R | S["DecodingServices"]>;
189
- <I, O, E, R, S extends Schema.Top>(guard: GuardInput<I, O, E, R>, schema: S): Guard<I, S["Type"], Schema.SchemaError | E, R | S["DecodingServices"]>;
569
+ <S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["Encoded"], E, R>) => Guard<I, S["Type"], Schema.SchemaError | E, R | S["DecodingServices"]>;
570
+ <I, E, R, S extends Schema.Top>(guard: GuardInput<I, S["Encoded"], E, R>, schema: S): Guard<I, S["Type"], Schema.SchemaError | E, R | S["DecodingServices"]>;
190
571
  };
191
572
  /**
573
+ * Encodes each matched schema Type to its Encoded representation.
574
+ * @remarks
575
+ * ## Why
576
+ * The composed Guard preserves source non-match and errors while adding schema failures and encoding service requirements.
577
+ * ## Ownership and lifetime
578
+ * Construction acquires no resources; encoding starts only for `Some` and follows that invocation's lifetime.
579
+ * @example
580
+ * ```ts
581
+ * import { encode, liftPredicate } from "@typed/guard"
582
+ * import { Schema } from "effect"
583
+ * const text = encode(liftPredicate((u: unknown): u is number => typeof u === "number"), Schema.NumberFromString)
584
+ * ```
585
+ * @category Schema decoding and encoding
192
586
  * @since 1.0.0
193
587
  */
194
588
  export declare const encode: {
195
- <S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["Type"], E, R>) => Guard<I, S["Type"], Schema.SchemaError | E, R | S["EncodingServices"]>;
196
- <I, O, E, R, S extends Schema.Top>(guard: GuardInput<I, O, E, R>, schema: S): Guard<I, S["Encoded"], Schema.SchemaError | E, R | S["EncodingServices"]>;
589
+ <S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["Type"], E, R>) => Guard<I, S["Encoded"], Schema.SchemaError | E, R | S["EncodingServices"]>;
590
+ <I, E, R, S extends Schema.Top>(guard: GuardInput<I, S["Type"], E, R>, schema: S): Guard<I, S["Encoded"], Schema.SchemaError | E, R | S["EncodingServices"]>;
197
591
  };
198
592
  /**
593
+ * Adds a fixed property to every matched record output.
594
+ * @remarks
595
+ * ## Why
596
+ * Record construction does not mutate the input and rejects existing enumerable keys; it returns an unfrozen plain object containing own enumerable string and symbol properties while dropping prototypes and non-enumerables.
597
+ * ## Ownership and lifetime
598
+ * This pure combinator acquires no resources and creates a fresh plain object for every match.
599
+ * @example
600
+ * ```ts
601
+ * import { bindTo, let as letGuard, liftPredicate } from "@typed/guard"
602
+ * const named = letGuard(bindTo(liftPredicate(Boolean), "value"), "kind", "input")
603
+ * ```
604
+ * @category Record construction
199
605
  * @since 1.0.0
200
606
  */
201
607
  declare const let_: {
202
- <K extends PropertyKey, B>(key: K, value: B): <I, O, E = never, R = never>(guard: Guard<I, O, E, R>) => Guard<I, O & {
608
+ <K extends PropertyKey, B>(key: K, value: B): <G extends GuardInput<any, any, any, any>>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & (K extends NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never> ? never : unknown)) => Guard<Guard.Input<G>, Guard.Output<G> & {
203
609
  [k in K]: B;
204
- }, E, R>;
205
- <I, O, E, R, K extends PropertyKey, B>(guard: Guard<I, O, E, R>, key: K, value: B): Guard<I, O & {
610
+ }, Guard.Error<G>, Guard.Services<G>>;
611
+ <G extends GuardInput<any, any, any, any>, K extends PropertyKey, B>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>>, key: Exclude<K, NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never>>, value: B): Guard<Guard.Input<G>, Guard.Output<G> & {
206
612
  [k in K]: B;
207
- }, E, R>;
613
+ }, Guard.Error<G>, Guard.Services<G>>;
208
614
  };
209
615
  export {
210
616
  /**
617
+ * Adds a fixed property to every matched object output. The key must not
618
+ * already exist; use `bindTo` first when the prior output is not an object.
619
+ *
620
+ * This alias shares the canonical documentation and behavior of `let_`.
621
+ *
211
622
  * @since 1.0.0
212
623
  */
213
624
  let_ as let, };
214
625
  /**
626
+ * Adds a readonly `_tag` to every matched object output. The output must not
627
+ * already have an `_tag` property.
628
+ *
629
+ * @remarks
630
+ * ## Why
631
+ * A typed discriminant turns matched record outputs into exhaustively narrowable tagged values while rejecting key collisions.
632
+ *
633
+ * ## Ownership and lifetime
634
+ * This pure combinator acquires no resources and creates a fresh plain object for every match.
635
+ *
636
+ * @example
637
+ * ```ts
638
+ * import { addTag, bindTo, liftPredicate } from "@typed/guard"
639
+ * const tagged = addTag(bindTo(liftPredicate(Boolean), "value"), "Input")
640
+ * ```
641
+ *
642
+ * @category Record construction
215
643
  * @since 1.0.0
216
644
  */
217
645
  export declare const addTag: {
218
- <B>(value: B): <I, O, E = never, R = never>(guard: GuardInput<I, O, E, R>) => Guard<I, O & {
646
+ <B>(value: B): <G extends GuardInput<any, any, any, any>>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & ("_tag" extends NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never> ? never : unknown)) => Guard<Guard.Input<G>, Guard.Output<G> & {
219
647
  readonly _tag: B;
220
- }, E, R>;
221
- <I, O, E, R, B>(guard: GuardInput<I, O, E, R>, value: B): Guard<I, O & {
648
+ }, Guard.Error<G>, Guard.Services<G>>;
649
+ <G extends GuardInput<any, any, any, any>, B>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & ("_tag" extends NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never> ? never : unknown), value: B): Guard<Guard.Input<G>, Guard.Output<G> & {
222
650
  readonly _tag: B;
223
- }, E, R>;
651
+ }, Guard.Error<G>, Guard.Services<G>>;
224
652
  };
225
653
  /**
654
+ * Wraps any matched output in a new object under `key`. This is the explicit
655
+ * transition from an arbitrary output to the record-building workflow.
656
+ *
657
+ * @remarks
658
+ * ## Why
659
+ * Explicit wrapping makes arbitrary values safe for record combinators without assuming their runtime shape.
660
+ *
661
+ * ## Ownership and lifetime
662
+ * This pure combinator acquires no resources and creates a fresh plain object for every match.
663
+ *
664
+ * @example
665
+ * ```ts
666
+ * import { bindTo, liftPredicate } from "@typed/guard"
667
+ * const named = bindTo(liftPredicate(Boolean), "value")
668
+ * ```
669
+ *
670
+ * @category Record construction
226
671
  * @since 1.0.0
227
672
  */
228
673
  export declare const bindTo: {
@@ -234,14 +679,34 @@ export declare const bindTo: {
234
679
  }, E, R>;
235
680
  };
236
681
  /**
682
+ * Runs `f` on a matched object and adds its matched value under a new key. The
683
+ * key must not already exist. Enumerable getters and proxy traps may execute
684
+ * during the object spread.
685
+ *
686
+ * @remarks
687
+ * ## Why
688
+ * Dependent record construction runs the second Guard only after the base matches, preserves `None`, and unions both error and service channels.
689
+ *
690
+ * ## Ownership and lifetime
691
+ * Construction acquires no resources; each successful bind creates a fresh plain object and the dependent Effect follows the invocation lifetime.
692
+ *
693
+ * @example
694
+ * ```ts
695
+ * import { bind, bindTo, liftPredicate, map } from "@typed/guard"
696
+ * const base = bindTo(liftPredicate((u: unknown): u is string => typeof u === "string"), "text")
697
+ * const nonEmptyLength = map(liftPredicate((record: { readonly text: string }) => record.text.length > 0), (record) => record.text.length)
698
+ * const sized = bind(base, "length", nonEmptyLength)
699
+ * ```
700
+ *
701
+ * @category Record construction
237
702
  * @since 1.0.0
238
703
  */
239
704
  export declare const bind: {
240
- <I, O, E, R, K extends PropertyKey, B, E2, R2>(key: K, f: GuardInput<O, B, E2, R2>): (guard: GuardInput<I, O, E, R>) => Guard<I, O & {
705
+ <O extends object, K extends PropertyKey, B, E2, R2>(key: K, f: GuardInput<O, B, E2, R2>): <G extends GuardInput<any, O, any, any>>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & (K extends NoInfer<Guard.Output<G> extends infer A ? (A extends unknown ? keyof A : never) : never> ? never : unknown)) => Guard<Guard.Input<G>, Guard.Output<G> & {
241
706
  [k in K]: B;
242
- }, E | E2, R | R2>;
243
- <I, O, E, R, K extends PropertyKey, B, E2, R2>(guard: GuardInput<I, O, E, R>, key: K, f: GuardInput<O, B, E2, R2>): Guard<I, O & {
707
+ }, Guard.Error<G> | E2, Guard.Services<G> | R2>;
708
+ <G extends GuardInput<any, any, any, any>, K extends PropertyKey, B, E2, R2>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>>, key: Exclude<K, NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never>>, f: GuardInput<NoInfer<Guard.Output<G>>, B, E2, R2>): Guard<Guard.Input<G>, Guard.Output<G> & {
244
709
  [k in K]: B;
245
- }, E | E2, R | R2>;
710
+ }, Guard.Error<G> | E2, Guard.Services<G> | R2>;
246
711
  };
247
712
  //# sourceMappingURL=index.d.ts.map