# 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)` + `static serialize(value)` | 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 | null`, map to ISO strings; otherwise map and call `SerializeWithContext(ctx)` when available |
| `Map<K,V>` | For primitive-like values, `Object.fromEntries(map.entries())`; for `Date`/`Date | null`, convert to ISO strings; otherwise call `SerializeWithContext(ctx)` per value when available |
| `Set<T>` | Convert to array; element handling matches `Array<T>` |
| Nullable | Include `null` explicitly; for primitive-like and `Date` unions the generator avoids runtime `SerializeWithContext` checks |
| Objects | Call `SerializeWithContext(ctx)` if available (to support user-defined implementations) |

Note: the generator specializes some code paths based on the declared TypeScript type to
avoid runtime feature detection on primitives and literal unions.

## 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/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): string {
        return userSerialize(value);
    }
    /** @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: @SERIALIZE_CONTEXT): 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
): string {
    const ctx = @SERIALIZE_CONTEXT.create();
    return JSON.stringify(userSerializeWithContext(value, ctx));
} /** @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/serde`.
