@consolidados/results 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -10
- package/dist/helpers/match.cjs +24 -42
- package/dist/helpers/match.cjs.map +1 -1
- package/dist/helpers/match.d.cts +9 -17
- package/dist/helpers/match.d.ts +9 -17
- package/dist/helpers/match.js +24 -42
- package/dist/helpers/match.js.map +1 -1
- package/dist/index.cjs +41 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +41 -46
- package/dist/index.js.map +1 -1
- package/dist/option/index.cjs +17 -4
- package/dist/option/index.cjs.map +1 -1
- package/dist/option/index.d.cts +1 -1
- package/dist/option/index.d.ts +1 -1
- package/dist/option/index.js +17 -4
- package/dist/option/index.js.map +1 -1
- package/dist/option/option.cjs +17 -4
- package/dist/option/option.cjs.map +1 -1
- package/dist/option/option.d.cts +2 -2
- package/dist/option/option.d.ts +2 -2
- package/dist/option/option.js +17 -4
- package/dist/option/option.js.map +1 -1
- package/dist/{option-B_KKIecf.d.cts → option-CYCiGxtw.d.cts} +13 -0
- package/dist/{option-B_KKIecf.d.ts → option-CYCiGxtw.d.ts} +13 -0
- package/dist/result/index.cjs +9 -2
- package/dist/result/index.cjs.map +1 -1
- package/dist/result/index.d.cts +1 -1
- package/dist/result/index.d.ts +1 -1
- package/dist/result/index.js +9 -2
- package/dist/result/index.js.map +1 -1
- package/dist/result/result.cjs +9 -2
- package/dist/result/result.cjs.map +1 -1
- package/dist/result/result.d.cts +2 -2
- package/dist/result/result.d.ts +2 -2
- package/dist/result/result.js +9 -2
- package/dist/result/result.js.map +1 -1
- package/dist/types/globals.d.cts +9 -17
- package/dist/types/globals.d.ts +9 -17
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ This package provides robust implementations of the `Result` and `Option` types,
|
|
|
11
11
|
- 🎯 **Type-safe** - Full TypeScript support with type narrowing
|
|
12
12
|
- 🚀 **Performance** - None singleton pattern (95% less allocations)
|
|
13
13
|
- 🔄 **Flexible** - Support for any error type (enums, strings, custom classes)
|
|
14
|
-
- 🎨 **Pattern matching** - Match primitives, enums, and
|
|
14
|
+
- 🎨 **Pattern matching** - Match primitives, enums, discriminated unions, and mixed primitive + object unions
|
|
15
15
|
- 🛠️ **Rich API** - unwrapOr, orElse, filter, and more
|
|
16
16
|
- 🌍 **Global availability** - Optional global imports for cleaner code
|
|
17
17
|
|
|
@@ -48,6 +48,71 @@ const result = Ok(42);
|
|
|
48
48
|
const option = Some("hello");
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
### Monorepo / workspace setup
|
|
52
|
+
|
|
53
|
+
In a monorepo (Turborepo, Nx, pnpm workspaces, etc.) you usually don't want
|
|
54
|
+
to repeat the global wiring in every `tsconfig.json` and every entrypoint.
|
|
55
|
+
Wrap it once in a shared types package and have the other workspaces depend
|
|
56
|
+
on that.
|
|
57
|
+
|
|
58
|
+
**1. Create a shared package, e.g. `packages/types`:**
|
|
59
|
+
|
|
60
|
+
`packages/types/src/globals.ts` — runtime side-effect (registers
|
|
61
|
+
`Ok`, `Err`, `Some`, `None`, `match` on `globalThis`):
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import "@consolidados/results";
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`packages/types/src/globals-types.d.ts` — ambient TypeScript types so
|
|
68
|
+
`Result<T, E>` and `Option<T>` work without per-file imports:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import type { Option as _Option, Result as _Result } from "@consolidados/results";
|
|
72
|
+
|
|
73
|
+
declare global {
|
|
74
|
+
type Result<T, E> = _Result<T, E>;
|
|
75
|
+
type Option<T> = _Option<T>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export {};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`packages/types/package.json` — expose both as subpath exports:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"name": "@your-org/types",
|
|
86
|
+
"dependencies": { "@consolidados/results": "^0.5.0" },
|
|
87
|
+
"exports": {
|
|
88
|
+
"./globals": "./src/globals.ts",
|
|
89
|
+
"./globals-types": "./src/globals-types.d.ts"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**2. In each consuming workspace:**
|
|
95
|
+
|
|
96
|
+
`services/<name>/tsconfig.json`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"compilerOptions": {
|
|
101
|
+
"types": ["node", "@your-org/types/globals-types"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`services/<name>/src/main.ts` (first import of the entrypoint):
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import "@your-org/types/globals";
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Now every file in that workspace can use `Ok`, `Err`, `Some`, `None`, `match`,
|
|
113
|
+
`Result<T, E>`, and `Option<T>` without any import — and the wiring lives in
|
|
114
|
+
one place.
|
|
115
|
+
|
|
51
116
|
## Quick Start
|
|
52
117
|
|
|
53
118
|
### Result - Handle Success/Failure
|
|
@@ -150,8 +215,8 @@ var APIError;
|
|
|
150
215
|
```typescript
|
|
151
216
|
const APIError = {
|
|
152
217
|
NotFound: "NOT_FOUND",
|
|
153
|
-
Unauthorized
|
|
154
|
-
ServerError
|
|
218
|
+
Unauthorized: "UNAUTHORIZED",
|
|
219
|
+
ServerError: "SERVER_ERROR",
|
|
155
220
|
} as const;
|
|
156
221
|
|
|
157
222
|
type APIError = (typeof APIError)[keyof typeof APIError];
|
|
@@ -390,6 +455,49 @@ const simplified = match(status, {
|
|
|
390
455
|
});
|
|
391
456
|
```
|
|
392
457
|
|
|
458
|
+
### Matching Mixed Primitive + Object Unions
|
|
459
|
+
|
|
460
|
+
Unions that combine primitive strings and object variants (common in Rust-style error types):
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
type ServiceError =
|
|
464
|
+
| "ConnectionFailed"
|
|
465
|
+
| "InvalidConfiguration"
|
|
466
|
+
| { Other: [string, string] };
|
|
467
|
+
|
|
468
|
+
const err: ServiceError = { Other: ["reason", "detail"] };
|
|
469
|
+
|
|
470
|
+
// Exhaustive - all cases required
|
|
471
|
+
const message = match(err, {
|
|
472
|
+
ConnectionFailed: () => "Connection failed",
|
|
473
|
+
InvalidConfiguration: () => "Invalid config",
|
|
474
|
+
Other: (data) => `Other error: ${data[0]} - ${data[1]}`,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// With default - partial cases allowed
|
|
478
|
+
const simplified = match(err, {
|
|
479
|
+
ConnectionFailed: () => "Connection issue",
|
|
480
|
+
default: () => "Something else happened",
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Also works with complex object properties:
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
type AppError =
|
|
488
|
+
| "NotFound"
|
|
489
|
+
| { Details: { code: number; message: string } }
|
|
490
|
+
| { Metadata: string[] };
|
|
491
|
+
|
|
492
|
+
const error: AppError = { Details: { code: 404, message: "Not found" } };
|
|
493
|
+
|
|
494
|
+
const result = match(error, {
|
|
495
|
+
NotFound: () => "Not found",
|
|
496
|
+
Details: (details) => `Error ${details.code}: ${details.message}`,
|
|
497
|
+
Metadata: (meta) => meta.join(", "),
|
|
498
|
+
});
|
|
499
|
+
```
|
|
500
|
+
|
|
393
501
|
### Matching Discriminated Unions
|
|
394
502
|
|
|
395
503
|
```typescript
|
|
@@ -451,13 +559,12 @@ const result = await fetchUser(123);
|
|
|
451
559
|
|
|
452
560
|
const message = match(result, {
|
|
453
561
|
Ok: (user) => `Welcome, ${user.name}!`,
|
|
454
|
-
Err: (error) =>
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
},
|
|
562
|
+
Err: (error) =>
|
|
563
|
+
match(error, {
|
|
564
|
+
NOT_FOUND: () => "User not found",
|
|
565
|
+
UNAUTHORIZED: () => "Please login",
|
|
566
|
+
SERVER_ERROR: () => "Server error, try again",
|
|
567
|
+
}),
|
|
461
568
|
});
|
|
462
569
|
```
|
|
463
570
|
|
|
@@ -602,6 +709,18 @@ match<T extends string | number | symbol, R>(
|
|
|
602
709
|
cases: { [K in T]?: () => R } & { default: () => R }
|
|
603
710
|
): R
|
|
604
711
|
|
|
712
|
+
// Mixed primitive + object union (exhaustive)
|
|
713
|
+
match<T extends PropertyKey | object, R>(
|
|
714
|
+
matcher: T,
|
|
715
|
+
cases: MatchCases<T, R, false>
|
|
716
|
+
): R
|
|
717
|
+
|
|
718
|
+
// Mixed primitive + object union (with default)
|
|
719
|
+
match<T extends PropertyKey | object, R>(
|
|
720
|
+
matcher: T,
|
|
721
|
+
cases: MatchCases<T, R, true>
|
|
722
|
+
): R
|
|
723
|
+
|
|
605
724
|
// Discriminated union matching
|
|
606
725
|
match<T, D extends keyof T, R>(
|
|
607
726
|
matcher: T,
|
package/dist/helpers/match.cjs
CHANGED
|
@@ -1,63 +1,45 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/helpers/match.ts
|
|
4
|
+
var TAG = Symbol.for("@consolidados/results.tag");
|
|
4
5
|
function match(matcher, cases, discriminant) {
|
|
5
|
-
|
|
6
|
+
const t = typeof matcher;
|
|
7
|
+
if (t === "string" || t === "number" || t === "symbol") {
|
|
6
8
|
const handler = cases[matcher];
|
|
7
|
-
if (handler)
|
|
9
|
+
if (handler) return handler();
|
|
10
|
+
if (cases.default) return cases.default();
|
|
11
|
+
throw new Error(`No case found for value: ${String(matcher)}`);
|
|
12
|
+
}
|
|
13
|
+
if (matcher !== null && t === "object") {
|
|
14
|
+
const tag = matcher[TAG];
|
|
15
|
+
if (tag !== void 0) {
|
|
16
|
+
const handler = cases[tag];
|
|
17
|
+
if (!handler) throw new Error(`Missing case for ${tag}`);
|
|
18
|
+
if (tag === "Ok" || tag === "Some") return handler(matcher.unwrap());
|
|
19
|
+
if (tag === "Err") return handler(matcher.unwrapErr());
|
|
8
20
|
return handler();
|
|
9
21
|
}
|
|
10
|
-
if (
|
|
11
|
-
|
|
22
|
+
if (discriminant) {
|
|
23
|
+
const dv = matcher[discriminant];
|
|
24
|
+
const handler = cases[dv];
|
|
25
|
+
if (handler) return handler(matcher);
|
|
26
|
+
if (cases.default) return cases.default(matcher);
|
|
27
|
+
throw new Error(`No case found for discriminant value: ${String(dv)}`);
|
|
12
28
|
}
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
if (typeof matcher === "object" && matcher !== null && !discriminant) {
|
|
16
|
-
for (const key in cases) {
|
|
29
|
+
for (const key in matcher) {
|
|
17
30
|
if (key === "default") continue;
|
|
18
|
-
if (key in
|
|
31
|
+
if (key in cases) {
|
|
19
32
|
const handler = cases[key];
|
|
20
33
|
if (handler) {
|
|
21
34
|
return typeof handler === "function" ? handler(matcher[key]) : handler();
|
|
22
35
|
}
|
|
23
36
|
}
|
|
24
37
|
}
|
|
25
|
-
if (cases.default)
|
|
26
|
-
return cases.default();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (discriminant && typeof matcher === "object" && matcher !== null) {
|
|
30
|
-
const discriminantValue = matcher[discriminant];
|
|
31
|
-
const handler = cases[discriminantValue];
|
|
32
|
-
if (handler) {
|
|
33
|
-
return handler(matcher);
|
|
34
|
-
}
|
|
35
|
-
if (cases.default) {
|
|
36
|
-
return cases.default(matcher);
|
|
37
|
-
}
|
|
38
|
-
throw new Error(
|
|
39
|
-
`No case found for discriminant value: ${String(discriminantValue)}`
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
if (typeof matcher === "object" && matcher !== null && "isOk" in matcher && matcher.isOk()) {
|
|
43
|
-
if (!cases.Ok) throw new Error("Missing case for Ok");
|
|
44
|
-
return cases.Ok(matcher.unwrap());
|
|
45
|
-
}
|
|
46
|
-
if (typeof matcher === "object" && matcher !== null && "isErr" in matcher && matcher.isErr()) {
|
|
47
|
-
if (!cases.Err) throw new Error("Missing case for Err");
|
|
48
|
-
return cases.Err(matcher.unwrapErr());
|
|
49
|
-
}
|
|
50
|
-
if (typeof matcher === "object" && matcher !== null && "isSome" in matcher && matcher.isSome()) {
|
|
51
|
-
if (!cases.Some) throw new Error("Missing case for Some");
|
|
52
|
-
return cases.Some(matcher.unwrap());
|
|
53
|
-
}
|
|
54
|
-
if (typeof matcher === "object" && matcher !== null && "isNone" in matcher && matcher.isNone()) {
|
|
55
|
-
if (!cases.None) throw new Error("Missing case for None");
|
|
56
|
-
return cases.None();
|
|
38
|
+
if (cases.default) return cases.default();
|
|
57
39
|
}
|
|
58
40
|
throw new Error("Invalid matcher or missing case");
|
|
59
41
|
}
|
|
60
|
-
|
|
42
|
+
globalThis.match = match;
|
|
61
43
|
|
|
62
44
|
exports.match = match;
|
|
63
45
|
//# sourceMappingURL=match.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/match.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/match.ts"],"names":[],"mappings":";;;AAKA,IAAM,GAAA,GAAM,MAAO,CAAA,GAAA,CAAI,2BAA2B,CAAA;AA+F3C,SAAS,KAAA,CACd,OACA,EAAA,KAAA,EACA,YACG,EAAA;AAEH,EAAA,MAAM,IAAI,OAAO,OAAA;AACjB,EAAA,IAAI,CAAM,KAAA,QAAA,IAAY,CAAM,KAAA,QAAA,IAAY,MAAM,QAAU,EAAA;AACtD,IAAM,MAAA,OAAA,GAAU,MAAM,OAAO,CAAA;AAC7B,IAAI,IAAA,OAAA,SAAgB,OAAQ,EAAA;AAC5B,IAAA,IAAI,KAAM,CAAA,OAAA,EAAgB,OAAA,KAAA,CAAM,OAAQ,EAAA;AACxC,IAAA,MAAM,IAAI,KAAM,CAAA,CAAA,yBAAA,EAA4B,MAAO,CAAA,OAAO,CAAC,CAAE,CAAA,CAAA;AAAA;AAG/D,EAAI,IAAA,OAAA,KAAY,IAAQ,IAAA,CAAA,KAAM,QAAU,EAAA;AAKtC,IAAM,MAAA,GAAA,GAAM,QAAQ,GAAG,CAAA;AACvB,IAAA,IAAI,QAAQ,MAAW,EAAA;AACrB,MAAM,MAAA,OAAA,GAAU,MAAM,GAAG,CAAA;AACzB,MAAA,IAAI,CAAC,OAAS,EAAA,MAAM,IAAI,KAAM,CAAA,CAAA,iBAAA,EAAoB,GAAG,CAAE,CAAA,CAAA;AACvD,MAAI,IAAA,GAAA,KAAQ,QAAQ,GAAQ,KAAA,MAAA,SAAe,OAAQ,CAAA,OAAA,CAAQ,QAAQ,CAAA;AACnE,MAAA,IAAI,QAAQ,KAAO,EAAA,OAAO,OAAQ,CAAA,OAAA,CAAQ,WAAgB,CAAA;AAE1D,MAAA,OAAO,OAAQ,EAAA;AAAA;AAIjB,IAAA,IAAI,YAAc,EAAA;AAChB,MAAM,MAAA,EAAA,GAAK,QAAQ,YAAY,CAAA;AAC/B,MAAM,MAAA,OAAA,GAAU,MAAM,EAAE,CAAA;AACxB,MAAI,IAAA,OAAA,EAAgB,OAAA,OAAA,CAAQ,OAAO,CAAA;AACnC,MAAA,IAAI,KAAM,CAAA,OAAA,EAAgB,OAAA,KAAA,CAAM,QAAQ,OAAO,CAAA;AAC/C,MAAA,MAAM,IAAI,KAAM,CAAA,CAAA,sCAAA,EAAyC,MAAO,CAAA,EAAE,CAAC,CAAE,CAAA,CAAA;AAAA;AAOvE,IAAA,KAAA,MAAW,OAAO,OAAS,EAAA;AACzB,MAAA,IAAI,QAAQ,SAAW,EAAA;AACvB,MAAA,IAAI,OAAO,KAAO,EAAA;AAChB,QAAM,MAAA,OAAA,GAAU,MAAM,GAAG,CAAA;AACzB,QAAA,IAAI,OAAS,EAAA;AACX,UAAO,OAAA,OAAO,YAAY,UACtB,GAAA,OAAA,CAAQ,QAAQ,GAAG,CAAC,IACpB,OAAQ,EAAA;AAAA;AACd;AACF;AAEF,IAAA,IAAI,KAAM,CAAA,OAAA,EAAgB,OAAA,KAAA,CAAM,OAAQ,EAAA;AAAA;AAG1C,EAAM,MAAA,IAAI,MAAM,iCAAiC,CAAA;AACnD;AAGC,UAAA,CAAmB,KAAQ,GAAA,KAAA","file":"match.cjs","sourcesContent":["import type { Option } from \"../option\";\nimport type { Result } from \"../result\";\n\n// Hidden discriminant tag — set by Ok/Err/Some/None instances for O(1) dispatch.\n// Same Symbol as the one declared in the variant classes via Symbol.for.\nconst TAG = Symbol.for(\"@consolidados/results.tag\");\n\n// Utility types for mixed primitive + object unions\ntype PrimitiveMembers<T> = Extract<T, PropertyKey>;\ntype ObjectKeys<T> = T extends object ? keyof T : never;\ntype ObjectPropertyType<T, K extends PropertyKey> = T extends object\n ? K extends keyof T\n ? T[K]\n : never\n : never;\n\n// Helper to determine if a key K is a primitive member or object key\ntype HandlerFor<T, K extends PropertyKey, R> = K extends PrimitiveMembers<T>\n ? () => R\n : K extends ObjectKeys<T>\n ? (value: ObjectPropertyType<T, K>) => R\n : never;\n\n// Build cases type with a single mapped type\ntype MatchCases<T, R, HasDefault extends boolean = false> = (HasDefault extends true\n ? Partial<{\n [K in PrimitiveMembers<T> | ObjectKeys<T>]: HandlerFor<T, K, R>;\n }>\n : {\n [K in PrimitiveMembers<T> | ObjectKeys<T>]: HandlerFor<T, K, R>;\n }) &\n (HasDefault extends true ? { default: () => R } : {});\n\n// Overload for Result type\nexport function match<T, E, ROk, RErr>(\n matcher: Result<T, E>,\n cases: {\n Ok: (value: T) => ROk;\n Err: (error: E) => RErr;\n },\n): ROk | RErr;\n\n// Overload for Option type\nexport function match<T, RSome, RNone>(\n matcher: Option<T>,\n cases: {\n Some: (value: T) => RSome;\n None: () => RNone;\n },\n): RSome | RNone;\n\n// Overload for mixed primitive + object unions WITH default (cases optional)\nexport function match<T extends PropertyKey | object, R>(\n matcher: T,\n cases: MatchCases<T, R, true>,\n): R;\n\n// Overload for mixed primitive + object unions WITHOUT default (exhaustive)\nexport function match<T extends PropertyKey | object, R>(\n matcher: T,\n cases: MatchCases<T, R, false>,\n): R;\n\n// Overload for discriminated unions with default case\nexport function match<\n T extends { [K in D]: string | number | symbol },\n D extends keyof T,\n R,\n>(\n matcher: T,\n cases: { [K in T[D]]?: (value: Extract<T, { [P in D]: K }>) => R } & {\n default: (value: T) => R;\n },\n discriminant: D,\n): R;\n\n// Overload for discriminated unions without default case (exhaustive)\nexport function match<\n T extends { [K in D]: string | number | symbol },\n D extends keyof T,\n R,\n>(\n matcher: T,\n cases: { [K in T[D]]: (value: Extract<T, { [P in D]: K }>) => R },\n discriminant: D,\n): R;\n\n// Overload for primitives with default case\nexport function match<T extends PropertyKey, R>(\n matcher: T,\n cases: Partial<Record<T, () => R>> & { default: () => R },\n): R;\n\n// Overload for primitives without default case (exhaustive)\nexport function match<T extends PropertyKey, R>(\n matcher: T,\n cases: Record<T, () => R>,\n): R;\n\n// Implementation\nexport function match<T, E, R>(\n matcher: Result<T, E> | Option<T> | any,\n cases: any,\n discriminant?: keyof any,\n): R {\n // 1) Primitives (string/number/symbol) — direct key lookup, O(1).\n const t = typeof matcher;\n if (t === \"string\" || t === \"number\" || t === \"symbol\") {\n const handler = cases[matcher];\n if (handler) return handler();\n if (cases.default) return cases.default();\n throw new Error(`No case found for value: ${String(matcher)}`);\n }\n\n if (matcher !== null && t === \"object\") {\n // 2) Tagged variant (Ok/Err/Some/None) — single Symbol read + single lookup.\n // The Symbol property is invisible to `for...in`, `Object.keys`, and\n // `JSON.stringify`, so mixed unions with plain-object variants are\n // unaffected.\n const tag = matcher[TAG];\n if (tag !== undefined) {\n const handler = cases[tag];\n if (!handler) throw new Error(`Missing case for ${tag}`);\n if (tag === \"Ok\" || tag === \"Some\") return handler(matcher.unwrap());\n if (tag === \"Err\") return handler(matcher.unwrapErr() as E);\n // tag === \"None\"\n return handler();\n }\n\n // 3) Discriminated union via explicit `discriminant` arg — O(1).\n if (discriminant) {\n const dv = matcher[discriminant];\n const handler = cases[dv];\n if (handler) return handler(matcher);\n if (cases.default) return cases.default(matcher);\n throw new Error(`No case found for discriminant value: ${String(dv)}`);\n }\n\n // 4) Mixed primitive + object union — iterate matcher's own enumerable\n // keys (usually 1 for tagged variant objects like `{ Other: [...] }`)\n // and look up by key in cases. This flips the loop from O(cases)\n // to O(matcher-keys), which is effectively O(1) for tagged variants.\n for (const key in matcher) {\n if (key === \"default\") continue;\n if (key in cases) {\n const handler = cases[key];\n if (handler) {\n return typeof handler === \"function\"\n ? handler(matcher[key])\n : handler();\n }\n }\n }\n if (cases.default) return cases.default();\n }\n\n throw new Error(\"Invalid matcher or missing case\");\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: <explanation>\n(globalThis as any).match = match;\n"]}
|
package/dist/helpers/match.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as Result, O as Option } from '../option-
|
|
1
|
+
import { R as Result, O as Option } from '../option-CYCiGxtw.cjs';
|
|
2
2
|
|
|
3
3
|
type PrimitiveMembers<T> = Extract<T, PropertyKey>;
|
|
4
4
|
type ObjectKeys<T> = T extends object ? keyof T : never;
|
|
@@ -11,14 +11,14 @@ type MatchCases<T, R, HasDefault extends boolean = false> = (HasDefault extends
|
|
|
11
11
|
}) & (HasDefault extends true ? {
|
|
12
12
|
default: () => R;
|
|
13
13
|
} : {});
|
|
14
|
-
declare function match<T, E,
|
|
15
|
-
Ok: (value: T) =>
|
|
16
|
-
Err: (error: E) =>
|
|
17
|
-
}):
|
|
18
|
-
declare function match<T,
|
|
19
|
-
Some: (value: T) =>
|
|
20
|
-
None: () =>
|
|
21
|
-
}):
|
|
14
|
+
declare function match<T, E, ROk, RErr>(matcher: Result<T, E>, cases: {
|
|
15
|
+
Ok: (value: T) => ROk;
|
|
16
|
+
Err: (error: E) => RErr;
|
|
17
|
+
}): ROk | RErr;
|
|
18
|
+
declare function match<T, RSome, RNone>(matcher: Option<T>, cases: {
|
|
19
|
+
Some: (value: T) => RSome;
|
|
20
|
+
None: () => RNone;
|
|
21
|
+
}): RSome | RNone;
|
|
22
22
|
declare function match<T extends PropertyKey | object, R>(matcher: T, cases: MatchCases<T, R, true>): R;
|
|
23
23
|
declare function match<T extends PropertyKey | object, R>(matcher: T, cases: MatchCases<T, R, false>): R;
|
|
24
24
|
declare function match<T extends {
|
|
@@ -41,13 +41,5 @@ declare function match<T extends PropertyKey, R>(matcher: T, cases: Partial<Reco
|
|
|
41
41
|
default: () => R;
|
|
42
42
|
}): R;
|
|
43
43
|
declare function match<T extends PropertyKey, R>(matcher: T, cases: Record<T, () => R>): R;
|
|
44
|
-
declare function match<T, E>(matcher: Result<T, E>, cases: {
|
|
45
|
-
Ok: (value: T) => Option<T>;
|
|
46
|
-
Err: (error: E) => Option<T>;
|
|
47
|
-
}): Option<T>;
|
|
48
|
-
declare function match<T, E>(matcher: Option<T>, cases: {
|
|
49
|
-
Some: (value: T) => Result<T, E>;
|
|
50
|
-
None: () => Result<T, E>;
|
|
51
|
-
}): Result<T, E>;
|
|
52
44
|
|
|
53
45
|
export { match };
|
package/dist/helpers/match.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as Result, O as Option } from '../option-
|
|
1
|
+
import { R as Result, O as Option } from '../option-CYCiGxtw.js';
|
|
2
2
|
|
|
3
3
|
type PrimitiveMembers<T> = Extract<T, PropertyKey>;
|
|
4
4
|
type ObjectKeys<T> = T extends object ? keyof T : never;
|
|
@@ -11,14 +11,14 @@ type MatchCases<T, R, HasDefault extends boolean = false> = (HasDefault extends
|
|
|
11
11
|
}) & (HasDefault extends true ? {
|
|
12
12
|
default: () => R;
|
|
13
13
|
} : {});
|
|
14
|
-
declare function match<T, E,
|
|
15
|
-
Ok: (value: T) =>
|
|
16
|
-
Err: (error: E) =>
|
|
17
|
-
}):
|
|
18
|
-
declare function match<T,
|
|
19
|
-
Some: (value: T) =>
|
|
20
|
-
None: () =>
|
|
21
|
-
}):
|
|
14
|
+
declare function match<T, E, ROk, RErr>(matcher: Result<T, E>, cases: {
|
|
15
|
+
Ok: (value: T) => ROk;
|
|
16
|
+
Err: (error: E) => RErr;
|
|
17
|
+
}): ROk | RErr;
|
|
18
|
+
declare function match<T, RSome, RNone>(matcher: Option<T>, cases: {
|
|
19
|
+
Some: (value: T) => RSome;
|
|
20
|
+
None: () => RNone;
|
|
21
|
+
}): RSome | RNone;
|
|
22
22
|
declare function match<T extends PropertyKey | object, R>(matcher: T, cases: MatchCases<T, R, true>): R;
|
|
23
23
|
declare function match<T extends PropertyKey | object, R>(matcher: T, cases: MatchCases<T, R, false>): R;
|
|
24
24
|
declare function match<T extends {
|
|
@@ -41,13 +41,5 @@ declare function match<T extends PropertyKey, R>(matcher: T, cases: Partial<Reco
|
|
|
41
41
|
default: () => R;
|
|
42
42
|
}): R;
|
|
43
43
|
declare function match<T extends PropertyKey, R>(matcher: T, cases: Record<T, () => R>): R;
|
|
44
|
-
declare function match<T, E>(matcher: Result<T, E>, cases: {
|
|
45
|
-
Ok: (value: T) => Option<T>;
|
|
46
|
-
Err: (error: E) => Option<T>;
|
|
47
|
-
}): Option<T>;
|
|
48
|
-
declare function match<T, E>(matcher: Option<T>, cases: {
|
|
49
|
-
Some: (value: T) => Result<T, E>;
|
|
50
|
-
None: () => Result<T, E>;
|
|
51
|
-
}): Result<T, E>;
|
|
52
44
|
|
|
53
45
|
export { match };
|
package/dist/helpers/match.js
CHANGED
|
@@ -1,61 +1,43 @@
|
|
|
1
1
|
// src/helpers/match.ts
|
|
2
|
+
var TAG = Symbol.for("@consolidados/results.tag");
|
|
2
3
|
function match(matcher, cases, discriminant) {
|
|
3
|
-
|
|
4
|
+
const t = typeof matcher;
|
|
5
|
+
if (t === "string" || t === "number" || t === "symbol") {
|
|
4
6
|
const handler = cases[matcher];
|
|
5
|
-
if (handler)
|
|
7
|
+
if (handler) return handler();
|
|
8
|
+
if (cases.default) return cases.default();
|
|
9
|
+
throw new Error(`No case found for value: ${String(matcher)}`);
|
|
10
|
+
}
|
|
11
|
+
if (matcher !== null && t === "object") {
|
|
12
|
+
const tag = matcher[TAG];
|
|
13
|
+
if (tag !== void 0) {
|
|
14
|
+
const handler = cases[tag];
|
|
15
|
+
if (!handler) throw new Error(`Missing case for ${tag}`);
|
|
16
|
+
if (tag === "Ok" || tag === "Some") return handler(matcher.unwrap());
|
|
17
|
+
if (tag === "Err") return handler(matcher.unwrapErr());
|
|
6
18
|
return handler();
|
|
7
19
|
}
|
|
8
|
-
if (
|
|
9
|
-
|
|
20
|
+
if (discriminant) {
|
|
21
|
+
const dv = matcher[discriminant];
|
|
22
|
+
const handler = cases[dv];
|
|
23
|
+
if (handler) return handler(matcher);
|
|
24
|
+
if (cases.default) return cases.default(matcher);
|
|
25
|
+
throw new Error(`No case found for discriminant value: ${String(dv)}`);
|
|
10
26
|
}
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
if (typeof matcher === "object" && matcher !== null && !discriminant) {
|
|
14
|
-
for (const key in cases) {
|
|
27
|
+
for (const key in matcher) {
|
|
15
28
|
if (key === "default") continue;
|
|
16
|
-
if (key in
|
|
29
|
+
if (key in cases) {
|
|
17
30
|
const handler = cases[key];
|
|
18
31
|
if (handler) {
|
|
19
32
|
return typeof handler === "function" ? handler(matcher[key]) : handler();
|
|
20
33
|
}
|
|
21
34
|
}
|
|
22
35
|
}
|
|
23
|
-
if (cases.default)
|
|
24
|
-
return cases.default();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
if (discriminant && typeof matcher === "object" && matcher !== null) {
|
|
28
|
-
const discriminantValue = matcher[discriminant];
|
|
29
|
-
const handler = cases[discriminantValue];
|
|
30
|
-
if (handler) {
|
|
31
|
-
return handler(matcher);
|
|
32
|
-
}
|
|
33
|
-
if (cases.default) {
|
|
34
|
-
return cases.default(matcher);
|
|
35
|
-
}
|
|
36
|
-
throw new Error(
|
|
37
|
-
`No case found for discriminant value: ${String(discriminantValue)}`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
if (typeof matcher === "object" && matcher !== null && "isOk" in matcher && matcher.isOk()) {
|
|
41
|
-
if (!cases.Ok) throw new Error("Missing case for Ok");
|
|
42
|
-
return cases.Ok(matcher.unwrap());
|
|
43
|
-
}
|
|
44
|
-
if (typeof matcher === "object" && matcher !== null && "isErr" in matcher && matcher.isErr()) {
|
|
45
|
-
if (!cases.Err) throw new Error("Missing case for Err");
|
|
46
|
-
return cases.Err(matcher.unwrapErr());
|
|
47
|
-
}
|
|
48
|
-
if (typeof matcher === "object" && matcher !== null && "isSome" in matcher && matcher.isSome()) {
|
|
49
|
-
if (!cases.Some) throw new Error("Missing case for Some");
|
|
50
|
-
return cases.Some(matcher.unwrap());
|
|
51
|
-
}
|
|
52
|
-
if (typeof matcher === "object" && matcher !== null && "isNone" in matcher && matcher.isNone()) {
|
|
53
|
-
if (!cases.None) throw new Error("Missing case for None");
|
|
54
|
-
return cases.None();
|
|
36
|
+
if (cases.default) return cases.default();
|
|
55
37
|
}
|
|
56
38
|
throw new Error("Invalid matcher or missing case");
|
|
57
39
|
}
|
|
58
|
-
|
|
40
|
+
globalThis.match = match;
|
|
59
41
|
|
|
60
42
|
export { match };
|
|
61
43
|
//# sourceMappingURL=match.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/match.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/match.ts"],"names":[],"mappings":";AAKA,IAAM,GAAA,GAAM,MAAO,CAAA,GAAA,CAAI,2BAA2B,CAAA;AA+F3C,SAAS,KAAA,CACd,OACA,EAAA,KAAA,EACA,YACG,EAAA;AAEH,EAAA,MAAM,IAAI,OAAO,OAAA;AACjB,EAAA,IAAI,CAAM,KAAA,QAAA,IAAY,CAAM,KAAA,QAAA,IAAY,MAAM,QAAU,EAAA;AACtD,IAAM,MAAA,OAAA,GAAU,MAAM,OAAO,CAAA;AAC7B,IAAI,IAAA,OAAA,SAAgB,OAAQ,EAAA;AAC5B,IAAA,IAAI,KAAM,CAAA,OAAA,EAAgB,OAAA,KAAA,CAAM,OAAQ,EAAA;AACxC,IAAA,MAAM,IAAI,KAAM,CAAA,CAAA,yBAAA,EAA4B,MAAO,CAAA,OAAO,CAAC,CAAE,CAAA,CAAA;AAAA;AAG/D,EAAI,IAAA,OAAA,KAAY,IAAQ,IAAA,CAAA,KAAM,QAAU,EAAA;AAKtC,IAAM,MAAA,GAAA,GAAM,QAAQ,GAAG,CAAA;AACvB,IAAA,IAAI,QAAQ,MAAW,EAAA;AACrB,MAAM,MAAA,OAAA,GAAU,MAAM,GAAG,CAAA;AACzB,MAAA,IAAI,CAAC,OAAS,EAAA,MAAM,IAAI,KAAM,CAAA,CAAA,iBAAA,EAAoB,GAAG,CAAE,CAAA,CAAA;AACvD,MAAI,IAAA,GAAA,KAAQ,QAAQ,GAAQ,KAAA,MAAA,SAAe,OAAQ,CAAA,OAAA,CAAQ,QAAQ,CAAA;AACnE,MAAA,IAAI,QAAQ,KAAO,EAAA,OAAO,OAAQ,CAAA,OAAA,CAAQ,WAAgB,CAAA;AAE1D,MAAA,OAAO,OAAQ,EAAA;AAAA;AAIjB,IAAA,IAAI,YAAc,EAAA;AAChB,MAAM,MAAA,EAAA,GAAK,QAAQ,YAAY,CAAA;AAC/B,MAAM,MAAA,OAAA,GAAU,MAAM,EAAE,CAAA;AACxB,MAAI,IAAA,OAAA,EAAgB,OAAA,OAAA,CAAQ,OAAO,CAAA;AACnC,MAAA,IAAI,KAAM,CAAA,OAAA,EAAgB,OAAA,KAAA,CAAM,QAAQ,OAAO,CAAA;AAC/C,MAAA,MAAM,IAAI,KAAM,CAAA,CAAA,sCAAA,EAAyC,MAAO,CAAA,EAAE,CAAC,CAAE,CAAA,CAAA;AAAA;AAOvE,IAAA,KAAA,MAAW,OAAO,OAAS,EAAA;AACzB,MAAA,IAAI,QAAQ,SAAW,EAAA;AACvB,MAAA,IAAI,OAAO,KAAO,EAAA;AAChB,QAAM,MAAA,OAAA,GAAU,MAAM,GAAG,CAAA;AACzB,QAAA,IAAI,OAAS,EAAA;AACX,UAAO,OAAA,OAAO,YAAY,UACtB,GAAA,OAAA,CAAQ,QAAQ,GAAG,CAAC,IACpB,OAAQ,EAAA;AAAA;AACd;AACF;AAEF,IAAA,IAAI,KAAM,CAAA,OAAA,EAAgB,OAAA,KAAA,CAAM,OAAQ,EAAA;AAAA;AAG1C,EAAM,MAAA,IAAI,MAAM,iCAAiC,CAAA;AACnD;AAGC,UAAA,CAAmB,KAAQ,GAAA,KAAA","file":"match.js","sourcesContent":["import type { Option } from \"../option\";\nimport type { Result } from \"../result\";\n\n// Hidden discriminant tag — set by Ok/Err/Some/None instances for O(1) dispatch.\n// Same Symbol as the one declared in the variant classes via Symbol.for.\nconst TAG = Symbol.for(\"@consolidados/results.tag\");\n\n// Utility types for mixed primitive + object unions\ntype PrimitiveMembers<T> = Extract<T, PropertyKey>;\ntype ObjectKeys<T> = T extends object ? keyof T : never;\ntype ObjectPropertyType<T, K extends PropertyKey> = T extends object\n ? K extends keyof T\n ? T[K]\n : never\n : never;\n\n// Helper to determine if a key K is a primitive member or object key\ntype HandlerFor<T, K extends PropertyKey, R> = K extends PrimitiveMembers<T>\n ? () => R\n : K extends ObjectKeys<T>\n ? (value: ObjectPropertyType<T, K>) => R\n : never;\n\n// Build cases type with a single mapped type\ntype MatchCases<T, R, HasDefault extends boolean = false> = (HasDefault extends true\n ? Partial<{\n [K in PrimitiveMembers<T> | ObjectKeys<T>]: HandlerFor<T, K, R>;\n }>\n : {\n [K in PrimitiveMembers<T> | ObjectKeys<T>]: HandlerFor<T, K, R>;\n }) &\n (HasDefault extends true ? { default: () => R } : {});\n\n// Overload for Result type\nexport function match<T, E, ROk, RErr>(\n matcher: Result<T, E>,\n cases: {\n Ok: (value: T) => ROk;\n Err: (error: E) => RErr;\n },\n): ROk | RErr;\n\n// Overload for Option type\nexport function match<T, RSome, RNone>(\n matcher: Option<T>,\n cases: {\n Some: (value: T) => RSome;\n None: () => RNone;\n },\n): RSome | RNone;\n\n// Overload for mixed primitive + object unions WITH default (cases optional)\nexport function match<T extends PropertyKey | object, R>(\n matcher: T,\n cases: MatchCases<T, R, true>,\n): R;\n\n// Overload for mixed primitive + object unions WITHOUT default (exhaustive)\nexport function match<T extends PropertyKey | object, R>(\n matcher: T,\n cases: MatchCases<T, R, false>,\n): R;\n\n// Overload for discriminated unions with default case\nexport function match<\n T extends { [K in D]: string | number | symbol },\n D extends keyof T,\n R,\n>(\n matcher: T,\n cases: { [K in T[D]]?: (value: Extract<T, { [P in D]: K }>) => R } & {\n default: (value: T) => R;\n },\n discriminant: D,\n): R;\n\n// Overload for discriminated unions without default case (exhaustive)\nexport function match<\n T extends { [K in D]: string | number | symbol },\n D extends keyof T,\n R,\n>(\n matcher: T,\n cases: { [K in T[D]]: (value: Extract<T, { [P in D]: K }>) => R },\n discriminant: D,\n): R;\n\n// Overload for primitives with default case\nexport function match<T extends PropertyKey, R>(\n matcher: T,\n cases: Partial<Record<T, () => R>> & { default: () => R },\n): R;\n\n// Overload for primitives without default case (exhaustive)\nexport function match<T extends PropertyKey, R>(\n matcher: T,\n cases: Record<T, () => R>,\n): R;\n\n// Implementation\nexport function match<T, E, R>(\n matcher: Result<T, E> | Option<T> | any,\n cases: any,\n discriminant?: keyof any,\n): R {\n // 1) Primitives (string/number/symbol) — direct key lookup, O(1).\n const t = typeof matcher;\n if (t === \"string\" || t === \"number\" || t === \"symbol\") {\n const handler = cases[matcher];\n if (handler) return handler();\n if (cases.default) return cases.default();\n throw new Error(`No case found for value: ${String(matcher)}`);\n }\n\n if (matcher !== null && t === \"object\") {\n // 2) Tagged variant (Ok/Err/Some/None) — single Symbol read + single lookup.\n // The Symbol property is invisible to `for...in`, `Object.keys`, and\n // `JSON.stringify`, so mixed unions with plain-object variants are\n // unaffected.\n const tag = matcher[TAG];\n if (tag !== undefined) {\n const handler = cases[tag];\n if (!handler) throw new Error(`Missing case for ${tag}`);\n if (tag === \"Ok\" || tag === \"Some\") return handler(matcher.unwrap());\n if (tag === \"Err\") return handler(matcher.unwrapErr() as E);\n // tag === \"None\"\n return handler();\n }\n\n // 3) Discriminated union via explicit `discriminant` arg — O(1).\n if (discriminant) {\n const dv = matcher[discriminant];\n const handler = cases[dv];\n if (handler) return handler(matcher);\n if (cases.default) return cases.default(matcher);\n throw new Error(`No case found for discriminant value: ${String(dv)}`);\n }\n\n // 4) Mixed primitive + object union — iterate matcher's own enumerable\n // keys (usually 1 for tagged variant objects like `{ Other: [...] }`)\n // and look up by key in cases. This flips the loop from O(cases)\n // to O(matcher-keys), which is effectively O(1) for tagged variants.\n for (const key in matcher) {\n if (key === \"default\") continue;\n if (key in cases) {\n const handler = cases[key];\n if (handler) {\n return typeof handler === \"function\"\n ? handler(matcher[key])\n : handler();\n }\n }\n }\n if (cases.default) return cases.default();\n }\n\n throw new Error(\"Invalid matcher or missing case\");\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: <explanation>\n(globalThis as any).match = match;\n"]}
|