@player-tools/fluent 0.12.1--canary.241.6077

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.
Files changed (134) hide show
  1. package/dist/cjs/index.cjs +2396 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.legacy-esm.js +2276 -0
  4. package/dist/index.mjs +2276 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +38 -0
  7. package/src/core/base-builder/__tests__/fluent-builder-base.test.ts +2423 -0
  8. package/src/core/base-builder/__tests__/fluent-partial.test.ts +179 -0
  9. package/src/core/base-builder/__tests__/id-generator.test.ts +658 -0
  10. package/src/core/base-builder/__tests__/registry.test.ts +534 -0
  11. package/src/core/base-builder/__tests__/resolution-mixed-arrays.test.ts +319 -0
  12. package/src/core/base-builder/__tests__/resolution-pipeline.test.ts +416 -0
  13. package/src/core/base-builder/__tests__/resolution-switches.test.ts +468 -0
  14. package/src/core/base-builder/__tests__/resolution-templates.test.ts +255 -0
  15. package/src/core/base-builder/__tests__/switch.test.ts +815 -0
  16. package/src/core/base-builder/__tests__/template.test.ts +596 -0
  17. package/src/core/base-builder/__tests__/value-extraction.test.ts +200 -0
  18. package/src/core/base-builder/__tests__/value-storage.test.ts +459 -0
  19. package/src/core/base-builder/conditional/index.ts +64 -0
  20. package/src/core/base-builder/context.ts +152 -0
  21. package/src/core/base-builder/errors.ts +69 -0
  22. package/src/core/base-builder/fluent-builder-base.ts +308 -0
  23. package/src/core/base-builder/guards.ts +137 -0
  24. package/src/core/base-builder/id/generator.ts +290 -0
  25. package/src/core/base-builder/id/registry.ts +152 -0
  26. package/src/core/base-builder/index.ts +72 -0
  27. package/src/core/base-builder/resolution/path-resolver.ts +116 -0
  28. package/src/core/base-builder/resolution/pipeline.ts +103 -0
  29. package/src/core/base-builder/resolution/steps/__tests__/nested-asset-wrappers.test.ts +206 -0
  30. package/src/core/base-builder/resolution/steps/asset-id.ts +77 -0
  31. package/src/core/base-builder/resolution/steps/asset-wrappers.ts +64 -0
  32. package/src/core/base-builder/resolution/steps/builders.ts +84 -0
  33. package/src/core/base-builder/resolution/steps/mixed-arrays.ts +95 -0
  34. package/src/core/base-builder/resolution/steps/nested-asset-wrappers.ts +124 -0
  35. package/src/core/base-builder/resolution/steps/static-values.ts +35 -0
  36. package/src/core/base-builder/resolution/steps/switches.ts +71 -0
  37. package/src/core/base-builder/resolution/steps/templates.ts +40 -0
  38. package/src/core/base-builder/resolution/value-resolver.ts +333 -0
  39. package/src/core/base-builder/storage/auxiliary-storage.ts +82 -0
  40. package/src/core/base-builder/storage/value-storage.ts +282 -0
  41. package/src/core/base-builder/types.ts +266 -0
  42. package/src/core/base-builder/utils.ts +10 -0
  43. package/src/core/flow/__tests__/index.test.ts +292 -0
  44. package/src/core/flow/index.ts +118 -0
  45. package/src/core/index.ts +8 -0
  46. package/src/core/mocks/generated/action.builder.ts +92 -0
  47. package/src/core/mocks/generated/choice-item.builder.ts +120 -0
  48. package/src/core/mocks/generated/choice.builder.ts +134 -0
  49. package/src/core/mocks/generated/collection.builder.ts +93 -0
  50. package/src/core/mocks/generated/field-collection.builder.ts +86 -0
  51. package/src/core/mocks/generated/index.ts +10 -0
  52. package/src/core/mocks/generated/info.builder.ts +64 -0
  53. package/src/core/mocks/generated/input.builder.ts +63 -0
  54. package/src/core/mocks/generated/overview-collection.builder.ts +65 -0
  55. package/src/core/mocks/generated/splash-collection.builder.ts +93 -0
  56. package/src/core/mocks/generated/text.builder.ts +47 -0
  57. package/src/core/mocks/index.ts +1 -0
  58. package/src/core/mocks/types/action.ts +92 -0
  59. package/src/core/mocks/types/choice.ts +129 -0
  60. package/src/core/mocks/types/collection.ts +140 -0
  61. package/src/core/mocks/types/info.ts +7 -0
  62. package/src/core/mocks/types/input.ts +7 -0
  63. package/src/core/mocks/types/text.ts +5 -0
  64. package/src/core/schema/__tests__/index.test.ts +127 -0
  65. package/src/core/schema/index.ts +195 -0
  66. package/src/core/schema/types.ts +7 -0
  67. package/src/core/switch/__tests__/index.test.ts +156 -0
  68. package/src/core/switch/index.ts +81 -0
  69. package/src/core/tagged-template/README.md +448 -0
  70. package/src/core/tagged-template/__tests__/extract-bindings-from-schema.test.ts +207 -0
  71. package/src/core/tagged-template/__tests__/index.test.ts +190 -0
  72. package/src/core/tagged-template/__tests__/schema-std-integration.test.ts +580 -0
  73. package/src/core/tagged-template/binding.ts +95 -0
  74. package/src/core/tagged-template/expression.ts +92 -0
  75. package/src/core/tagged-template/extract-bindings-from-schema.ts +120 -0
  76. package/src/core/tagged-template/index.ts +5 -0
  77. package/src/core/tagged-template/std.ts +472 -0
  78. package/src/core/tagged-template/types.ts +123 -0
  79. package/src/core/template/__tests__/index.test.ts +380 -0
  80. package/src/core/template/index.ts +196 -0
  81. package/src/core/utils/index.ts +160 -0
  82. package/src/fp/README.md +411 -0
  83. package/src/fp/__tests__/index.test.ts +1178 -0
  84. package/src/fp/index.ts +386 -0
  85. package/src/gen/common.ts +15 -0
  86. package/src/index.ts +5 -0
  87. package/src/types.ts +203 -0
  88. package/types/core/base-builder/conditional/index.d.ts +21 -0
  89. package/types/core/base-builder/context.d.ts +39 -0
  90. package/types/core/base-builder/errors.d.ts +45 -0
  91. package/types/core/base-builder/fluent-builder-base.d.ts +147 -0
  92. package/types/core/base-builder/guards.d.ts +58 -0
  93. package/types/core/base-builder/id/generator.d.ts +69 -0
  94. package/types/core/base-builder/id/registry.d.ts +93 -0
  95. package/types/core/base-builder/index.d.ts +9 -0
  96. package/types/core/base-builder/resolution/path-resolver.d.ts +15 -0
  97. package/types/core/base-builder/resolution/pipeline.d.ts +27 -0
  98. package/types/core/base-builder/resolution/steps/asset-id.d.ts +14 -0
  99. package/types/core/base-builder/resolution/steps/asset-wrappers.d.ts +14 -0
  100. package/types/core/base-builder/resolution/steps/builders.d.ts +14 -0
  101. package/types/core/base-builder/resolution/steps/mixed-arrays.d.ts +14 -0
  102. package/types/core/base-builder/resolution/steps/nested-asset-wrappers.d.ts +14 -0
  103. package/types/core/base-builder/resolution/steps/static-values.d.ts +14 -0
  104. package/types/core/base-builder/resolution/steps/switches.d.ts +15 -0
  105. package/types/core/base-builder/resolution/steps/templates.d.ts +14 -0
  106. package/types/core/base-builder/resolution/value-resolver.d.ts +62 -0
  107. package/types/core/base-builder/storage/auxiliary-storage.d.ts +50 -0
  108. package/types/core/base-builder/storage/value-storage.d.ts +82 -0
  109. package/types/core/base-builder/types.d.ts +183 -0
  110. package/types/core/base-builder/utils.d.ts +2 -0
  111. package/types/core/flow/index.d.ts +23 -0
  112. package/types/core/index.d.ts +8 -0
  113. package/types/core/mocks/index.d.ts +2 -0
  114. package/types/core/mocks/types/action.d.ts +58 -0
  115. package/types/core/mocks/types/choice.d.ts +95 -0
  116. package/types/core/mocks/types/collection.d.ts +102 -0
  117. package/types/core/mocks/types/info.d.ts +7 -0
  118. package/types/core/mocks/types/input.d.ts +7 -0
  119. package/types/core/mocks/types/text.d.ts +5 -0
  120. package/types/core/schema/index.d.ts +34 -0
  121. package/types/core/schema/types.d.ts +5 -0
  122. package/types/core/switch/index.d.ts +21 -0
  123. package/types/core/tagged-template/binding.d.ts +19 -0
  124. package/types/core/tagged-template/expression.d.ts +11 -0
  125. package/types/core/tagged-template/extract-bindings-from-schema.d.ts +7 -0
  126. package/types/core/tagged-template/index.d.ts +6 -0
  127. package/types/core/tagged-template/std.d.ts +174 -0
  128. package/types/core/tagged-template/types.d.ts +69 -0
  129. package/types/core/template/index.d.ts +97 -0
  130. package/types/core/utils/index.d.ts +47 -0
  131. package/types/fp/index.d.ts +149 -0
  132. package/types/gen/common.d.ts +6 -0
  133. package/types/index.d.ts +3 -0
  134. package/types/types.d.ts +163 -0
@@ -0,0 +1,160 @@
1
+ import { isTaggedTemplateValue, TaggedTemplateValue } from "../tagged-template";
2
+
3
+ /**
4
+ * Type that can be either a direct value T or a TaggedTemplateValue
5
+ */
6
+ export type TaggedOr<T> = T | TaggedTemplateValue;
7
+
8
+ /**
9
+ * Safely extract a string if TaggedTemplateValue is present
10
+ */
11
+ export function safeToString<T extends TaggedOr<string | unknown>>(
12
+ value: T,
13
+ ): string {
14
+ if (isTaggedTemplateValue(value)) {
15
+ return value.toString();
16
+ }
17
+ return String(value);
18
+ }
19
+
20
+ /**
21
+ * Safely extract a boolean if TaggedTemplateValue is present
22
+ */
23
+ export function safeToBoolean<T extends TaggedOr<boolean | unknown>>(
24
+ value: T,
25
+ ): boolean {
26
+ if (isTaggedTemplateValue(value)) {
27
+ return value.toString() === "true";
28
+ }
29
+ return Boolean(value);
30
+ }
31
+
32
+ /**
33
+ * Safely extract a number if TaggedTemplateValue is present
34
+ */
35
+ export function safeToNumber<T extends TaggedOr<number | unknown>>(
36
+ value: T,
37
+ ): number {
38
+ if (isTaggedTemplateValue(value)) {
39
+ return Number(value.toString());
40
+ }
41
+ return Number(value);
42
+ }
43
+
44
+ /**
45
+ * Type for an item that could be in an array with TaggedTemplate values
46
+ */
47
+ export type ArrayItem<T> = T extends (infer U)[] ? U : T;
48
+
49
+ /**
50
+ * Safely extract an array of values if TaggedTemplateValue is present
51
+ * Preserves element types when possible and handles nested arrays recursively
52
+ */
53
+ export function safeToArray<T extends unknown[] | unknown>(
54
+ value: TaggedOr<T>,
55
+ ): Array<DeepUnwrapTagged<ArrayItem<T>>> {
56
+ if (isTaggedTemplateValue(value)) {
57
+ return [value.toString()] as Array<DeepUnwrapTagged<ArrayItem<T>>>;
58
+ }
59
+
60
+ if (Array.isArray(value)) {
61
+ return value.map((item) => {
62
+ if (isTaggedTemplateValue(item)) {
63
+ return item.toString();
64
+ } else if (Array.isArray(item)) {
65
+ return safeToArray(item);
66
+ } else if (
67
+ item &&
68
+ typeof item === "object" &&
69
+ !isTaggedTemplateValue(item)
70
+ ) {
71
+ return safeToObject(item as Record<string, unknown>);
72
+ }
73
+ return item;
74
+ }) as Array<DeepUnwrapTagged<ArrayItem<T>>>;
75
+ }
76
+
77
+ return [value] as Array<DeepUnwrapTagged<ArrayItem<T>>>;
78
+ }
79
+
80
+ /**
81
+ * Recursively transforms a type by replacing TaggedTemplateValue with string
82
+ * and handling unions that contain TaggedTemplateValue
83
+ */
84
+ export type DeepUnwrapTagged<T> =
85
+ // If T is exactly TaggedTemplateValue, convert to string
86
+ T extends TaggedTemplateValue
87
+ ? T extends string // Check if TaggedTemplateValue also extends string to avoid conflicts
88
+ ? string
89
+ : string
90
+ : // If T is a union that includes TaggedTemplateValue, remove TaggedTemplateValue from the union
91
+ TaggedTemplateValue extends T
92
+ ? T extends TaggedTemplateValue
93
+ ? string // T is exactly TaggedTemplateValue
94
+ : Exclude<T, TaggedTemplateValue> // T is a union containing TaggedTemplateValue - remove it
95
+ : // Handle arrays
96
+ T extends Array<infer U>
97
+ ? Array<DeepUnwrapTagged<U>>
98
+ : // Handle records/objects
99
+ T extends Record<string, unknown>
100
+ ? { [K in keyof T]: DeepUnwrapTagged<T[K]> }
101
+ : // Default case - return as is
102
+ T;
103
+
104
+ /**
105
+ * Safely extract an object if TaggedTemplateValue is present
106
+ * Recursively handles nested TaggedTemplateValues
107
+ */
108
+ export function safeToObject<T extends Record<string, unknown>>(
109
+ value: T,
110
+ ): DeepUnwrapTagged<T> {
111
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
112
+ return value as DeepUnwrapTagged<T>;
113
+ }
114
+
115
+ return Object.fromEntries(
116
+ Object.entries(value).map(([key, val]) => {
117
+ if (isTaggedTemplateValue(val)) {
118
+ return [key, val.toString()];
119
+ } else if (Array.isArray(val)) {
120
+ return [key, safeToArray(val)];
121
+ } else if (
122
+ val &&
123
+ typeof val === "object" &&
124
+ !isTaggedTemplateValue(val)
125
+ ) {
126
+ return [key, safeToObject(val as Record<string, unknown>)];
127
+ }
128
+ return [key, val];
129
+ }),
130
+ ) as DeepUnwrapTagged<T>;
131
+ }
132
+
133
+ /**
134
+ * Processes a value that could be a string, TaggedTemplateValue, or
135
+ * a complex object with nested TaggedTemplateValue instances
136
+ *
137
+ * This is useful for handling complex union types like:
138
+ * string | TaggedTemplateValue | Record<string, string | TaggedTemplateValue>
139
+ */
140
+ export function safeFromMixedType<T>(value: unknown): DeepUnwrapTagged<T> {
141
+ // Handle TaggedTemplateValue directly
142
+ if (isTaggedTemplateValue(value)) {
143
+ return value.toString() as DeepUnwrapTagged<T>;
144
+ }
145
+
146
+ // Handle objects (including records)
147
+ if (value && typeof value === "object" && !Array.isArray(value)) {
148
+ return safeToObject(
149
+ value as Record<string, unknown>,
150
+ ) as DeepUnwrapTagged<T>;
151
+ }
152
+
153
+ // Handle arrays
154
+ if (Array.isArray(value)) {
155
+ return safeToArray(value) as unknown as DeepUnwrapTagged<T>;
156
+ }
157
+
158
+ // Handle primitives
159
+ return value as DeepUnwrapTagged<T>;
160
+ }
@@ -0,0 +1,411 @@
1
+ # Functional Programming Utilities
2
+
3
+ This module provides a collection of functional programming utilities for handling data transformations and error management in a type-safe way. It includes function composition utilities and a Result type system for elegant error handling.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Function Composition](#function-composition)
8
+ - [Result Type System](#result-type-system)
9
+ - [API Reference](#api-reference)
10
+ - [Examples](#examples)
11
+ - [Best Practices](#best-practices)
12
+
13
+ ## Function Composition
14
+
15
+ ### `pipe`
16
+
17
+ The `pipe` function allows you to compose functions in a readable, left-to-right manner. It takes an initial value and applies a sequence of functions to it.
18
+
19
+ ```typescript
20
+ import { pipe } from "./fp";
21
+
22
+ // Basic usage
23
+ const result = pipe(
24
+ 5,
25
+ (x) => x + 1, // 6
26
+ (x) => x * 2, // 12
27
+ (x) => x.toString(), // "12"
28
+ );
29
+ console.log(result); // "12"
30
+
31
+ // Complex data transformation
32
+ const processUser = (userData: any) =>
33
+ pipe(
34
+ userData,
35
+ (data) => ({ ...data, id: Math.random() }),
36
+ (user) => ({ ...user, name: user.name.trim() }),
37
+ (user) => ({ ...user, email: user.email.toLowerCase() }),
38
+ (user) => ({ ...user, createdAt: new Date() }),
39
+ );
40
+ ```
41
+
42
+ The `pipe` function supports up to 7 functions and maintains type safety throughout the chain.
43
+
44
+ ## Result Type System
45
+
46
+ The Result type system provides a way to handle operations that might fail without throwing exceptions. It's inspired by functional programming languages like Rust and Haskell.
47
+
48
+ ### Core Types
49
+
50
+ ```typescript
51
+ type Result<T, E = Error> = Success<T> | Failure<E>;
52
+
53
+ interface Success<T> {
54
+ success: true;
55
+ value: T;
56
+ }
57
+
58
+ interface Failure<E = Error> {
59
+ success: false;
60
+ error: E;
61
+ }
62
+ ```
63
+
64
+ ### Creating Results
65
+
66
+ ```typescript
67
+ import { success, failure } from "./fp";
68
+
69
+ // Create a successful result
70
+ const successResult = success(42);
71
+ // { success: true, value: 42 }
72
+
73
+ // Create a failure result
74
+ const failureResult = failure(new Error("Something went wrong"));
75
+ // { success: false, error: Error("Something went wrong") }
76
+
77
+ // Custom error types
78
+ const customFailure = failure({ code: 404, message: "Not found" });
79
+ ```
80
+
81
+ ### Type Guards
82
+
83
+ ```typescript
84
+ import { isSuccess, isFailure } from "./fp";
85
+
86
+ const result: Result<number, string> = success(42);
87
+
88
+ if (isSuccess(result)) {
89
+ // TypeScript knows this is Success<number>
90
+ console.log(result.value); // 42
91
+ }
92
+
93
+ if (isFailure(result)) {
94
+ // TypeScript knows this is Failure<string>
95
+ console.log(result.error); // string
96
+ }
97
+ ```
98
+
99
+ ## API Reference
100
+
101
+ ### Function Composition
102
+
103
+ #### `pipe(initialValue, ...functions)`
104
+
105
+ Applies a sequence of functions to an initial value.
106
+
107
+ ```typescript
108
+ pipe(value, fn1, fn2, fn3, ...); // fn3(fn2(fn1(value)))
109
+ ```
110
+
111
+ ### Result Creation
112
+
113
+ #### `success<T>(value: T): Success<T>`
114
+
115
+ Creates a successful result containing the given value.
116
+
117
+ #### `failure<E>(error: E): Failure<E>`
118
+
119
+ Creates a failure result containing the given error.
120
+
121
+ ### Type Guards
122
+
123
+ #### `isSuccess<T, E>(result: Result<T, E>): result is Success<T>`
124
+
125
+ Type guard that checks if a result is successful.
126
+
127
+ #### `isFailure<T, E>(result: Result<T, E>): result is Failure<E>`
128
+
129
+ Type guard that checks if a result is a failure.
130
+
131
+ ### Result Transformations
132
+
133
+ #### `map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>`
134
+
135
+ Transforms the value inside a successful result. Does nothing for failures.
136
+
137
+ ```typescript
138
+ const result = success(5);
139
+ const doubled = map(result, (x) => x * 2);
140
+ // Success { value: 10 }
141
+
142
+ const failed = failure("error");
143
+ const stillFailed = map(failed, (x) => x * 2);
144
+ // Failure { error: "error" }
145
+ ```
146
+
147
+ #### `flatMap<T, U, E, F>(result: Result<T, E>, fn: (value: T) => Result<U, F>): Result<U, E | F>`
148
+
149
+ Chains results together. Useful for operations that might fail.
150
+
151
+ ```typescript
152
+ const parseNumber = (str: string): Result<number, string> => {
153
+ const num = parseInt(str, 10);
154
+ return isNaN(num) ? failure("Invalid number") : success(num);
155
+ };
156
+
157
+ const result = success("42");
158
+ const parsed = flatMap(result, parseNumber);
159
+ // Success { value: 42 }
160
+
161
+ const invalid = success("not-a-number");
162
+ const failed = flatMap(invalid, parseNumber);
163
+ // Failure { error: "Invalid number" }
164
+ ```
165
+
166
+ #### `recover<T, E>(result: Result<T, E>, fn: (error: E) => T): Success<T>`
167
+
168
+ Recovers from a failure by providing a default value or transformation.
169
+
170
+ ```typescript
171
+ const failed = failure("Network error");
172
+ const recovered = recover(failed, (error) => `Default: ${error}`);
173
+ // Success { value: "Default: Network error" }
174
+ ```
175
+
176
+ ### Value Extraction
177
+
178
+ #### `getOrThrow<T, E extends Error>(result: Result<T, E>): T`
179
+
180
+ Extracts the value from a successful result or throws the error.
181
+
182
+ ```typescript
183
+ const success = success(42);
184
+ const value = getOrThrow(success); // 42
185
+
186
+ const failed = failure(new Error("Oops"));
187
+ const value2 = getOrThrow(failed); // throws Error("Oops")
188
+ ```
189
+
190
+ #### `getOrElse<T, E>(result: Result<T, E>, defaultValue: T): T`
191
+
192
+ Extracts the value from a successful result or returns a default value.
193
+
194
+ ```typescript
195
+ const success = success(42);
196
+ const value = getOrElse(success, 0); // 42
197
+
198
+ const failed = failure("error");
199
+ const value2 = getOrElse(failed, 0); // 0
200
+ ```
201
+
202
+ ### Utility Functions
203
+
204
+ #### `tryResult<T>(fn: () => T): Result<T, Error>`
205
+
206
+ Wraps a function that might throw in a Result.
207
+
208
+ ```typescript
209
+ const safeParseJson = (str: string) => tryResult(() => JSON.parse(str));
210
+
211
+ const valid = safeParseJson('{"name": "John"}');
212
+ // Success { value: { name: "John" } }
213
+
214
+ const invalid = safeParseJson("invalid json");
215
+ // Failure { error: SyntaxError(...) }
216
+ ```
217
+
218
+ #### `match<T, E, U>(result: Result<T, E>, onSuccess: (value: T) => U, onFailure: (error: E) => U): U`
219
+
220
+ Pattern matching for results. Always returns a value.
221
+
222
+ ```typescript
223
+ const result = success(42);
224
+ const message = match(
225
+ result,
226
+ (value) => `Success: ${value}`,
227
+ (error) => `Error: ${error}`,
228
+ );
229
+ // "Success: 42"
230
+ ```
231
+
232
+ ## Examples
233
+
234
+ ### Basic Error Handling
235
+
236
+ ```typescript
237
+ import { success, failure, map, flatMap, getOrElse } from "./fp";
238
+
239
+ // Simulate API calls that might fail
240
+ const fetchUser = (id: number): Result<User, string> => {
241
+ if (id <= 0) return failure("Invalid user ID");
242
+ return success({ id, name: "John Doe", email: "john@example.com" });
243
+ };
244
+
245
+ const validateEmail = (user: User): Result<User, string> => {
246
+ if (!user.email.includes("@")) return failure("Invalid email");
247
+ return success(user);
248
+ };
249
+
250
+ // Chain operations
251
+ const result = pipe(
252
+ 1,
253
+ fetchUser,
254
+ (res) => flatMap(res, validateEmail),
255
+ (res) => map(res, (user) => ({ ...user, validated: true })),
256
+ (res) =>
257
+ getOrElse(res, { id: 0, name: "Unknown", email: "", validated: false }),
258
+ );
259
+ ```
260
+
261
+ ### Data Processing Pipeline
262
+
263
+ ```typescript
264
+ import { pipe, tryResult, map, flatMap, recover } from "./fp";
265
+
266
+ const processData = (input: string) =>
267
+ pipe(
268
+ input,
269
+ // Parse JSON safely
270
+ (str) => tryResult(() => JSON.parse(str)),
271
+ // Validate structure
272
+ (res) =>
273
+ flatMap(res, (data) =>
274
+ typeof data === "object" && data.items
275
+ ? success(data)
276
+ : failure("Invalid data structure"),
277
+ ),
278
+ // Transform items
279
+ (res) =>
280
+ map(res, (data) => ({
281
+ ...data,
282
+ items: data.items.map((item: any) => ({ ...item, processed: true })),
283
+ })),
284
+ // Recover from errors with default
285
+ (res) =>
286
+ recover(res, (error) => ({
287
+ items: [],
288
+ error: error.message || String(error),
289
+ })),
290
+ );
291
+
292
+ const result = processData('{"items": [{"name": "test"}]}');
293
+ // Success with processed data
294
+ ```
295
+
296
+ ### Combining with Async Operations
297
+
298
+ ```typescript
299
+ import { success, failure, flatMap, map } from "./fp";
300
+
301
+ // Wrap async operations in Results
302
+ const asyncFetchUser = async (id: number): Promise<Result<User, string>> => {
303
+ try {
304
+ const response = await fetch(`/api/users/${id}`);
305
+ if (!response.ok) return failure(`HTTP ${response.status}`);
306
+ const user = await response.json();
307
+ return success(user);
308
+ } catch (error) {
309
+ return failure(error.message);
310
+ }
311
+ };
312
+
313
+ // Use with async/await
314
+ const processUser = async (id: number) => {
315
+ const userResult = await asyncFetchUser(id);
316
+
317
+ return pipe(
318
+ userResult,
319
+ (res) => map(res, (user) => ({ ...user, lastSeen: new Date() })),
320
+ (res) =>
321
+ flatMap(res, (user) =>
322
+ user.active ? success(user) : failure("User is inactive"),
323
+ ),
324
+ );
325
+ };
326
+ ```
327
+
328
+ ## Best Practices
329
+
330
+ ### 1. Use Descriptive Error Types
331
+
332
+ ```typescript
333
+ // Good: Specific error types
334
+ type ValidationError = "INVALID_EMAIL" | "MISSING_NAME" | "INVALID_AGE";
335
+ const validateUser = (data: any): Result<User, ValidationError> => {
336
+ // ...
337
+ };
338
+
339
+ // Avoid: Generic string errors
340
+ const validateUser = (data: any): Result<User, string> => {
341
+ // ...
342
+ };
343
+ ```
344
+
345
+ ### 2. Chain Operations with flatMap
346
+
347
+ ```typescript
348
+ // Good: Chain operations that might fail
349
+ const result = pipe(
350
+ input,
351
+ parseJson,
352
+ (res) => flatMap(res, validateSchema),
353
+ (res) => flatMap(res, transformData),
354
+ (res) => map(res, finalTransform),
355
+ );
356
+
357
+ // Avoid: Nested if statements
358
+ if (isSuccess(parsed)) {
359
+ const validated = validateSchema(parsed.value);
360
+ if (isSuccess(validated)) {
361
+ // ...
362
+ }
363
+ }
364
+ ```
365
+
366
+ ### 3. Use recover for Default Values
367
+
368
+ ```typescript
369
+ // Good: Provide meaningful defaults
370
+ const config = pipe(configFile, parseConfig, (res) =>
371
+ recover(res, () => DEFAULT_CONFIG),
372
+ );
373
+
374
+ // Good: Transform errors into user-friendly messages
375
+ const userMessage = pipe(operation, (res) =>
376
+ recover(res, (error) => `Operation failed: ${error}`),
377
+ );
378
+ ```
379
+
380
+ ### 4. Prefer match for Exhaustive Handling
381
+
382
+ ```typescript
383
+ // Good: Handle both cases explicitly
384
+ const message = match(
385
+ result,
386
+ (user) => `Welcome, ${user.name}!`,
387
+ (error) => `Login failed: ${error}`,
388
+ );
389
+
390
+ // Avoid: Only handling success case
391
+ if (isSuccess(result)) {
392
+ console.log(`Welcome, ${result.value.name}!`);
393
+ }
394
+ ```
395
+
396
+ ### 5. Use tryResult for Exception-Prone Code
397
+
398
+ ```typescript
399
+ // Good: Wrap risky operations
400
+ const safeOperation = (data: string) =>
401
+ tryResult(() => {
402
+ return JSON.parse(data).someProperty.deepValue;
403
+ });
404
+
405
+ // Avoid: Letting exceptions bubble up
406
+ const riskyOperation = (data: string) => {
407
+ return JSON.parse(data).someProperty.deepValue; // Might throw
408
+ };
409
+ ```
410
+
411
+ This functional programming module provides a robust foundation for handling data transformations and errors in a type-safe, composable way. The Result type system eliminates the need for exception handling in many cases and makes error states explicit in your type signatures.