# Serialize

The `Serialize` macro generates JSON serialization methods with **cycle detection** and object
identity tracking. This enables serialization of complex object graphs including circular
references.

## Generated Methods

| Type       | Generated Code                                                                        | Description                                 |
| ---------- | ------------------------------------------------------------------------------------- | ------------------------------------------- |
| Class      | `classNameSerialize(value, keepMetadata?)` + `static serialize(value, keepMetadata?)` | Standalone function + static wrapper method |
| Enum       | `enumNameSerialize(value)`, `enumNameSerializeWithContext`                            | Standalone functions                        |
| Interface  | `interfaceNameSerialize(value)`, etc.                                                 | Standalone functions                        |
| Type Alias | `typeNameSerialize(value)`, etc.                                                      | Standalone functions                        |

## Cycle Detection Protocol

The generated code handles circular references using `__id` and `__ref` markers:

```json
{
    "__type": "User",
    "__id": 1,
    "name": "Alice",
    "friend": { "__ref": 2 } // Reference to object with __id: 2
}
```

When an object is serialized:

1. Check if it's already been serialized (has an `__id`)
2. If so, return `{ "__ref": existingId }` instead
3. Otherwise, register the object and serialize its fields

## Type-Specific Serialization

| Type                                      | Serialization Strategy                                                           |
| ----------------------------------------- | -------------------------------------------------------------------------------- |
| Primitives                                | Direct value                                                                     |
| `Date`                                    | `toISOString()`                                                                  |
| Arrays                                    | For primitive-like element types, pass through; for `Date`/`Date                 |
| `Map<K,V>`                                | For primitive-like values, `Object.fromEntries(map.entries())`; for `Date`/`Date |
| `Set<T>`                                  | Convert to array; element handling matches `Array<T>`                            |
| `Record<K,V>`                             | For primitive-like values, pass through; for `Date`/`Date                        |
| Wrappers (`Partial<T>`, `Pick<T,K>`, ...) | Serialize based on the inner type `T`                                            |
| Nullable                                  | Include `null` explicitly; non-null values follow the inner type's strategy      |
| Objects                                   | Call the type's `nameSerializeWithContext` function                              |

Note: which strategy applies is resolved **statically** from the field's declared TypeScript type at
expansion time — there is no runtime feature detection.

## Field-Level Options

The `@serde` decorator supports:

- `skip` / `skipSerializing` - Exclude field from serialization
- `rename = "jsonKey"` - Use different JSON property name
- `flatten` - Merge nested object's fields into parent

## Example

```typescript before
/** @derive(Serialize) */
class User {
    id: number;

    /** @serde({ rename: "userName" }) */
    name: string;

    /** @serde({ skipSerializing: true }) */
    password: string;

    /** @serde({ flatten: true }) */
    metadata: UserMetadata;
}
```

```typescript after
import { SerializeContext } from '@macroforge/core/serde';

class User {
    id: number;

    name: string;

    password: string;

    metadata: UserMetadata;
    /** Serializes a value to a JSON string.
@param value - The value to serialize
@returns JSON string representation with cycle detection metadata  */

    static serialize(value: User, keepMetadata?: boolean): string {
        return userSerialize(value, keepMetadata);
    }
    /** @internal Serializes with an existing context for nested/cyclic object graphs.
@param value - The value to serialize
@param ctx - The serialization context  */

    static serializeWithContext(value: User, ctx: __mf_SerializeContext): Record<string, unknown> {
        return userSerializeWithContext(value, ctx);
    }
}

/** Serializes a value to a JSON string.
@param value - The value to serialize
@returns JSON string representation with cycle detection metadata */ export function userSerialize(
    value: User,
    keepMetadata?: boolean
): string {
    const ctx = __mf_SerializeContext.create();
    const __raw = userSerializeWithContext(value, ctx);
    if (keepMetadata) return JSON.stringify(__raw);
    return JSON.stringify(
        __raw,
        (key, val) => key === '__type' || key === '__id' ? undefined : val
    );
} /** @internal Serializes with an existing context for nested/cyclic object graphs.
@param value - The value to serialize
@param ctx - The serialization context */

export function userSerializeWithContext(
    value: User,
    ctx: SerializeContext
): Record<string, unknown> {
    const existingId = ctx.getId(value);
    if (existingId !== undefined) {
        return { __ref: existingId };
    }
    const __id = ctx.register(value);
    const result: Record<string, unknown> = { __type: 'User', __id };
    result['id'] = value.id;
    result['userName'] = value.name;
    {
        const __flattened = userMetadataSerializeWithContext(value.metadata, ctx);
        const { __type: _, __id: __, ...rest } = __flattened as any; // tag field name is configurable via `tag` option
        Object.assign(result, rest);
    }
    return result;
}
```

## Required Import

The generated code automatically imports `SerializeContext` from `@macroforge/core/serde`.
