unthrown 5.0.0-beta.5 → 5.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,13 +7,11 @@
7
7
  [API Reference](https://btravstack.github.io/unthrown/api/core/)
8
8
 
9
9
  ```sh
10
- pnpm add unthrown ts-pattern
10
+ pnpm add unthrown
11
11
  ```
12
12
 
13
- `ts-pattern` (`^5`) is a peer dependency — it powers the exhaustive error
14
- matchers and is re-exported as `match` / `P`. Declaring it a peer means you own
15
- the single copy, so `import { P } from "ts-pattern"` composes with unthrown's
16
- matchers.
13
+ No peer dependencies — the exhaustive error matcher is built-in and exported as
14
+ `match` / `P` / `tag`.
17
15
 
18
16
  ```ts
19
17
  import { fromPromise, P, TaggedError } from "unthrown";
@@ -38,8 +36,8 @@ const status = await user.match({
38
36
  - **Qualification at every boundary** — `fromPromise` / `fromThrowable` force you
39
37
  to triage each failure into a modeled error or a defect.
40
38
  - **Tagged errors** — `TaggedError(tag)` + `tag(t)`, folded exhaustively through
41
- `match`'s ts-pattern error matcher.
42
- - One tiny runtime dependency (`ts-pattern`, a peer you share), ESM-first, dual
39
+ `match`'s built-in error matcher.
40
+ - **Zero runtime dependencies** (the matcher is built-in), ESM-first, dual
43
41
  CJS/ESM.
44
42
 
45
43
  See the [full documentation](https://btravstack.github.io/unthrown/) for the guide
package/dist/index.cjs CHANGED
@@ -1,5 +1,160 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let ts_pattern = require("ts-pattern");
2
+ //#region src/matcher.ts
3
+ /**
4
+ * Cross-copy brand for `P.*` pattern objects: `Symbol.for` yields the same
5
+ * symbol in every copy of the library (dual CJS/ESM, duplicated install,
6
+ * another realm), so a pattern built by one copy is recognised by another —
7
+ * the same rationale as `isResult`'s prototype brand.
8
+ *
9
+ * @internal
10
+ */
11
+ const PATTERN_BRAND = Symbol.for("unthrown.matcher.pattern");
12
+ /**
13
+ * Thrown by `.run()` / `.exhaustive()` when no arm matched the value. For
14
+ * well-typed callers the match is exhaustive by construction, so this is only
15
+ * reachable by a value that slipped past the types (a widened cast, a raw-JS
16
+ * caller); inside the error combinators the throw-to-defect net converts it to
17
+ * a `Defect`, and at the `match` edge it surfaces (a genuinely unmodeled value
18
+ * is a bug).
19
+ *
20
+ * @category Errors
21
+ */
22
+ var NonExhaustiveError = class extends Error {
23
+ /** The value no arm matched. */
24
+ input;
25
+ constructor(input) {
26
+ let printed;
27
+ try {
28
+ printed = JSON.stringify(input);
29
+ } catch {
30
+ printed = String(input);
31
+ }
32
+ super(`unthrown: no pattern matched the value ${printed}`);
33
+ this.name = "NonExhaustiveError";
34
+ this.input = input;
35
+ Object.setPrototypeOf(this, new.target.prototype);
36
+ }
37
+ };
38
+ /**
39
+ * Is `x` a *plain* object (prototype `Object.prototype` or `null`) — an object
40
+ * literal, the only object shape that acts as a structural pattern?
41
+ *
42
+ * @internal
43
+ */
44
+ function isPlainObject(x) {
45
+ const proto = Object.getPrototypeOf(x);
46
+ return proto === Object.prototype || proto === null;
47
+ }
48
+ /**
49
+ * Runtime test: does `pattern` match `value`? A branded `P.*` pattern applies
50
+ * its predicate; a **plain-object** pattern (an object literal, e.g. the
51
+ * `{ _tag }` produced by `tag()`) matches when every key matches recursively
52
+ * (extra keys on the value are ignored — matching is structural); anything
53
+ * else — primitives, but also class instances, arrays, and foreign pattern
54
+ * objects (e.g. a real ts-pattern matcher, whose keys are symbols) — is
55
+ * compared with `Object.is`. Restricting structural matching to plain objects
56
+ * is load-bearing: a keyless non-plain object (`new Date()`, `new Error()`, a
57
+ * symbol-keyed foreign pattern) would otherwise vacuously match *every* object
58
+ * via an empty `Object.entries`.
59
+ *
60
+ * @internal
61
+ */
62
+ function matches(pattern, value) {
63
+ if (typeof pattern === "object" && pattern !== null) {
64
+ const predicate = pattern[PATTERN_BRAND];
65
+ if (typeof predicate === "function") return predicate(value);
66
+ if (!isPlainObject(pattern) || Object.getOwnPropertySymbols(pattern).length > 0) return Object.is(pattern, value);
67
+ if (typeof value !== "object" || value === null) return false;
68
+ return Object.entries(pattern).every(([key, sub]) => matches(sub, value[key]));
69
+ }
70
+ return Object.is(pattern, value);
71
+ }
72
+ /**
73
+ * The runtime builder: first matching arm wins; later arms are skipped once a
74
+ * result is captured. `exhaustive` is a *method* at runtime (the conditional
75
+ * type gates its callability per instantiation).
76
+ *
77
+ * @internal
78
+ */
79
+ var MatcherImpl = class {
80
+ #value;
81
+ #matched = false;
82
+ #result;
83
+ constructor(value) {
84
+ this.#value = value;
85
+ }
86
+ with(...args) {
87
+ if (this.#matched) return this;
88
+ const handler = args[args.length - 1];
89
+ for (let i = 0; i < args.length - 1; i++) if (matches(args[i], this.#value)) {
90
+ this.#matched = true;
91
+ this.#result = handler(this.#value);
92
+ return this;
93
+ }
94
+ return this;
95
+ }
96
+ /**
97
+ * Type-level only — pinning the output type has no runtime meaning, so the
98
+ * builder is returned unchanged (as ts-pattern does).
99
+ */
100
+ returnType() {
101
+ return this;
102
+ }
103
+ exhaustive() {
104
+ if (this.#matched) return this.#result;
105
+ throw new NonExhaustiveError(this.#value);
106
+ }
107
+ run() {
108
+ return this.exhaustive();
109
+ }
110
+ };
111
+ Object.freeze(MatcherImpl.prototype);
112
+ /**
113
+ * Begin a match over `value`. Chain `.with(pattern, …patterns, handler)` arms;
114
+ * terminate with `.exhaustive()` — or return the un-terminated builder to an
115
+ * unthrown error combinator / `match({ errCases })`, which runs it for you.
116
+ *
117
+ * @remarks
118
+ * This is unthrown's own matcher (the former ts-pattern re-export): the same
119
+ * call-site shape, with exhaustiveness computed by plain `Exclude` over the
120
+ * builder's `Remaining` parameter. A `P._` catch-all is provably exhaustive
121
+ * even over an unresolved generic input.
122
+ *
123
+ * @category Constructors
124
+ */
125
+ function match(value) {
126
+ return new MatcherImpl(value);
127
+ }
128
+ /** @internal */
129
+ function pattern(predicate) {
130
+ return Object.freeze({ [PATTERN_BRAND]: predicate });
131
+ }
132
+ const universal = pattern(() => true);
133
+ /**
134
+ * The pattern namespace (unthrown's own; the former ts-pattern `P`):
135
+ *
136
+ * - `P._` / `P.any` — the universal catch-all. Matches anything, and (because
137
+ * its phantom type is `unknown`) makes the builder provably exhaustive even
138
+ * when the matched input is an unresolved type parameter.
139
+ * - `P.instanceOf(Cls)` — an `instanceof` check, narrowing to the class
140
+ * instance type (for union members that are not tagged, e.g. a third-party
141
+ * error class).
142
+ * - `P.when(guard)` — an arbitrary type-guard predicate.
143
+ * - `P.union(…patterns)` — matches when any sub-pattern matches.
144
+ * - `P.string` / `P.number` — primitive-type wildcards.
145
+ *
146
+ * @category Constructors
147
+ */
148
+ const P = Object.freeze({
149
+ _: universal,
150
+ any: universal,
151
+ instanceOf: (cls) => pattern((value) => value instanceof cls),
152
+ when: (guard) => pattern(guard),
153
+ union: (...patterns) => pattern((value) => patterns.some((sub) => matches(sub, value))),
154
+ string: pattern((value) => typeof value === "string"),
155
+ number: pattern((value) => typeof value === "number")
156
+ });
157
+ //#endregion
3
158
  //#region src/defect.ts
4
159
  const DEFECT = Symbol("unthrown/Defect");
5
160
  /**
@@ -186,7 +341,8 @@ var Res = class {
186
341
  tapErrCases(f) {
187
342
  if (this.tag !== "Err") return this;
188
343
  try {
189
- runMatch(f, this.error);
344
+ const out = runMatch(f, this.error);
345
+ if (isDefectMarker(out)) return observerThrowToDefect(out.cause, this.error);
190
346
  return this;
191
347
  } catch (cause) {
192
348
  return observerThrowToDefect(cause, this.error);
@@ -196,6 +352,7 @@ var Res = class {
196
352
  if (this.tag !== "Err") return this;
197
353
  try {
198
354
  const r = runMatch(f, this.error);
355
+ if (isDefectMarker(r)) return observerThrowToDefect(r.cause, this.error);
199
356
  if (!isResult(r)) return nonResultCallbackDefect();
200
357
  return r.tag === "Ok" ? this : passThrough(r);
201
358
  } catch (cause) {
@@ -232,7 +389,7 @@ var Res = class {
232
389
  match(cases) {
233
390
  switch (this.tag) {
234
391
  case "Ok": return cases.ok(this.value);
235
- case "Err": return cases.errCases((0, ts_pattern.match)(this.error)).run();
392
+ case "Err": return cases.errCases(match(this.error)).run();
236
393
  case "Defect": return cases.defect(this.cause);
237
394
  }
238
395
  }
@@ -408,19 +565,24 @@ function nonResultCallbackDefect() {
408
565
  * @internal
409
566
  */
410
567
  function runMatch(f, error) {
411
- return f((0, ts_pattern.match)(error), defect).run();
568
+ return f(match(error), defect).run();
412
569
  }
413
570
  /**
414
- * A throw inside a *failure observer* (`tapErrCases` / `tapDefect` / `flatTapErrCases`)
415
- * must not destroy the failure being observed — that is the exact place (e.g. a
416
- * failing error-logger) where losing the underlying failure hurts most. The
417
- * resulting Defect aggregates both: `errors[0]` is the observer's throw,
418
- * `errors[1]` the original failure.
571
+ * A throw inside a *failure observer* (`tapErrCases` / `tapDefect` /
572
+ * `tapFailure` / `flatTapErrCases`) must not destroy the failure being observed
573
+ * — that is the exact place (e.g. a failing error-logger) where losing the
574
+ * underlying failure hurts most. The resulting Defect aggregates both:
575
+ * `errors[0]` is the observer's own failure, `errors[1]` the original failure.
576
+ *
577
+ * An observer branch returning the injected `defect(cause)` marker
578
+ * (`tapErrCases` / `flatTapErrCases`) takes the same route: it is the
579
+ * lint-clean, expression-position form of a `throw` (Thesis #5), so it must not
580
+ * behave differently from one.
419
581
  *
420
582
  * @internal
421
583
  */
422
584
  function observerThrowToDefect(thrown, original) {
423
- return defectRes(new AggregateError([thrown, original], "unthrown: a failure-observer callback threw; errors[0] is the callback's throw, errors[1] the original failure"));
585
+ return defectRes(new AggregateError([thrown, original], "unthrown: a failure-observer callback failed; errors[0] is the callback's failure (a throw, or a deliberate defect), errors[1] the original failure"));
424
586
  }
425
587
  /**
426
588
  * Validate that a `bind`/`let` scope is a real (non-null) object before merging a
@@ -590,7 +752,8 @@ var AsyncRes = class AsyncRes {
590
752
  return new AsyncRes(this.#promise.then((r) => {
591
753
  if (r.tag !== "Err") return r;
592
754
  try {
593
- runMatch(f, r.error);
755
+ const out = runMatch(f, r.error);
756
+ if (isDefectMarker(out)) return observerThrowToDefect(out.cause, r.error);
594
757
  return r;
595
758
  } catch (cause) {
596
759
  return observerThrowToDefect(cause, r.error);
@@ -601,7 +764,9 @@ var AsyncRes = class AsyncRes {
601
764
  return new AsyncRes(this.#promise.then(async (r) => {
602
765
  if (r.tag !== "Err") return passThrough(r);
603
766
  try {
604
- const inner = await runMatch(f, r.error);
767
+ const out = runMatch(f, r.error);
768
+ if (isDefectMarker(out)) return observerThrowToDefect(out.cause, r.error);
769
+ const inner = await out;
605
770
  if (!isResult(inner)) return nonResultCallbackDefect();
606
771
  return inner.tag === "Ok" ? passThrough(r) : passThrough(inner);
607
772
  } catch (cause) {
@@ -1405,7 +1570,7 @@ function TaggedError(tag, options) {
1405
1570
  return TaggedErrorBase;
1406
1571
  }
1407
1572
  /**
1408
- * A `ts-pattern` pattern matching any value whose `_tag` equals `value` — a
1573
+ * A matcher pattern matching any value whose `_tag` equals `value` — a
1409
1574
  * {@link TaggedError}, or any discriminated member. Equivalent to the object
1410
1575
  * pattern `{ _tag: value }`, but reads better inside an error-matching
1411
1576
  * combinator and narrows to the matching variant, payload included.
@@ -1434,14 +1599,10 @@ exports.DoAsync = DoAsync;
1434
1599
  exports.Err = Err;
1435
1600
  exports.ErrAsync = ErrAsync;
1436
1601
  exports.GetError = GetError;
1602
+ exports.NonExhaustiveError = NonExhaustiveError;
1437
1603
  exports.Ok = Ok;
1438
1604
  exports.OkAsync = OkAsync;
1439
- Object.defineProperty(exports, "P", {
1440
- enumerable: true,
1441
- get: function() {
1442
- return ts_pattern.P;
1443
- }
1444
- });
1605
+ exports.P = P;
1445
1606
  exports.Result = Result;
1446
1607
  exports.TaggedError = TaggedError;
1447
1608
  exports.all = all;
@@ -1457,10 +1618,5 @@ exports.isDefect = isDefect;
1457
1618
  exports.isErr = isErr;
1458
1619
  exports.isOk = isOk;
1459
1620
  exports.isResult = isResult;
1460
- Object.defineProperty(exports, "match", {
1461
- enumerable: true,
1462
- get: function() {
1463
- return ts_pattern.match;
1464
- }
1465
- });
1621
+ exports.match = match;
1466
1622
  exports.tag = tag;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,3 @@
1
- import { P, match, match as match$1 } from "ts-pattern";
2
1
  //#region src/defect.d.ts
3
2
  declare const DEFECT: unique symbol;
4
3
  /**
@@ -20,6 +19,226 @@ type Defect = {
20
19
  readonly cause: unknown;
21
20
  };
22
21
  //#endregion
22
+ //#region src/matcher.d.ts
23
+ /**
24
+ * Cross-copy brand for `P.*` pattern objects: `Symbol.for` yields the same
25
+ * symbol in every copy of the library (dual CJS/ESM, duplicated install,
26
+ * another realm), so a pattern built by one copy is recognised by another —
27
+ * the same rationale as `isResult`'s prototype brand.
28
+ *
29
+ * @internal
30
+ */
31
+ declare const PATTERN_BRAND: unique symbol;
32
+ declare const MATCHES: unique symbol;
33
+ declare const UNIVERSAL: unique symbol;
34
+ /**
35
+ * A `P.*` pattern: a runtime predicate plus the phantom type `M` it matches.
36
+ * The phantom is declaration-only (never present at runtime); it drives the
37
+ * type-level narrowing (`Extract`) and exhaustiveness (`Exclude`).
38
+ *
39
+ * @typeParam M - the type this pattern matches.
40
+ * @category Types
41
+ */
42
+ type PatternMatcher<M> = {
43
+ readonly [PATTERN_BRAND]: (value: unknown) => boolean;
44
+ readonly [MATCHES]?: M;
45
+ };
46
+ /**
47
+ * The statically-known universal pattern — the type of `P._` / `P.any` only.
48
+ * The phantom `UNIVERSAL` marker is *required*, so no other
49
+ * `PatternMatcher<unknown>` (e.g. a `P.when` guard that happens to be
50
+ * universal) is assignable: the catch-all `.with` overload must only fire for
51
+ * a pattern the type system KNOWS covers everything.
52
+ *
53
+ * @category Types
54
+ */
55
+ type UniversalPattern = PatternMatcher<unknown> & {
56
+ readonly [UNIVERSAL]: true;
57
+ };
58
+ /**
59
+ * The type a single pattern matches: a `P.*` matcher's phantom, an object
60
+ * literal mapped key-by-key (so `{ _tag: "A" }` matches the `"A"`-tagged
61
+ * variant), or the primitive literal itself.
62
+ *
63
+ * @internal
64
+ */
65
+ type MatchedOf<Pt> = Pt extends PatternMatcher<infer M> ? M : Pt extends object ? { [K in keyof Pt]: MatchedOf<Pt[K]>; } : Pt;
66
+ /**
67
+ * The diagnostic type of `.exhaustive` on a builder that has NOT covered every
68
+ * case: not callable (so it fails the `ExhaustiveMatch` constraint at the call
69
+ * site), and it names the remaining cases so the error reads as a to-do list.
70
+ *
71
+ * @internal
72
+ */
73
+ type NonExhaustive<Remaining> = {
74
+ readonly "unthrown: this match is not exhaustive — add a `.with(…)` for the remaining cases": Remaining;
75
+ };
76
+ /**
77
+ * The "no output type declared" sentinel for a builder's `Declared` parameter.
78
+ * A `unique symbol` so no user type can collide with it. Declaration-only —
79
+ * `tsc` emits it into the `.d.ts` without it needing to be exported.
80
+ *
81
+ * @internal
82
+ */
83
+ declare const UNSET: unique symbol;
84
+ /** @internal */
85
+ type Unset = typeof UNSET;
86
+ /**
87
+ * A branch handler's return position: free inference (`O2`) while the builder
88
+ * is unpinned — today's behaviour, unchanged — or the declared type once
89
+ * `.returnType<R>()` has pinned it.
90
+ *
91
+ * `Defect` stays legal under a pin: the injected `defect` helper is the
92
+ * sanctioned deliberate `Err`→`Defect` form (Thesis #5), and `Defect` is not a
93
+ * nameable public type, so `returnType<R | Defect>()` cannot be spelled. The
94
+ * marker is subtracted from the output by {@link PinnedOut} — the same net
95
+ * result as the unpinned `Exclude<O, Defect>`, decided up front.
96
+ *
97
+ * @internal
98
+ */
99
+ type BranchReturn<Declared, O2> = [Declared] extends [Unset] ? O2 : Declared | Defect;
100
+ /**
101
+ * The builder's output: the accumulated union of branch returns while
102
+ * unpinned, or the declared type once pinned.
103
+ *
104
+ * @internal
105
+ */
106
+ type PinnedOut<Declared, O> = [Declared] extends [Unset] ? O : Declared;
107
+ /**
108
+ * The diagnostic type of `.returnType` on a builder that already has an output
109
+ * to contradict — an arm has contributed a return type, or it is already
110
+ * pinned: not callable, so the mistake is caught where it is written.
111
+ *
112
+ * @internal
113
+ */
114
+ type PinTooLate = {
115
+ readonly "unthrown: `.returnType<R>()` must come before any arm produces an output, and only once": true;
116
+ };
117
+ /**
118
+ * The match builder over an input union `E`. `Remaining` tracks the cases not
119
+ * yet covered by a `.with(…)` arm; `O` accumulates the branch output union.
120
+ * `.exhaustive` is callable only once `Remaining` is `never` — which is what
121
+ * the `ExhaustiveMatch` constraint requires — and `.run()` executes it.
122
+ *
123
+ * @typeParam E - the full input union being matched.
124
+ * @typeParam Remaining - the cases not yet covered.
125
+ * @typeParam O - the union of branch return types so far.
126
+ * @category Types
127
+ */
128
+ type Matcher<E, Remaining, O, Declared = Unset> = {
129
+ /**
130
+ * The catch-all arm: `.with(P._, handler)` / `.with(P.any, handler)`. A
131
+ * **state transition**, not a computation — it returns `Matcher<E, never, …>`
132
+ * with the remaining cases literally `never`, so the builder is provably
133
+ * exhaustive even when `E` is an unresolved type parameter (a lazily-deferred
134
+ * `Exclude<E, unknown>` would not resolve there). This is what lets a
135
+ * boundary helper generic in `E` terminate with the catch-all (issue #145).
136
+ */
137
+ with<O2>(pattern: UniversalPattern, handler: (value: Remaining) => BranchReturn<Declared, O2>): Matcher<E, never, O | O2, Declared>;
138
+ /**
139
+ * Add an arm: one or more patterns sharing a single handler (grouped
140
+ * patterns — `matcher.with(tag("A"), tag("B"), handler)`). The handler
141
+ * receives the input narrowed to what the patterns match (computed against
142
+ * `Remaining`, so cases already handled by earlier arms are excluded); the
143
+ * matched cases are subtracted from `Remaining`.
144
+ */
145
+ with<const Pts extends readonly [unknown, ...unknown[]], O2>(...args: [...patterns: Pts, handler: (value: Extract<Remaining, MatchedOf<Pts[number]>>) => BranchReturn<Declared, O2>]): Matcher<E, Exclude<Remaining, MatchedOf<Pts[number]>>, O | O2, Declared>;
146
+ /**
147
+ * Declare the match's output type up front: every subsequent branch handler
148
+ * is checked against `R`, and the match evaluates to `R` instead of the
149
+ * union of whatever the branches happened to return.
150
+ *
151
+ * @remarks
152
+ * Reach for it when the output is **decided by a signature rather than by
153
+ * the branches** — most sharply in code generic in `E`, where the fold's type
154
+ * has to be declared. It also stops a drifting branch from silently widening
155
+ * the outgoing type, reports the mismatch **on the offending branch**, and
156
+ * gives branch returns a contextual type (so object literals need no
157
+ * annotation).
158
+ *
159
+ * A branch may still return the injected `defect` helper's marker; the defect
160
+ * channel is not part of the declared output.
161
+ *
162
+ * Callable **before any arm has produced an output**, and only once
163
+ * (mirroring ts-pattern's up-front pin): once there is an inferred output for
164
+ * the pin to contradict — or the builder is already pinned — this is typed as
165
+ * a non-callable diagnostic. In practice that means calling it directly after
166
+ * `match(…)`; the gate is about output rather than position, so an earlier arm
167
+ * whose handler returns `never` (it always throws) contributes nothing and
168
+ * does not close it — sound, since a `never` branch can contradict no declared
169
+ * type. A no-op at runtime.
170
+ *
171
+ * @typeParam R - the declared output type of every branch.
172
+ */
173
+ returnType: [O] extends [never] ? [Declared] extends [Unset] ? <R>() => Matcher<E, Remaining, never, R> : PinTooLate : PinTooLate;
174
+ /**
175
+ * Terminate the match. Typed callable only when every case is covered
176
+ * (`Remaining` is `never`); otherwise it is a branded diagnostic object
177
+ * naming the remaining cases, and the builder fails the `ExhaustiveMatch`
178
+ * constraint at the combinator call site.
179
+ */
180
+ exhaustive: [Remaining] extends [never] ? () => PinnedOut<Declared, O> : NonExhaustive<Remaining>;
181
+ /**
182
+ * Execute the match (the combinators call this; it runs `.exhaustive()`).
183
+ * A value with no matching arm throws {@link NonExhaustiveError} —
184
+ * unreachable for well-typed callers.
185
+ */
186
+ run(): PinnedOut<Declared, O>;
187
+ };
188
+ /**
189
+ * Thrown by `.run()` / `.exhaustive()` when no arm matched the value. For
190
+ * well-typed callers the match is exhaustive by construction, so this is only
191
+ * reachable by a value that slipped past the types (a widened cast, a raw-JS
192
+ * caller); inside the error combinators the throw-to-defect net converts it to
193
+ * a `Defect`, and at the `match` edge it surfaces (a genuinely unmodeled value
194
+ * is a bug).
195
+ *
196
+ * @category Errors
197
+ */
198
+ declare class NonExhaustiveError extends Error {
199
+ /** The value no arm matched. */
200
+ readonly input: unknown;
201
+ constructor(input: unknown);
202
+ }
203
+ /**
204
+ * Begin a match over `value`. Chain `.with(pattern, …patterns, handler)` arms;
205
+ * terminate with `.exhaustive()` — or return the un-terminated builder to an
206
+ * unthrown error combinator / `match({ errCases })`, which runs it for you.
207
+ *
208
+ * @remarks
209
+ * This is unthrown's own matcher (the former ts-pattern re-export): the same
210
+ * call-site shape, with exhaustiveness computed by plain `Exclude` over the
211
+ * builder's `Remaining` parameter. A `P._` catch-all is provably exhaustive
212
+ * even over an unresolved generic input.
213
+ *
214
+ * @category Constructors
215
+ */
216
+ declare function match<const E>(value: E): Matcher<E, E, never>;
217
+ /**
218
+ * The pattern namespace (unthrown's own; the former ts-pattern `P`):
219
+ *
220
+ * - `P._` / `P.any` — the universal catch-all. Matches anything, and (because
221
+ * its phantom type is `unknown`) makes the builder provably exhaustive even
222
+ * when the matched input is an unresolved type parameter.
223
+ * - `P.instanceOf(Cls)` — an `instanceof` check, narrowing to the class
224
+ * instance type (for union members that are not tagged, e.g. a third-party
225
+ * error class).
226
+ * - `P.when(guard)` — an arbitrary type-guard predicate.
227
+ * - `P.union(…patterns)` — matches when any sub-pattern matches.
228
+ * - `P.string` / `P.number` — primitive-type wildcards.
229
+ *
230
+ * @category Constructors
231
+ */
232
+ declare const P: Readonly<{
233
+ _: UniversalPattern;
234
+ any: UniversalPattern;
235
+ instanceOf: <C extends abstract new (...args: never[]) => unknown>(cls: C) => PatternMatcher<InstanceType<C>>;
236
+ when: <G>(guard: (value: unknown) => value is G) => PatternMatcher<G>;
237
+ union: <const Pts extends readonly [unknown, ...unknown[]]>(...patterns: Pts) => PatternMatcher<MatchedOf<Pts[number]>>;
238
+ string: PatternMatcher<string>;
239
+ number: PatternMatcher<number>;
240
+ }>;
241
+ //#endregion
23
242
  //#region src/types.d.ts
24
243
  /**
25
244
  * Flatten an intersection into a single object literal so accumulated `bind` /
@@ -55,23 +274,23 @@ type Bound<T, K extends string, U> = Prettify<Omit<T, K> & { readonly [P in K]:
55
274
  */
56
275
  type NotThenable<R> = [R] extends [PromiseLike<unknown>] ? "unthrown: combinator callbacks are synchronous — lift async work with fromPromise and compose with flatMap" : unknown;
57
276
  /**
58
- * The ts-pattern match builder over an error union `E`, as produced by
277
+ * The built-in match builder over an error union `E`, as produced by
59
278
  * `match(error)`. This is what an error combinator's callback receives — chain
60
279
  * `.with(pattern, handler)` on it; the combinator itself calls `.exhaustive()`,
61
280
  * so the callback returns the **un-terminated** builder.
62
281
  *
63
282
  * @remarks
64
- * Named via `ReturnType<typeof match<E>>` so the internal ts-pattern `Match`
65
- * type (not part of ts-pattern's public exports) never has to be imported.
283
+ * Named via `ReturnType<typeof match<E>>` (i.e. `Matcher<E, E, never>`),
284
+ * keeping this alias stable however the builder evolves.
66
285
  *
67
286
  * @typeParam E - the error union being matched.
68
287
  * @category Types
69
288
  */
70
- type ErrMatcher<E> = ReturnType<typeof match$1<E>>;
289
+ type ErrMatcher<E> = ReturnType<typeof match<E>>;
71
290
  /**
72
291
  * The shape an error-combinator callback must return: an **exhaustive**
73
- * ts-pattern builder. `exhaustive` is required to be *callable* — on a builder
74
- * that hasn't covered every case ts-pattern types it as a `NonExhaustiveError`
292
+ * match builder. `exhaustive` is required to be *callable* — on a builder
293
+ * that hasn't covered every case the matcher types it as a branded diagnostic
75
294
  * (not a function), so a non-exhaustive chain fails to satisfy this and errors
76
295
  * at the call site. `run` carries the output type.
77
296
  *
@@ -267,7 +486,7 @@ type ResultMethods<out T, out E> = {
267
486
  */
268
487
  ensure<E2>(predicate: (value: T) => boolean, onFail: (value: T) => E2 & NotThenable<E2>): Result$1<T, E | E2>;
269
488
  /**
270
- * Transform the modeled error by **matching it exhaustively** with ts-pattern.
489
+ * Transform the modeled error by **matching it exhaustively**.
271
490
  *
272
491
  * @remarks
273
492
  * The callback receives `match(error)` (an {@link ErrMatcher}) and the
@@ -281,7 +500,7 @@ type ResultMethods<out T, out E> = {
281
500
  * pass through. A branch that throws also becomes a `Defect`.
282
501
  *
283
502
  * `.with(P._, …)` is the deliberate uniform/catch-all (it makes the match
284
- * exhaustive). Match on anything ts-pattern supports — `_tag`, `code`,
503
+ * exhaustive). Match on anything the matcher supports — `_tag`, `code`,
285
504
  * structural shape, guards, or grouped patterns `.with(a, b, handler)`.
286
505
  *
287
506
  * @typeParam M - the exhaustive builder the callback returns.
@@ -328,11 +547,15 @@ type ResultMethods<out T, out E> = {
328
547
  * failure]` — observing a failure never destroys it. An **async branch is
329
548
  * rejected at compile time** ({@link NotThenable} on the builder output):
330
549
  * because the branch results are discarded, a returned `Promise` would float
331
- * unobserved and its rejection would vanish. A failable
550
+ * unobserved and its rejection would vanish. The one branch return that is
551
+ * **not** discarded is the injected `defect(cause)` marker: it is the
552
+ * lint-clean, expression-position form of a `throw`, so it follows the throw
553
+ * rule above (an `AggregateError` of `[the branch's cause, original
554
+ * failure]`), never a silent no-op. A failable
332
555
  * `Result`-returning effect belongs in
333
556
  * {@link ResultMethods.flatTapErrCases | flatTapErrCases}.
334
557
  *
335
- * @param f - builds the match; branch returns are ignored.
558
+ * @param f - builds the match; branch returns are ignored, bar `defect(cause)`.
336
559
  */
337
560
  tapErrCases<R>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<R & NotThenable<R>>): Result$1<T, E>;
338
561
  /**
@@ -345,10 +568,13 @@ type ResultMethods<out T, out E> = {
345
568
  * branch returns a `Result` whose **success value is discarded** — on the
346
569
  * effect's `Ok` the original `Err` flows through, while an `Err`/`Defect` from a
347
570
  * branch short-circuits and threads its error. Note the asymmetry with a
348
- * *throw*: a branch that **returns** a `Defect` **replaces** the original `Err`
349
- * (Defect-dominance, the short-circuit rule — it is not aggregated), whereas a
350
- * branch that **throws** produces a `Defect` aggregating `[thrown, original
351
- * failure]` (observing a failure by throwing never destroys it).
571
+ * *throw*: a branch that **returns** a Defect-state `Result` **replaces** the
572
+ * original `Err` (Defect-dominance, the short-circuit rule — it is not
573
+ * aggregated), whereas a branch that **throws** produces a `Defect`
574
+ * aggregating `[thrown, original failure]` (observing a failure by throwing
575
+ * never destroys it). A branch returning the injected `defect(cause)` marker —
576
+ * reachable under a `returnType` pin — follows the *throw* rule, since it is
577
+ * the lint-clean, expression-position form of one.
352
578
  *
353
579
  * @typeParam M - the exhaustive builder the callback returns.
354
580
  * @param f - builds the match; each branch is a failable effect (its `Ok` is ignored).
@@ -620,7 +846,7 @@ type FailureView<E, T = never> = ErrView<E, T> | DefectView<T, E>;
620
846
  * never appears in `E`; it is the library's third, out-of-band channel.
621
847
  *
622
848
  * Because it is a real union, you can match it natively (a `switch` on `tag`, or
623
- * `ts-pattern`'s `match(...).with({ tag: "Ok" }, …).exhaustive()`), *and* it
849
+ * the built-in `match(...).with({ tag: "Ok" }, …).exhaustive()`), *and* it
624
850
  * carries the full method surface ({@link ResultMethods}) for fluent chaining.
625
851
  * Either way, the payload (`value`/`error`/`cause`) is only reachable after you
626
852
  * narrow — so "check before you access" still holds.
@@ -775,11 +1001,13 @@ type AsyncResultMethods<out T, out E> = {
775
1001
  recoverErrCases<M extends ExhaustiveMatch<unknown>>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => M): AsyncResult$1<T | MatchErrOut<M>, never>;
776
1002
  /**
777
1003
  * Asynchronous {@link ResultMethods.tapErrCases | tapErrCases}. `f` is synchronous; if it
778
- * throws, the result is a `Defect` whose cause is an `AggregateError` of
779
- * `[thrown, original failure]` — observing a failure never destroys it. An
1004
+ * throws — or a branch returns the injected `defect(cause)` marker, the
1005
+ * expression-position form of a throw — the result is a `Defect` whose cause
1006
+ * is an `AggregateError` of `[thrown, original failure]` — observing a failure
1007
+ * never destroys it. An
780
1008
  * async branch is rejected at compile time ({@link NotThenable} on the
781
- * builder output) — branch results are discarded, so a rejected `Promise`
782
- * would float unobserved. The
1009
+ * builder output) — other branch results are discarded, so a rejected
1010
+ * `Promise` would float unobserved. The
783
1011
  * {@link AsyncResultMethods.tap | tap} fire-and-forget caveat applies here
784
1012
  * too — a failable effect belongs in
785
1013
  * {@link AsyncResultMethods.flatTapErrCases | flatTapErrCases}.
@@ -789,9 +1017,10 @@ type AsyncResultMethods<out T, out E> = {
789
1017
  * Asynchronous {@link ResultMethods.flatTapErrCases | flatTapErrCases} — the
790
1018
  * error-channel mirror of `flatTap`. `f` may return a `Result` **or** an
791
1019
  * `AsyncResult`; its `Ok` value is discarded, an `Err`/`Defect` from `f`
792
- * threads through, and if `f` throws, the result is a `Defect` whose cause is
793
- * an `AggregateError` of `[thrown, original failure]` — observing a failure
794
- * never destroys it.
1020
+ * threads through, and if `f` throws — or a branch returns the injected
1021
+ * `defect(cause)` marker, the expression-position form of a throw — the result
1022
+ * is a `Defect` whose cause is an `AggregateError` of `[thrown, original
1023
+ * failure]` — observing a failure never destroys it.
795
1024
  */
796
1025
  flatTapErrCases<E2>(f: (matcher: ErrMatcher<E>, defect: (cause: unknown) => Defect) => ExhaustiveMatch<Result$1<unknown, E2> | AsyncResult$1<unknown, E2>>): AsyncResult$1<T, E | E2>;
797
1026
  /**
@@ -1746,7 +1975,7 @@ declare function TaggedError<Tag extends string>(tag: Tag, options?: {
1746
1975
  readonly name?: string;
1747
1976
  }): TaggedErrorConstructor<Tag>;
1748
1977
  /**
1749
- * A `ts-pattern` pattern matching any value whose `_tag` equals `value` — a
1978
+ * A matcher pattern matching any value whose `_tag` equals `value` — a
1750
1979
  * {@link TaggedError}, or any discriminated member. Equivalent to the object
1751
1980
  * pattern `{ _tag: value }`, but reads better inside an error-matching
1752
1981
  * combinator and narrows to the matching variant, payload included.
@@ -1769,5 +1998,5 @@ declare function tag<const Tag extends string>(value: Tag): {
1769
1998
  _tag: Tag;
1770
1999
  };
1771
2000
  //#endregion
1772
- export { type AsyncErrOf, type AsyncOkOf, AsyncResult, type AsyncResultMethods, type Awaitable, type DefectView, Do, DoAsync, Err, ErrAsync, type ErrMatcher, type ErrOf, type ErrView, type FailureView, GetError, type NotThenable, Ok, OkAsync, type OkOf, type OkView, P, Result, type ResultMethods, TaggedError, type TaggedErrorConstructor, type TaggedErrorInstance, all, allAsync, allFromDict, allFromDictAsync, fromNullable, fromPromise, fromSafePromise, fromSafeThrowable, fromThrowable, isDefect, isErr, isOk, isResult, match, tag };
2001
+ export { type AsyncErrOf, type AsyncOkOf, AsyncResult, type AsyncResultMethods, type Awaitable, type DefectView, Do, DoAsync, Err, ErrAsync, type ErrMatcher, type ErrOf, type ErrView, type FailureView, GetError, type Matcher, NonExhaustiveError, type NotThenable, Ok, OkAsync, type OkOf, type OkView, P, type PatternMatcher, Result, type ResultMethods, TaggedError, type TaggedErrorConstructor, type TaggedErrorInstance, type UniversalPattern, all, allAsync, allFromDict, allFromDictAsync, fromNullable, fromPromise, fromSafePromise, fromSafeThrowable, fromThrowable, isDefect, isErr, isOk, isResult, match, tag };
1773
2002
  //# sourceMappingURL=index.d.cts.map