@typed/guard 1.0.0-beta.4 → 1.0.0-beta.6

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