diff --git a/migration/app/proven/ProvenError.affine b/migration/app/proven/ProvenError.affine new file mode 100644 index 00000000..fb596a85 --- /dev/null +++ b/migration/app/proven/ProvenError.affine @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// Copyright (c) 2026 Joshua B. Jewell and Jonathan D.A. Jewell +// +// ProvenError.affine — error types for proven-safe modules. +// +// Translation of src/app/proven/ProvenError.res (32 LoC). Self-contained +// pure-data + pure-constructors module — no FFI, no cross-module deps, +// no Tea_/Pixi/Gossamer surface. Cleanest possible Mode-A AffineScript +// port in idaptik's src/app/ tree. +// +// Translation choices: +// `type x = { field: T, ... }` → unchanged (no trailing comma). +// `let f = (~name: T, ...): U => body` → `fn f(name: T, ...) -> U { body }` +// (AS uses positional args, not labelled — the ReScript ~prefix +// drops on the call site for `notANumber(operation, message)` etc.). +// `{operation, message, errorType}` → `#{operation: operation, ...}` +// punning shorthand drops; AS record literals are explicit. +// `` `[${a}] ${b}: ${c}` `` template-literal → `"[" ++ a ++ "] " ++ b ++ ": " ++ c` +// AS uses `++` concatenation; template literals are JS-specific. +// +// What this PR is NOT +// Does NOT replace src/app/proven/ProvenError.res in the build. The .res +// still owns the runtime contract — SafeFloat / SafeJson / SafeAngle +// reference it directly. Replacement requires the same .affine→.mjs +// codegen story tracked under standards#279 STEP 8. +// +// Refs: standards#279 (megaport step), standards#252 (campaign umbrella). + +module ProvenError; + +type ProvenError = { + operation: String, + message: String, + errorType: String, +} + +fn notANumber(operation: String, message: String) -> ProvenError { + #{ operation: operation, message: message, errorType: "NaN" } +} + +fn infinity(operation: String, message: String) -> ProvenError { + #{ operation: operation, message: message, errorType: "Infinity" } +} + +fn parseFailure(operation: String, message: String) -> ProvenError { + #{ operation: operation, message: message, errorType: "ParseFailure" } +} + +fn divisionByZero(operation: String, message: String) -> ProvenError { + #{ operation: operation, message: message, errorType: "DivisionByZero" } +} + +fn invalidArgument(operation: String, message: String) -> ProvenError { + #{ operation: operation, message: message, errorType: "InvalidArgument" } +} + +fn toString(err: ProvenError) -> String { + "[" ++ err.errorType ++ "] " ++ err.operation ++ ": " ++ err.message +}