better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
use better_auth_core::utils::json::parse_client_json;
pub(super) use better_auth_core::utils::json::parse_native_json as parse_json;
use better_auth_core::{
    AuthRecordFields, AuthResult, FieldValue, FromFieldMap, Organization, SchemaValue,
};

pub(super) fn metadata(organization: Organization, create: bool) -> AuthResult<Organization> {
    let mut fields = organization.field_values()?;
    let raw = fields.get("metadata").cloned().unwrap_or_default();
    let parsed = if !raw.is_truthy() || (create && !raw.is_string()) {
        FieldValue::Undefined
    } else if create {
        parse_json(&raw)?
    } else {
        parse_client_json(raw)?
    };
    let _ = fields.insert("metadata".into(), parsed);
    Organization::from_field_values(fields)
}

pub(super) fn permission(value: &SchemaValue<FieldValue>) -> AuthResult<FieldValue> {
    parse_json(&value.field_value())
}

#[cfg(test)]
mod tests {
    use super::*;
    use better_auth_core::Utf16String;
    use serde_json::{Value, json};

    fn parsed_metadata(value: FieldValue, create: bool) -> AuthResult<FieldValue> {
        let organization = Organization::from_field_values([("metadata".into(), value)].into())?;
        Ok(metadata(organization, create)?.metadata.field_value())
    }

    #[test]
    fn metadata_dates_follow_the_upstream_component_parser() -> AuthResult<()> {
        let parsed = parsed_metadata(
            r#"{"fraction":"2026-01-02T03:04:05.1234Z","overflow":"2026-02-30T25:00:00+02:00","year":"0099-01-01T00:00:00Z","untouched":"2026-01-02T03:04:05.12345678Z"}"#.into(),
            false,
        )?;
        let fields = parsed.as_object().unwrap().snapshot_fields()?;
        assert!(fields["fraction"].as_date().is_some());
        assert!(fields["overflow"].as_date().is_some());
        assert!(fields["year"].as_date().is_some());
        assert!(fields["untouched"].as_str().is_some());
        assert_eq!(
            parsed.json()?,
            Some(
                json!({"fraction":"2026-01-02T03:04:06.234Z","overflow":"2026-03-02T23:00:00.000Z","year":"1999-01-01T00:00:00.000Z","untouched":"2026-01-02T03:04:05.12345678Z"})
            )
        );
        Ok(())
    }

    #[test]
    fn metadata_parser_preserves_nonfinite_values_until_serialization() -> AuthResult<()> {
        for (text, expected) in [
            ("NaN", f64::NAN),
            ("Infinity", f64::INFINITY),
            ("-Infinity", f64::NEG_INFINITY),
        ] {
            let parsed = parsed_metadata(text.into(), false)?;
            let number = parsed.as_f64().unwrap();
            assert!(number == expected || number.is_nan() && expected.is_nan());
            assert_eq!(parsed.json()?, Some(Value::Null));
        }
        Ok(())
    }

    #[test]
    fn metadata_and_permission_preserve_native_json_values_and_errors() -> AuthResult<()> {
        let raw: FieldValue = Utf16String::from_units(vec![34, 0xd800, 34]).into();
        let expected: FieldValue = Utf16String::from_units(vec![0xd800]).into();
        for create in [true, false] {
            assert_eq!(parsed_metadata(raw.clone(), create)?, expected);
            assert_eq!(
                parsed_metadata("1e400".into(), create)?.as_f64(),
                Some(f64::INFINITY),
            );
            let error = parsed_metadata("invalid".into(), create).unwrap_err();
            assert!(!error.is_api_error()?);
        }
        assert_eq!(permission(&SchemaValue::from_field(raw))?, expected);
        assert!(
            !permission(&SchemaValue::from_field("invalid".into()))
                .unwrap_err()
                .is_api_error()?
        );
        Ok(())
    }
}