@sylphx/flow 3.19.0 → 3.20.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/CHANGELOG.md +31 -0
- package/assets/agents/builder.md +12 -3
- package/package.json +1 -3
- package/src/commands/flow/execute-v2.ts +126 -128
- package/src/commands/flow-command.ts +52 -42
- package/src/commands/hook-command.ts +161 -20
- package/src/config/index.ts +0 -20
- package/src/core/agent-loader.ts +2 -2
- package/src/core/attach-manager.ts +5 -1
- package/src/core/cleanup-handler.ts +20 -16
- package/src/core/flow-executor.ts +93 -62
- package/src/core/functional/index.ts +0 -11
- package/src/core/index.ts +1 -1
- package/src/core/project-manager.ts +14 -29
- package/src/core/secrets-manager.ts +15 -18
- package/src/core/session-manager.ts +4 -8
- package/src/core/target-manager.ts +6 -3
- package/src/core/upgrade-manager.ts +1 -1
- package/src/index.ts +1 -1
- package/src/services/auto-upgrade.ts +6 -14
- package/src/services/config-service.ts +7 -23
- package/src/services/index.ts +1 -1
- package/src/targets/claude-code.ts +14 -8
- package/src/targets/functional/claude-code-logic.ts +11 -7
- package/src/targets/opencode.ts +61 -39
- package/src/targets/shared/mcp-transforms.ts +20 -43
- package/src/types/agent.types.ts +5 -3
- package/src/types/mcp.types.ts +38 -1
- package/src/types.ts +4 -0
- package/src/utils/agent-enhancer.ts +1 -1
- package/src/utils/errors.ts +13 -0
- package/src/utils/files/file-operations.ts +16 -0
- package/src/utils/index.ts +1 -1
- package/src/core/error-handling.ts +0 -482
- package/src/core/functional/async.ts +0 -101
- package/src/core/functional/either.ts +0 -109
- package/src/core/functional/error-handler.ts +0 -135
- package/src/core/functional/pipe.ts +0 -189
- package/src/core/functional/validation.ts +0 -138
- package/src/types/mcp-config.types.ts +0 -448
- package/src/utils/error-handler.ts +0 -53
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Functional error handling utilities
|
|
3
|
-
* Replaces exception-based error handling with Result-based approach
|
|
4
|
-
*
|
|
5
|
-
* DESIGN RATIONALE:
|
|
6
|
-
* - Errors as values, not exceptions
|
|
7
|
-
* - Explicit error handling in function signatures
|
|
8
|
-
* - Composable error handling
|
|
9
|
-
* - Separation of error handling from business logic
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { AppError } from './error-types.js';
|
|
13
|
-
import { formatError, toAppError } from './error-types.js';
|
|
14
|
-
import type { Result } from './result.js';
|
|
15
|
-
import { failure, success } from './result.js';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Execute a function and convert exceptions to Result
|
|
19
|
-
*/
|
|
20
|
-
export const execute = <T>(fn: () => T): Result<T, AppError> => {
|
|
21
|
-
try {
|
|
22
|
-
return success(fn());
|
|
23
|
-
} catch (error) {
|
|
24
|
-
return failure(toAppError(error));
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Execute an async function and convert exceptions to Result
|
|
30
|
-
*/
|
|
31
|
-
export const executeAsync = async <T>(fn: () => Promise<T>): Promise<Result<T, AppError>> => {
|
|
32
|
-
try {
|
|
33
|
-
const value = await fn();
|
|
34
|
-
return success(value);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
return failure(toAppError(error));
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Log error to console (side effect)
|
|
42
|
-
*/
|
|
43
|
-
export const logError = (error: AppError): void => {
|
|
44
|
-
console.error(formatError(error));
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Log error and exit process (side effect)
|
|
49
|
-
* Only use at top-level command handlers
|
|
50
|
-
*/
|
|
51
|
-
export const exitWithError = (error: AppError, exitCode = 1): never => {
|
|
52
|
-
logError(error);
|
|
53
|
-
process.exit(exitCode);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Convert Result to exit code
|
|
58
|
-
* 0 for success, 1 for failure
|
|
59
|
-
* Use at top-level command handlers
|
|
60
|
-
*/
|
|
61
|
-
export const toExitCode = <T>(result: Result<T, AppError>): number => {
|
|
62
|
-
if (result._tag === 'Success') {
|
|
63
|
-
return 0;
|
|
64
|
-
}
|
|
65
|
-
logError(result.error);
|
|
66
|
-
return 1;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Retry logic with exponential backoff
|
|
71
|
-
* Retries a function that returns a Result
|
|
72
|
-
*/
|
|
73
|
-
export const retry = async <T>(
|
|
74
|
-
fn: () => Promise<Result<T, AppError>>,
|
|
75
|
-
options: {
|
|
76
|
-
maxRetries: number;
|
|
77
|
-
delayMs: number;
|
|
78
|
-
backoff?: number;
|
|
79
|
-
onRetry?: (error: AppError, attempt: number) => void;
|
|
80
|
-
}
|
|
81
|
-
): Promise<Result<T, AppError>> => {
|
|
82
|
-
const { maxRetries, delayMs, backoff = 2, onRetry } = options;
|
|
83
|
-
|
|
84
|
-
let lastError: AppError = { type: 'unknown', message: 'Unknown error', code: 'UNKNOWN' };
|
|
85
|
-
let currentDelay = delayMs;
|
|
86
|
-
|
|
87
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
88
|
-
const result = await fn();
|
|
89
|
-
|
|
90
|
-
if (result._tag === 'Success') {
|
|
91
|
-
return result;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
lastError = result.error;
|
|
95
|
-
|
|
96
|
-
if (attempt < maxRetries) {
|
|
97
|
-
if (onRetry) {
|
|
98
|
-
onRetry(lastError, attempt + 1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
102
|
-
currentDelay *= backoff;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return failure(lastError);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Create an async handler that wraps a function returning Result
|
|
111
|
-
* Useful for command handlers
|
|
112
|
-
*/
|
|
113
|
-
export const createAsyncHandler =
|
|
114
|
-
<T extends Record<string, any>>(handler: (options: T) => Promise<Result<void, AppError>>) =>
|
|
115
|
-
async (options: T): Promise<void> => {
|
|
116
|
-
const result = await handler(options);
|
|
117
|
-
|
|
118
|
-
if (result._tag === 'Failure') {
|
|
119
|
-
exitWithError(result.error);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Create a sync handler that wraps a function returning Result
|
|
125
|
-
* Useful for command handlers
|
|
126
|
-
*/
|
|
127
|
-
export const createSyncHandler =
|
|
128
|
-
<T extends Record<string, any>>(handler: (options: T) => Result<void, AppError>) =>
|
|
129
|
-
(options: T): void => {
|
|
130
|
-
const result = handler(options);
|
|
131
|
-
|
|
132
|
-
if (result._tag === 'Failure') {
|
|
133
|
-
exitWithError(result.error);
|
|
134
|
-
}
|
|
135
|
-
};
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Function composition utilities
|
|
3
|
-
* Enable declarative data transformation pipelines
|
|
4
|
-
*
|
|
5
|
-
* DESIGN RATIONALE:
|
|
6
|
-
* - Left-to-right composition (more readable than f(g(h(x))))
|
|
7
|
-
* - Type-safe (TypeScript infers types through the pipeline)
|
|
8
|
-
* - Point-free style support
|
|
9
|
-
* - Declarative over imperative
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Compose functions left-to-right
|
|
14
|
-
* pipe(value, fn1, fn2, fn3) === fn3(fn2(fn1(value)))
|
|
15
|
-
*/
|
|
16
|
-
export function pipe<A>(value: A): A;
|
|
17
|
-
export function pipe<A, B>(value: A, fn1: (a: A) => B): B;
|
|
18
|
-
export function pipe<A, B, C>(value: A, fn1: (a: A) => B, fn2: (b: B) => C): C;
|
|
19
|
-
export function pipe<A, B, C, D>(value: A, fn1: (a: A) => B, fn2: (b: B) => C, fn3: (c: C) => D): D;
|
|
20
|
-
export function pipe<A, B, C, D, E>(
|
|
21
|
-
value: A,
|
|
22
|
-
fn1: (a: A) => B,
|
|
23
|
-
fn2: (b: B) => C,
|
|
24
|
-
fn3: (c: C) => D,
|
|
25
|
-
fn4: (d: D) => E
|
|
26
|
-
): E;
|
|
27
|
-
export function pipe<A, B, C, D, E, F>(
|
|
28
|
-
value: A,
|
|
29
|
-
fn1: (a: A) => B,
|
|
30
|
-
fn2: (b: B) => C,
|
|
31
|
-
fn3: (c: C) => D,
|
|
32
|
-
fn4: (d: D) => E,
|
|
33
|
-
fn5: (e: E) => F
|
|
34
|
-
): F;
|
|
35
|
-
export function pipe<A, B, C, D, E, F, G>(
|
|
36
|
-
value: A,
|
|
37
|
-
fn1: (a: A) => B,
|
|
38
|
-
fn2: (b: B) => C,
|
|
39
|
-
fn3: (c: C) => D,
|
|
40
|
-
fn4: (d: D) => E,
|
|
41
|
-
fn5: (e: E) => F,
|
|
42
|
-
fn6: (f: F) => G
|
|
43
|
-
): G;
|
|
44
|
-
export function pipe<A, B, C, D, E, F, G, H>(
|
|
45
|
-
value: A,
|
|
46
|
-
fn1: (a: A) => B,
|
|
47
|
-
fn2: (b: B) => C,
|
|
48
|
-
fn3: (c: C) => D,
|
|
49
|
-
fn4: (d: D) => E,
|
|
50
|
-
fn5: (e: E) => F,
|
|
51
|
-
fn6: (f: F) => G,
|
|
52
|
-
fn7: (g: G) => H
|
|
53
|
-
): H;
|
|
54
|
-
export function pipe<A, B, C, D, E, F, G, H, I>(
|
|
55
|
-
value: A,
|
|
56
|
-
fn1: (a: A) => B,
|
|
57
|
-
fn2: (b: B) => C,
|
|
58
|
-
fn3: (c: C) => D,
|
|
59
|
-
fn4: (d: D) => E,
|
|
60
|
-
fn5: (e: E) => F,
|
|
61
|
-
fn6: (f: F) => G,
|
|
62
|
-
fn7: (g: G) => H,
|
|
63
|
-
fn8: (h: H) => I
|
|
64
|
-
): I;
|
|
65
|
-
export function pipe<A, B, C, D, E, F, G, H, I, J>(
|
|
66
|
-
value: A,
|
|
67
|
-
fn1: (a: A) => B,
|
|
68
|
-
fn2: (b: B) => C,
|
|
69
|
-
fn3: (c: C) => D,
|
|
70
|
-
fn4: (d: D) => E,
|
|
71
|
-
fn5: (e: E) => F,
|
|
72
|
-
fn6: (f: F) => G,
|
|
73
|
-
fn7: (g: G) => H,
|
|
74
|
-
fn8: (h: H) => I,
|
|
75
|
-
fn9: (i: I) => J
|
|
76
|
-
): J;
|
|
77
|
-
export function pipe(value: any, ...fns: Array<(arg: any) => any>): any {
|
|
78
|
-
return fns.reduce((acc, fn) => fn(acc), value);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Create a composed function from multiple functions
|
|
83
|
-
* flow(fn1, fn2, fn3) creates a new function that applies fn1, then fn2, then fn3
|
|
84
|
-
*/
|
|
85
|
-
export function flow<A, B>(fn1: (a: A) => B): (a: A) => B;
|
|
86
|
-
export function flow<A, B, C>(fn1: (a: A) => B, fn2: (b: B) => C): (a: A) => C;
|
|
87
|
-
export function flow<A, B, C, D>(fn1: (a: A) => B, fn2: (b: B) => C, fn3: (c: C) => D): (a: A) => D;
|
|
88
|
-
export function flow<A, B, C, D, E>(
|
|
89
|
-
fn1: (a: A) => B,
|
|
90
|
-
fn2: (b: B) => C,
|
|
91
|
-
fn3: (c: C) => D,
|
|
92
|
-
fn4: (d: D) => E
|
|
93
|
-
): (a: A) => E;
|
|
94
|
-
export function flow<A, B, C, D, E, F>(
|
|
95
|
-
fn1: (a: A) => B,
|
|
96
|
-
fn2: (b: B) => C,
|
|
97
|
-
fn3: (c: C) => D,
|
|
98
|
-
fn4: (d: D) => E,
|
|
99
|
-
fn5: (e: E) => F
|
|
100
|
-
): (a: A) => F;
|
|
101
|
-
export function flow<A, B, C, D, E, F, G>(
|
|
102
|
-
fn1: (a: A) => B,
|
|
103
|
-
fn2: (b: B) => C,
|
|
104
|
-
fn3: (c: C) => D,
|
|
105
|
-
fn4: (d: D) => E,
|
|
106
|
-
fn5: (e: E) => F,
|
|
107
|
-
fn6: (f: F) => G
|
|
108
|
-
): (a: A) => G;
|
|
109
|
-
export function flow<A, B, C, D, E, F, G, H>(
|
|
110
|
-
fn1: (a: A) => B,
|
|
111
|
-
fn2: (b: B) => C,
|
|
112
|
-
fn3: (c: C) => D,
|
|
113
|
-
fn4: (d: D) => E,
|
|
114
|
-
fn5: (e: E) => F,
|
|
115
|
-
fn6: (f: F) => G,
|
|
116
|
-
fn7: (g: G) => H
|
|
117
|
-
): (a: A) => H;
|
|
118
|
-
export function flow<A, B, C, D, E, F, G, H, I>(
|
|
119
|
-
fn1: (a: A) => B,
|
|
120
|
-
fn2: (b: B) => C,
|
|
121
|
-
fn3: (c: C) => D,
|
|
122
|
-
fn4: (d: D) => E,
|
|
123
|
-
fn5: (e: E) => F,
|
|
124
|
-
fn6: (f: F) => G,
|
|
125
|
-
fn7: (g: G) => H,
|
|
126
|
-
fn8: (h: H) => I
|
|
127
|
-
): (a: A) => I;
|
|
128
|
-
export function flow<A, B, C, D, E, F, G, H, I, J>(
|
|
129
|
-
fn1: (a: A) => B,
|
|
130
|
-
fn2: (b: B) => C,
|
|
131
|
-
fn3: (c: C) => D,
|
|
132
|
-
fn4: (d: D) => E,
|
|
133
|
-
fn5: (e: E) => F,
|
|
134
|
-
fn6: (f: F) => G,
|
|
135
|
-
fn7: (g: G) => H,
|
|
136
|
-
fn8: (h: H) => I,
|
|
137
|
-
fn9: (i: I) => J
|
|
138
|
-
): (a: A) => J;
|
|
139
|
-
export function flow(...fns: Array<(arg: any) => any>): (arg: any) => any {
|
|
140
|
-
return (value: any) => pipe(value, ...fns);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Identity function - returns input unchanged
|
|
145
|
-
* Useful as a no-op transformation or default
|
|
146
|
-
*/
|
|
147
|
-
export const identity = <T>(value: T): T => value;
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Constant function - returns a function that always returns the same value
|
|
151
|
-
* Useful for providing defaults
|
|
152
|
-
*/
|
|
153
|
-
export const constant =
|
|
154
|
-
<T>(value: T) =>
|
|
155
|
-
(): T =>
|
|
156
|
-
value;
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Tap - run a side effect and return the original value
|
|
160
|
-
* Useful for debugging or logging in a pipeline
|
|
161
|
-
*/
|
|
162
|
-
export const tap =
|
|
163
|
-
<T>(fn: (value: T) => void) =>
|
|
164
|
-
(value: T): T => {
|
|
165
|
-
fn(value);
|
|
166
|
-
return value;
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Memoize - cache function results
|
|
171
|
-
* Only for pure functions with string/number arguments
|
|
172
|
-
*/
|
|
173
|
-
export const memoize = <Args extends Array<string | number>, R>(
|
|
174
|
-
fn: (...args: Args) => R
|
|
175
|
-
): ((...args: Args) => R) => {
|
|
176
|
-
const cache = new Map<string, R>();
|
|
177
|
-
|
|
178
|
-
return (...args: Args): R => {
|
|
179
|
-
const key = JSON.stringify(args);
|
|
180
|
-
|
|
181
|
-
if (cache.has(key)) {
|
|
182
|
-
return cache.get(key)!;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const result = fn(...args);
|
|
186
|
-
cache.set(key, result);
|
|
187
|
-
return result;
|
|
188
|
-
};
|
|
189
|
-
};
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation utilities for accumulating errors
|
|
3
|
-
* Unlike Result which short-circuits on first error,
|
|
4
|
-
* Validation accumulates all errors
|
|
5
|
-
*
|
|
6
|
-
* DESIGN RATIONALE:
|
|
7
|
-
* - Form validation needs all errors, not just first
|
|
8
|
-
* - Applicative functor for combining validations
|
|
9
|
-
* - Errors accumulated in array
|
|
10
|
-
* - Success only if all validations pass
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { Result } from './result.js';
|
|
14
|
-
import { failure, isSuccess, success } from './result.js';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Validation result with accumulated errors
|
|
18
|
-
*/
|
|
19
|
-
export type Validation<T, E = string> = Result<T, E[]>;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create a successful validation
|
|
23
|
-
*/
|
|
24
|
-
export const valid = <T, E = string>(value: T): Validation<T, E> => success(value);
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Create a failed validation with one or more errors
|
|
28
|
-
*/
|
|
29
|
-
export const invalid = <T, E = string>(...errors: E[]): Validation<T, E> => failure(errors);
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Combine multiple validations
|
|
33
|
-
* Collects all errors if any validation fails
|
|
34
|
-
*/
|
|
35
|
-
export const combine = <T, E = string>(validations: Validation<T, E>[]): Validation<T[], E> => {
|
|
36
|
-
const values: T[] = [];
|
|
37
|
-
const errors: E[] = [];
|
|
38
|
-
|
|
39
|
-
for (const validation of validations) {
|
|
40
|
-
if (isSuccess(validation)) {
|
|
41
|
-
values.push(validation.value);
|
|
42
|
-
} else {
|
|
43
|
-
errors.push(...validation.error);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (errors.length > 0) {
|
|
48
|
-
return invalid(...errors);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return valid(values);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Validate a value against multiple validators
|
|
56
|
-
* Returns first error or success
|
|
57
|
-
*/
|
|
58
|
-
export const validateAll =
|
|
59
|
-
<T, E = string>(...validators: Array<(value: T) => Validation<T, E>>) =>
|
|
60
|
-
(value: T): Validation<T, E> => {
|
|
61
|
-
const errors: E[] = [];
|
|
62
|
-
|
|
63
|
-
for (const validator of validators) {
|
|
64
|
-
const result = validator(value);
|
|
65
|
-
if (!isSuccess(result)) {
|
|
66
|
-
errors.push(...result.error);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (errors.length > 0) {
|
|
71
|
-
return invalid(...errors);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return valid(value);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Common validators
|
|
79
|
-
*/
|
|
80
|
-
|
|
81
|
-
export const nonEmpty =
|
|
82
|
-
(message: string) =>
|
|
83
|
-
(value: string): Validation<string, string> => {
|
|
84
|
-
if (value.trim().length === 0) {
|
|
85
|
-
return invalid(message);
|
|
86
|
-
}
|
|
87
|
-
return valid(value);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export const minLength =
|
|
91
|
-
(min: number, message: string) =>
|
|
92
|
-
(value: string): Validation<string, string> => {
|
|
93
|
-
if (value.length < min) {
|
|
94
|
-
return invalid(message);
|
|
95
|
-
}
|
|
96
|
-
return valid(value);
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export const maxLength =
|
|
100
|
-
(max: number, message: string) =>
|
|
101
|
-
(value: string): Validation<string, string> => {
|
|
102
|
-
if (value.length > max) {
|
|
103
|
-
return invalid(message);
|
|
104
|
-
}
|
|
105
|
-
return valid(value);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export const matches =
|
|
109
|
-
(pattern: RegExp, message: string) =>
|
|
110
|
-
(value: string): Validation<string, string> => {
|
|
111
|
-
if (!pattern.test(value)) {
|
|
112
|
-
return invalid(message);
|
|
113
|
-
}
|
|
114
|
-
return valid(value);
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
export const isEmail = (message: string) => matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, message);
|
|
118
|
-
|
|
119
|
-
export const isUrl = (message: string) => matches(/^https?:\/\/.+/, message);
|
|
120
|
-
|
|
121
|
-
export const isNumber =
|
|
122
|
-
(message: string) =>
|
|
123
|
-
(value: string): Validation<number, string> => {
|
|
124
|
-
const num = Number(value);
|
|
125
|
-
if (Number.isNaN(num)) {
|
|
126
|
-
return invalid(message);
|
|
127
|
-
}
|
|
128
|
-
return valid(num);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export const range =
|
|
132
|
-
(min: number, max: number, message: string) =>
|
|
133
|
-
(value: number): Validation<number, string> => {
|
|
134
|
-
if (value < min || value > max) {
|
|
135
|
-
return invalid(message);
|
|
136
|
-
}
|
|
137
|
-
return valid(value);
|
|
138
|
-
};
|