import * as Cause from "effect/Cause"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import * as Option from "effect/Option"; import * as Result from "effect/Result"; import * as Schema from "effect/Schema"; import * as SchemaGetter from "effect/SchemaGetter"; import * as SchemaIssue from "effect/SchemaIssue"; import * as SchemaTransformation from "effect/SchemaTransformation"; export const decodeJsonResult = >( schema: S, ) => { const decode = Schema.decodeExit(Schema.fromJsonString(schema)); return (input: string) => { const result = decode(input); if (Exit.isFailure(result)) { return Result.fail(result.cause); } return Result.succeed(result.value); }; }; export const decodeUnknownJsonResult = >( schema: S, ) => { const decode = Schema.decodeUnknownExit(Schema.fromJsonString(schema)); return (input: unknown) => { const result = decode(input); if (Exit.isFailure(result)) { return Result.fail(result.cause); } return Result.succeed(result.value); }; }; export const formatSchemaError = (cause: Cause.Cause) => { const squashed = Cause.squash(cause); return Schema.isSchemaError(squashed) ? SchemaIssue.makeFormatterDefault()(squashed.issue) : Cause.pretty(cause); }; /** * A `Getter` that parses a lenient JSON string (tolerating trailing commas * and JS-style comments) into an unknown value. * * Mirrors `SchemaGetter.parseJson()` but uses `parseLenientJson` instead * of `JSON.parse`. */ const parseLenientJsonGetter = SchemaGetter.onSome((input: string) => Effect.try({ try: () => { // Strip single-line comments — alternation preserves quoted strings. let stripped = input.replace( /("(?:[^"\\]|\\.)*")|\/\/[^\n]*/g, (match, stringLiteral: string | undefined) => (stringLiteral ? match : ""), ); // Strip multi-line comments. stripped = stripped.replace( /("(?:[^"\\]|\\.)*")|\/\*[\s\S]*?\*\//g, (match, stringLiteral: string | undefined) => (stringLiteral ? match : ""), ); // Strip trailing commas before `}` or `]`. stripped = stripped.replace(/,(\s*[}\]])/g, "$1"); return Option.some(JSON.parse(stripped)); }, catch: (e) => new SchemaIssue.InvalidValue(Option.some(input), { message: String(e) }), }), ); /** * Schema transformation: lenient JSONC string ↔ unknown. * * Same API as `SchemaTransformation.fromJsonString`, but the decode side * strips trailing commas and JS-style comments before parsing. * Encoding produces strict JSON via `JSON.stringify`. */ export const fromLenientJsonString = new SchemaTransformation.Transformation( parseLenientJsonGetter, SchemaGetter.stringifyJson(), ); /** * Build a schema that decodes a lenient JSON string into `A`. * * Drop-in replacement for `Schema.fromJsonString(schema)` that tolerates * trailing commas and comments in the input. */ export const fromLenientJson = (schema: S) => Schema.String.pipe(Schema.decodeTo(schema, fromLenientJsonString));