diff --git a/app/converters/json-yaml/page.tsx b/app/converters/json-yaml/page.tsx index 41b3ecf..44f2632 100644 --- a/app/converters/json-yaml/page.tsx +++ b/app/converters/json-yaml/page.tsx @@ -1,9 +1,12 @@ "use client"; import { useCallback, useMemo, useState } from "react"; +import * as O from "fp-ts/lib/Option"; import YAML from "yaml"; import { toolGroups } from "@/config/tools"; +import { safeJsonParse } from "@/lib/json"; +import { safeYamlParse } from "@/lib/yaml"; import { Editor, EditorProps } from "@/components/ui/editor"; import { Select, @@ -34,28 +37,24 @@ export default function Page() { const setJsonReactively = useCallback( (text: string) => { - setJson(text); + const parsed = safeJsonParse(text); - try { - const parsed = JSON.parse(text) as unknown; - setYaml(YAML.stringify(parsed, { indent: indentation.length, simpleKeys: true })); - } catch { - setYaml(""); - } + setJson(text); + setYaml( + O.isNone(parsed) + ? "" + : YAML.stringify(parsed.value, { indent: indentation.length, simpleKeys: true }) + ); }, [indentation.length] ); const setYamlReactively = useCallback( (text: string) => { - setYaml(text); + const parsed = safeYamlParse(text, (_, v) => v, { merge: true }); - try { - const parsed = YAML.parse(text, { merge: true }) as unknown; - setJson(JSON.stringify(parsed, null, indentation)); - } catch { - setJson(""); - } + setYaml(text); + setJson(O.isNone(parsed) ? "" : JSON.stringify(parsed.value, null, indentation)); }, [indentation] ); @@ -68,12 +67,13 @@ export default function Page() { const onIndentationChange: SelectProps["onValueChange"] = value => { setIndentation(value); - try { - const parsed = JSON.parse(json) as unknown; - setJson(JSON.stringify(parsed, null, value)); - setYaml(YAML.stringify(parsed, { indent: value.length, simpleKeys: true })); - } catch { + const parsed = safeJsonParse(json); + + if (O.isNone(parsed)) { clearBoth(); + } else { + setJson(JSON.stringify(parsed.value, null, value)); + setYaml(YAML.stringify(parsed.value, { indent: value.length, simpleKeys: true })); } }; diff --git a/app/encoders-decoders/jwt/page.tsx b/app/encoders-decoders/jwt/page.tsx index 0336c13..ac09bd3 100644 --- a/app/encoders-decoders/jwt/page.tsx +++ b/app/encoders-decoders/jwt/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useCallback, useMemo, useState } from "react"; +import * as O from "fp-ts/lib/Option"; import { toolGroups } from "@/config/tools"; import { decode } from "@/lib/jwt"; @@ -19,9 +20,9 @@ export default function Page() { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" ); - const { headerObj, payloadObj } = decode(jwt); - const header = JSON.stringify(headerObj, null, 2) ?? ""; - const payload = JSON.stringify(payloadObj, null, 2) ?? ""; + const { header: h, payload: p } = decode(jwt); + const header = O.isNone(h) ? "" : JSON.stringify(h.value, null, 2); + const payload = O.isNone(p) ? "" : JSON.stringify(p.value, null, 2); const clearJwt = useCallback(() => setJwt(""), []); diff --git a/app/encoders-decoders/url/page.tsx b/app/encoders-decoders/url/page.tsx index a02a98b..5a7ee0d 100644 --- a/app/encoders-decoders/url/page.tsx +++ b/app/encoders-decoders/url/page.tsx @@ -1,8 +1,11 @@ "use client"; import { useCallback, useMemo, useState } from "react"; +import * as O from "fp-ts/lib/Option"; +import { constant } from "fp-ts/lib/function"; import { toolGroups } from "@/config/tools"; +import { safeDecodeURIComponent, safeEncodeURIComponent } from "@/lib/uri"; import { Textarea, TextareaProps } from "@/components/ui/textarea"; import { ClearButton } from "@/components/buttons/clear"; import { CopyButton } from "@/components/buttons/copy"; @@ -18,22 +21,12 @@ export default function Page() { const setDecodedReactively = useCallback((text: string) => { setDecoded(text); - - try { - setEncoded(encodeURIComponent(text)); - } catch { - setEncoded(""); - } + setEncoded(O.getOrElse(constant(""))(safeEncodeURIComponent(text))); }, []); const setEncodedReactively = useCallback((text: string) => { setEncoded(text); - - try { - setDecoded(decodeURIComponent(text)); - } catch { - setDecoded(""); - } + setDecoded(O.getOrElse(constant(""))(safeDecodeURIComponent(text))); }, []); const clearBoth = useCallback(() => { diff --git a/app/formatters/json/page.tsx b/app/formatters/json/page.tsx index a917705..0099561 100644 --- a/app/formatters/json/page.tsx +++ b/app/formatters/json/page.tsx @@ -1,8 +1,10 @@ "use client"; import { useCallback, useMemo, useState } from "react"; +import * as O from "fp-ts/lib/Option"; import { toolGroups } from "@/config/tools"; +import { safeJsonParse } from "@/lib/json"; import { Editor, EditorProps } from "@/components/ui/editor"; import { Select, @@ -31,13 +33,8 @@ export default function Page() { const [indentation, setIndentation] = useState(two); const [input, setInput] = useState('{\n"foo":"bar"\n}'); - let output: string; - try { - const parsed = JSON.parse(input) as unknown; - output = JSON.stringify(parsed, null, indentation); - } catch { - output = ""; - } + const parsed = safeJsonParse(input); + const output = O.isNone(parsed) ? "" : JSON.stringify(parsed.value, null, indentation); const clearInput = useCallback(() => setInput(""), []); diff --git a/lib/json.ts b/lib/json.ts new file mode 100644 index 0000000..5499fce --- /dev/null +++ b/lib/json.ts @@ -0,0 +1,3 @@ +import { tryCatchK } from "fp-ts/lib/Option"; + +export const safeJsonParse = tryCatchK(JSON.parse); diff --git a/lib/jwt.ts b/lib/jwt.ts index e10aa70..afe9e3f 100644 --- a/lib/jwt.ts +++ b/lib/jwt.ts @@ -1,22 +1,16 @@ +import * as O from "fp-ts/lib/Option"; import jwt_decode from "jwt-decode"; +const safeJwtDecode = O.tryCatchK(jwt_decode); + export const decode = (token: string) => { - let headerObj; - let payloadObj; + let header: O.Option> = O.none; + let payload: O.Option = O.none; if (token.split(".").length === 3) { - /* eslint-disable no-empty */ - - try { - headerObj = jwt_decode(token, { header: true }); - } catch {} - - try { - payloadObj = jwt_decode(token, { header: false }); - } catch {} - - /* eslint-enable no-empty */ + header = safeJwtDecode(token, { header: true }); + payload = safeJwtDecode(token, { header: false }); } - return { headerObj, payloadObj }; + return { header, payload }; }; diff --git a/lib/uri.ts b/lib/uri.ts new file mode 100644 index 0000000..c02d85b --- /dev/null +++ b/lib/uri.ts @@ -0,0 +1,4 @@ +import { tryCatchK } from "fp-ts/lib/Option"; + +export const safeEncodeURIComponent = tryCatchK(encodeURIComponent); +export const safeDecodeURIComponent = tryCatchK(decodeURIComponent); diff --git a/lib/yaml.ts b/lib/yaml.ts new file mode 100644 index 0000000..70eb7f7 --- /dev/null +++ b/lib/yaml.ts @@ -0,0 +1,4 @@ +import { tryCatchK } from "fp-ts/lib/Option"; +import YAML from "yaml"; + +export const safeYamlParse = tryCatchK(YAML.parse);