UNPKG

@macroforge/mcp-server

Version:

MCP server for Macroforge documentation and code analysis

145 lines (114 loc) • 5.83 kB
# Encode The `Encode` macro generates JSON encoding methods with **cycle detection** and object identity tracking. This enables encoding of complex object graphs including circular references. ## Generated Methods | Type | Generated Code | Description | | ---------- | ------------------------------------------------------------------------------- | ------------------------------------------- | | Class | `classNameEncode(value, keepMetadata?)` + `static encode(value, keepMetadata?)` | Standalone function + static wrapper method | | Enum | `enumNameEncode(value)`, `enumNameEncodeWithContext` | Standalone functions | | Interface | `interfaceNameEncode(value)`, etc. | Standalone functions | | Type Alias | `typeNameEncode(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 encoded: 1. Check if it's already been encoded (has an `__id`) 2. If so, return `{ "__ref": existingId }` instead 3. Otherwise, register the object and encode its fields ## Type-Specific Encoding | Type | Encoding 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>`, ...) | Encode based on the inner type `T` | | Nullable | Include `null` explicitly; non-null values follow the inner type's strategy | | Objects | Call the type's `nameEncodeWithContext` 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 `@endec` decorator supports: - `skip` / `skipEncoding` - Exclude field from encoding - `rename = "jsonKey"` - Use different JSON property name - `flatten` - Merge nested object's fields into parent ## Example ```typescript before /** @derive(Encode) */ class User { id: number; /** @endec({ rename: "userName" }) */ name: string; /** @endec({ skipEncoding: true }) */ password: string; /** @endec({ flatten: true }) */ metadata: UserMetadata; } ``` ```typescript after import { EncodeContext } from '@macroforge/core/endec'; class User { id: number; name: string; password: string; metadata: UserMetadata; /** Encodes a value to a JSON string. @param value - The value to encode @returns JSON string representation with cycle detection metadata */ static encode(value: User, keepMetadata?: boolean): string { return userEncode(value, keepMetadata); } /** @internal Encodes with an existing context for nested/cyclic object graphs. @param value - The value to encode @param ctx - The encoding context */ static encodeWithContext(value: User, ctx: __mf_EncodeContext): Record<string, unknown> { return userEncodeWithContext(value, ctx); } } /** Encodes a value to a JSON string. @param value - The value to encode @returns JSON string representation with cycle detection metadata */ export function userEncode( value: User, keepMetadata?: boolean ): string { const ctx = __mf_EncodeContext.create(); const __raw = userEncodeWithContext(value, ctx); if (keepMetadata) return JSON.stringify(__raw); return JSON.stringify( __raw, (key, val) => key === '__type' || key === '__id' ? undefined : val ); } /** @internal Encodes with an existing context for nested/cyclic object graphs. @param value - The value to encode @param ctx - The encoding context */ export function userEncodeWithContext( value: User, ctx: EncodeContext ): 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 = userMetadataEncodeWithContext(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 `EncodeContext` from `@macroforge/core/endec`.