@macroforge/mcp-server 0.1.33 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/docs-loader.d.ts +133 -5
- package/dist/tools/docs-loader.d.ts.map +1 -1
- package/dist/tools/docs-loader.js +131 -15
- package/dist/tools/docs-loader.js.map +1 -1
- package/dist/tools/index.d.ts +48 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +163 -14
- package/dist/tools/index.js.map +1 -1
- package/docs/api/api-overview.md +24 -46
- package/docs/api/expand-sync.md +24 -51
- package/docs/api/native-plugin.md +24 -56
- package/docs/api/position-mapper.md +34 -76
- package/docs/api/transform-sync.md +27 -59
- package/docs/builtin-macros/clone.md +45 -104
- package/docs/builtin-macros/debug.md +33 -104
- package/docs/builtin-macros/default.md +78 -114
- package/docs/builtin-macros/deserialize.md +93 -273
- package/docs/builtin-macros/hash.md +58 -100
- package/docs/builtin-macros/macros-overview.md +42 -103
- package/docs/builtin-macros/ord.md +65 -133
- package/docs/builtin-macros/partial-eq.md +53 -179
- package/docs/builtin-macros/partial-ord.md +67 -159
- package/docs/builtin-macros/serialize.md +64 -194
- package/docs/concepts/architecture.md +40 -99
- package/docs/concepts/derive-system.md +129 -125
- package/docs/concepts/how-macros-work.md +52 -84
- package/docs/custom-macros/custom-overview.md +17 -39
- package/docs/custom-macros/rust-setup.md +22 -55
- package/docs/custom-macros/ts-macro-derive.md +43 -107
- package/docs/custom-macros/ts-quote.md +177 -507
- package/docs/getting-started/first-macro.md +108 -33
- package/docs/getting-started/installation.md +32 -73
- package/docs/integration/cli.md +70 -156
- package/docs/integration/configuration.md +32 -75
- package/docs/integration/integration-overview.md +16 -55
- package/docs/integration/mcp-server.md +30 -69
- package/docs/integration/svelte-preprocessor.md +60 -83
- package/docs/integration/typescript-plugin.md +32 -74
- package/docs/integration/vite-plugin.md +30 -79
- package/docs/language-servers/ls-overview.md +22 -46
- package/docs/language-servers/svelte.md +30 -69
- package/docs/language-servers/zed.md +34 -72
- package/docs/roadmap/roadmap.md +54 -130
- package/docs/sections.json +3 -262
- package/package.json +2 -2
|
@@ -1,138 +1,102 @@
|
|
|
1
1
|
# Default
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `Default` macro generates a static `defaultValue()` factory method that creates
|
|
4
|
+
instances with default values. This is analogous to Rust's `Default` trait, providing
|
|
5
|
+
a standard way to create "zero" or "empty" instances of types.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Generated Output
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
| Type | Generated Code | Description |
|
|
10
|
+
|------|----------------|-------------|
|
|
11
|
+
| Class | `static defaultValue(): ClassName` | Static factory method |
|
|
12
|
+
| Enum | `defaultValueEnumName(): EnumName` | Standalone function returning marked variant |
|
|
13
|
+
| Interface | `defaultValueInterfaceName(): InterfaceName` | Standalone function returning object literal |
|
|
14
|
+
| Type Alias | `defaultValueTypeName(): TypeName` | Standalone function with type-appropriate default |
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
const config = Config.defaultValue();
|
|
11
|
-
console.log(config.host); // ""
|
|
12
|
-
console.log(config.port); // 0
|
|
13
|
-
console.log(config.enabled); // false
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Automatic Default Values
|
|
17
|
-
|
|
18
|
-
Like Rust's `Default` trait, the macro automatically determines default values for primitive types and common collections:
|
|
19
|
-
|
|
20
|
-
| `string` | `""` | `String::default()`
|
|
21
|
-
|
|
22
|
-
| `number` | `0` | `i32::default()`
|
|
23
|
-
|
|
24
|
-
| `boolean` | `false` | `bool::default()`
|
|
25
|
-
|
|
26
|
-
| `bigint` | `0n` | `i64::default()`
|
|
27
|
-
|
|
28
|
-
| `T[]` / `Array<T>` | `[]` | `Vec::default()`
|
|
29
|
-
|
|
30
|
-
| `Map<K, V>` | `new Map()` | `HashMap::default()`
|
|
31
|
-
|
|
32
|
-
| `Set<T>` | `new Set()` | `HashSet::default()`
|
|
33
|
-
|
|
34
|
-
| `Date` | `new Date()` | —
|
|
35
|
-
|
|
36
|
-
| `T | null` / `T | undefined` | `null` | `Option::default()`
|
|
37
|
-
|
|
38
|
-
| Custom types | **Error** | **Error** (needs `impl Default`)
|
|
39
|
-
|
|
40
|
-
## Nullable Types (like Rust's Option)
|
|
41
|
-
|
|
42
|
-
Just like Rust's `Option<T>` defaults to `None`, nullable TypeScript types automatically default to `null`:
|
|
43
|
-
|
|
44
|
-
<MacroExample before={data.examples.nullable.before} after={data.examples.nullable.after} />
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
const user = User.defaultValue();
|
|
48
|
-
console.log(user.name); // ""
|
|
49
|
-
console.log(user.email); // null (nullable type)
|
|
50
|
-
console.log(user.age); // 0
|
|
51
|
-
console.log(user.metadata); // null (nullable type)
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Custom Types Require @default
|
|
55
|
-
|
|
56
|
-
Just like Rust requires `impl Default` for custom types, Macroforge requires the `@default()` decorator on fields with non-primitive types:
|
|
57
|
-
|
|
58
|
-
<MacroExample before={data.examples.customType.before} after={data.examples.customType.after} />
|
|
59
|
-
|
|
60
|
-
<p class="text-red-500 text-sm mt-2">
|
|
61
|
-
Without `@default` on custom type fields, the macro will emit an error:
|
|
62
|
-
</p>
|
|
16
|
+
## Configuration
|
|
63
17
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
18
|
+
The `functionNamingStyle` option in `macroforge.json` controls naming:
|
|
19
|
+
- `"suffix"` (default): Suffixes with type name (e.g., `defaultValueMyType`)
|
|
20
|
+
- `"prefix"`: Prefixes with type name (e.g., `myTypeDefaultValue`)
|
|
21
|
+
- `"generic"`: Uses TypeScript generics (e.g., `defaultValue<T extends MyType>`)
|
|
22
|
+
- `"namespace"`: Legacy namespace wrapping
|
|
68
23
|
|
|
69
|
-
##
|
|
24
|
+
## Default Values by Type
|
|
70
25
|
|
|
71
|
-
|
|
26
|
+
The macro uses Rust-like default semantics:
|
|
72
27
|
|
|
73
|
-
|
|
28
|
+
| Type | Default Value |
|
|
29
|
+
|------|---------------|
|
|
30
|
+
| `string` | `""` (empty string) |
|
|
31
|
+
| `number` | `0` |
|
|
32
|
+
| `boolean` | `false` |
|
|
33
|
+
| `bigint` | `0n` |
|
|
34
|
+
| `T[]` | `[]` (empty array) |
|
|
35
|
+
| `Array<T>` | `[]` (empty array) |
|
|
36
|
+
| `Map<K,V>` | `new Map()` |
|
|
37
|
+
| `Set<T>` | `new Set()` |
|
|
38
|
+
| `Date` | `new Date()` (current time) |
|
|
39
|
+
| `T \| null` | `null` |
|
|
40
|
+
| `CustomType` | `CustomType.defaultValue()` (recursive) |
|
|
74
41
|
|
|
75
|
-
|
|
76
|
-
const config = ServerConfig.defaultValue();
|
|
77
|
-
console.log(config.host); // "localhost"
|
|
78
|
-
console.log(config.port); // 8080
|
|
79
|
-
console.log(config.enabled); // true
|
|
80
|
-
console.log(config.logLevels); // ["info", "error"]
|
|
81
|
-
```
|
|
42
|
+
## Field-Level Options
|
|
82
43
|
|
|
83
|
-
|
|
44
|
+
The `@default` decorator allows specifying explicit default values:
|
|
84
45
|
|
|
85
|
-
|
|
46
|
+
- `@default(42)` - Use 42 as the default
|
|
47
|
+
- `@default("hello")` - Use "hello" as the default
|
|
48
|
+
- `@default([])` - Use empty array as the default
|
|
49
|
+
- `@default({ value: "test" })` - Named form for complex values
|
|
86
50
|
|
|
87
|
-
|
|
51
|
+
## Example
|
|
88
52
|
|
|
89
53
|
```typescript
|
|
90
|
-
|
|
91
|
-
|
|
54
|
+
@derive(Default)
|
|
55
|
+
class UserSettings {
|
|
56
|
+
@default("light")
|
|
57
|
+
theme: string;
|
|
58
|
+
|
|
59
|
+
@default(10)
|
|
60
|
+
pageSize: number;
|
|
61
|
+
|
|
62
|
+
notifications: boolean; // Uses type default: false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Generated:
|
|
66
|
+
// static defaultValue(): UserSettings {
|
|
67
|
+
// const instance = new UserSettings();
|
|
68
|
+
// instance.theme = "light";
|
|
69
|
+
// instance.pageSize = 10;
|
|
70
|
+
// instance.notifications = false;
|
|
71
|
+
// return instance;
|
|
72
|
+
// }
|
|
92
73
|
```
|
|
93
74
|
|
|
94
|
-
## Enum
|
|
75
|
+
## Enum Defaults
|
|
95
76
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<MacroExample before={data.examples.enum.before} after={data.examples.enum.after} />
|
|
77
|
+
For enums, mark one variant with `@default`:
|
|
99
78
|
|
|
100
79
|
```typescript
|
|
101
|
-
|
|
102
|
-
|
|
80
|
+
@derive(Default)
|
|
81
|
+
enum Status {
|
|
82
|
+
@default
|
|
83
|
+
Pending,
|
|
84
|
+
Active,
|
|
85
|
+
Completed
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Generated:
|
|
89
|
+
// export namespace Status {
|
|
90
|
+
// export function defaultValue(): Status {
|
|
91
|
+
// return Status.Pending;
|
|
92
|
+
// }
|
|
93
|
+
// }
|
|
103
94
|
```
|
|
104
95
|
|
|
105
|
-
##
|
|
106
|
-
|
|
107
|
-
Default works with type aliases. For object types, it creates an object with default field values:
|
|
96
|
+
## Error Handling
|
|
108
97
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
const dims = Dimensions.defaultValue();
|
|
113
|
-
console.log(dims); // { width: 0, height: 0 }
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Combining with Other Macros
|
|
117
|
-
|
|
118
|
-
<InteractiveMacro code={`/** @derive(Default, Debug, Clone, PartialEq) */
|
|
119
|
-
class User {
|
|
120
|
-
/** @default("Anonymous") */
|
|
121
|
-
name: string;
|
|
122
|
-
|
|
123
|
-
/** @default(0) */
|
|
124
|
-
age: number;
|
|
125
|
-
|
|
126
|
-
constructor(name: string, age: number) {
|
|
127
|
-
this.name = name;
|
|
128
|
-
this.age = age;
|
|
129
|
-
}
|
|
130
|
-
}`} />
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
const user1 = User.defaultValue();
|
|
134
|
-
const user2 = user1.clone();
|
|
98
|
+
The macro will return an error if:
|
|
135
99
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
100
|
+
- A non-primitive field lacks `@default` and has no known default
|
|
101
|
+
- An enum has no variant marked with `@default`
|
|
102
|
+
- A union type has no `@default` on a variant
|
|
@@ -1,317 +1,137 @@
|
|
|
1
1
|
# Deserialize
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `Deserialize` macro generates JSON deserialization methods with **cycle and
|
|
4
|
+
forward-reference support**, plus comprehensive runtime validation. This enables
|
|
5
|
+
safe parsing of complex JSON structures including circular references.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Generated Output
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
| Type | Generated Code | Description |
|
|
10
|
+
|------|----------------|-------------|
|
|
11
|
+
| Class | `static fromStringifiedJSON()`, `static fromObject()`, `static __deserialize()` | Static factory methods |
|
|
12
|
+
| Enum | `fromStringifiedJSONEnumName(json)`, `__deserializeEnumName(data)`, `isEnumName(value)` | Standalone functions |
|
|
13
|
+
| Interface | `fromStringifiedJSONInterfaceName(json)`, `fromObjectInterfaceName(obj)`, etc. | Standalone functions |
|
|
14
|
+
| Type Alias | `fromStringifiedJSONTypeName(json)`, `fromObjectTypeName(obj)`, etc. | Standalone functions |
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
const json = '{"name":"Alice","age":30,"createdAt":"2024-01-15T10:30:00.000Z"}';
|
|
11
|
-
const user = User.fromJSON(JSON.parse(json));
|
|
16
|
+
## Configuration
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
The `functionNamingStyle` option in `macroforge.json` controls naming:
|
|
19
|
+
- `"suffix"` (default): Suffixes with type name (e.g., `fromStringifiedJSONMyType`)
|
|
20
|
+
- `"prefix"`: Prefixes with type name (e.g., `myTypeFromStringifiedJSON`)
|
|
21
|
+
- `"generic"`: Uses TypeScript generics (e.g., `fromStringifiedJSON<T extends MyType>`)
|
|
22
|
+
- `"namespace"`: Legacy namespace wrapping
|
|
17
23
|
|
|
18
|
-
##
|
|
24
|
+
## Return Type
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
All public deserialization methods return `Result<T, Array<{ field: string; message: string }>>`:
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
name: string;
|
|
25
|
-
email: string;
|
|
26
|
-
}`} />
|
|
28
|
+
- `Result.ok(value)` - Successfully deserialized value
|
|
29
|
+
- `Result.err(errors)` - Array of validation errors with field names and messages
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
// Missing required field
|
|
30
|
-
User.fromJSON({ name: "Alice" });
|
|
31
|
-
// Error: User.fromJSON: missing required field "email"
|
|
31
|
+
## Cycle/Forward-Reference Support
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
User.fromJSON("not an object");
|
|
35
|
-
// Error: User.fromJSON: expected an object, got string
|
|
33
|
+
Uses deferred patching to handle references:
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```
|
|
35
|
+
1. When encountering `{ "__ref": id }`, returns a `PendingRef` marker
|
|
36
|
+
2. Continues deserializing other fields
|
|
37
|
+
3. After all objects are created, `ctx.applyPatches()` resolves all pending references
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
References only apply to object-shaped, serializable values. The generator avoids probing for
|
|
40
|
+
`__ref` on primitive-like fields (including literal unions and `T | null` where `T` is primitive-like),
|
|
41
|
+
and it parses `Date` / `Date | null` from ISO strings without treating them as references.
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
## Validation
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
| `string`/`number`/`boolean`
|
|
48
|
-
| Direct assignment
|
|
45
|
+
The macro supports 30+ validators via `@serde(validate(...))`:
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
### String Validators
|
|
48
|
+
- `email`, `url`, `uuid` - Format validation
|
|
49
|
+
- `minLength(n)`, `maxLength(n)`, `length(n)` - Length constraints
|
|
50
|
+
- `pattern("regex")` - Regular expression matching
|
|
51
|
+
- `nonEmpty`, `trimmed`, `lowercase`, `uppercase` - String properties
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
### Number Validators
|
|
54
|
+
- `gt(n)`, `gte(n)`, `lt(n)`, `lte(n)`, `between(min, max)` - Range checks
|
|
55
|
+
- `int`, `positive`, `nonNegative`, `finite` - Number properties
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
| `new Map(Object.entries())`
|
|
57
|
+
### Array Validators
|
|
58
|
+
- `minItems(n)`, `maxItems(n)`, `itemsCount(n)` - Collection size
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| `new Set(array)`
|
|
60
|
+
### Date Validators
|
|
61
|
+
- `validDate`, `afterDate("ISO")`, `beforeDate("ISO")` - Date validation
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
| Nested class
|
|
68
|
-
| Calls `fromJSON()` if available
|
|
63
|
+
## Field-Level Options
|
|
69
64
|
|
|
70
|
-
|
|
65
|
+
The `@serde` decorator supports:
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
const user = User.fromJSON({ user_id: "123", full_name: "Alice" });
|
|
80
|
-
console.log(user.id); // "123"
|
|
81
|
-
console.log(user.name); // "Alice"
|
|
82
|
-
```
|
|
67
|
+
- `skip` / `skip_deserializing` - Exclude field from deserialization
|
|
68
|
+
- `rename = "jsonKey"` - Read from different JSON property
|
|
69
|
+
- `default` / `default = expr` - Use default value if missing
|
|
70
|
+
- `flatten` - Read fields from parent object level
|
|
71
|
+
- `validate(...)` - Apply validators
|
|
83
72
|
|
|
84
|
-
|
|
73
|
+
## Container-Level Options
|
|
85
74
|
|
|
86
|
-
|
|
75
|
+
- `deny_unknown_fields` - Error on unrecognized JSON properties
|
|
76
|
+
- `rename_all = "camelCase"` - Apply naming convention to all fields
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
const config = Config.fromJSON({ host: "localhost" });
|
|
90
|
-
console.log(config.port); // "3000"
|
|
91
|
-
console.log(config.debug); // false
|
|
92
|
-
```
|
|
78
|
+
## Union Type Deserialization
|
|
93
79
|
|
|
94
|
-
|
|
80
|
+
Union types are deserialized based on their member types:
|
|
95
81
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
email: string;
|
|
82
|
+
### Literal Unions
|
|
83
|
+
For unions of literal values (`"A" | "B" | 123`), the value is validated against
|
|
84
|
+
the allowed literals directly.
|
|
100
85
|
|
|
101
|
-
|
|
102
|
-
|
|
86
|
+
### Primitive Unions
|
|
87
|
+
For unions containing primitive types (`string | number`), the deserializer uses
|
|
88
|
+
`typeof` checks to validate the value type. No `__type` discriminator is needed.
|
|
103
89
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
90
|
+
### Class/Interface Unions
|
|
91
|
+
For unions of serializable types (`User | Admin`), the deserializer requires a
|
|
92
|
+
`__type` field in the JSON to dispatch to the correct type's `__deserialize` method.
|
|
107
93
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
</Alert>
|
|
94
|
+
### Generic Type Parameters
|
|
95
|
+
For generic unions like `type Result<T> = T | Error`, the generic type parameter `T`
|
|
96
|
+
is passed through as-is since its concrete type is only known at the call site.
|
|
112
97
|
|
|
113
|
-
###
|
|
98
|
+
### Mixed Unions
|
|
99
|
+
Mixed unions (e.g., `string | Date | User`) check in order:
|
|
100
|
+
1. Literal values
|
|
101
|
+
2. Primitives (via `typeof`)
|
|
102
|
+
3. Date (via `instanceof` or ISO string parsing)
|
|
103
|
+
4. Serializable types (via `__type` dispatch)
|
|
104
|
+
5. Generic type parameters (pass-through)
|
|
114
105
|
|
|
115
|
-
|
|
116
|
-
/** @serde({ deny_unknown_fields: true }) */
|
|
117
|
-
class StrictUser {
|
|
118
|
-
name: string;
|
|
119
|
-
email: string;
|
|
120
|
-
}`} />
|
|
106
|
+
## Example
|
|
121
107
|
|
|
122
108
|
```typescript
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
// Error: StrictUser.fromJSON: unknown field "extra"
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Flatten Nested Objects
|
|
129
|
-
|
|
130
|
-
<InteractiveMacro code={`/** @derive(Deserialize) */
|
|
131
|
-
class Address {
|
|
132
|
-
city: string;
|
|
133
|
-
zip: string;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/** @derive(Deserialize) */
|
|
109
|
+
@derive(Deserialize)
|
|
110
|
+
@serde(deny_unknown_fields)
|
|
137
111
|
class User {
|
|
138
|
-
|
|
112
|
+
id: number;
|
|
139
113
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}`} />
|
|
114
|
+
@serde(validate(email, maxLength(255)))
|
|
115
|
+
email: string;
|
|
143
116
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const user = User.fromJSON({
|
|
147
|
-
name: "Alice",
|
|
148
|
-
city: "NYC",
|
|
149
|
-
zip: "10001"
|
|
150
|
-
});
|
|
151
|
-
console.log(user.address.city); // "NYC"
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## All Options
|
|
155
|
-
|
|
156
|
-
### Container Options (on class/interface)
|
|
157
|
-
|
|
158
|
-
| `rename_all`
|
|
159
|
-
| `string`
|
|
160
|
-
| Apply naming convention to all fields
|
|
161
|
-
|
|
162
|
-
| `deny_unknown_fields`
|
|
163
|
-
| `boolean`
|
|
164
|
-
| Throw error if JSON has unknown keys
|
|
165
|
-
|
|
166
|
-
### Field Options (on properties)
|
|
167
|
-
|
|
168
|
-
| `rename`
|
|
169
|
-
| `string`
|
|
170
|
-
| Use a different JSON key
|
|
171
|
-
|
|
172
|
-
| `skip`
|
|
173
|
-
| `boolean`
|
|
174
|
-
| Exclude from serialization and deserialization
|
|
175
|
-
|
|
176
|
-
| `skip_deserializing`
|
|
177
|
-
| `boolean`
|
|
178
|
-
| Exclude from deserialization only
|
|
179
|
-
|
|
180
|
-
| `default`
|
|
181
|
-
| `boolean | string`
|
|
182
|
-
| Use TypeScript default or custom expression if missing
|
|
183
|
-
|
|
184
|
-
| `flatten`
|
|
185
|
-
| `boolean`
|
|
186
|
-
| Merge nested object fields from parent
|
|
187
|
-
|
|
188
|
-
## Interface Support
|
|
189
|
-
|
|
190
|
-
Deserialize also works with interfaces. For interfaces, a namespace is generated with `is` (type guard) and `fromJSON` functions:
|
|
191
|
-
|
|
192
|
-
<MacroExample before={data.examples.interface.before} after={data.examples.interface.after} />
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
const json = { status: 200, message: "OK", timestamp: "2024-01-15T10:30:00.000Z" };
|
|
117
|
+
@serde(default = "guest")
|
|
118
|
+
name: string;
|
|
196
119
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
console.log(json.status); // TypeScript knows this is ApiResponse
|
|
120
|
+
@serde(validate(positive))
|
|
121
|
+
age?: number;
|
|
200
122
|
}
|
|
201
123
|
|
|
202
|
-
//
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
Deserialize also works with enums. The `fromJSON` function validates that the input matches one of the enum values:
|
|
210
|
-
|
|
211
|
-
<MacroExample before={data.examples.enum.before} after={data.examples.enum.after} />
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
const status = Status.fromJSON("active");
|
|
215
|
-
console.log(status); // Status.Active
|
|
216
|
-
|
|
217
|
-
// Invalid values throw an error
|
|
218
|
-
try {
|
|
219
|
-
Status.fromJSON("invalid");
|
|
220
|
-
} catch (e) {
|
|
221
|
-
console.log(e.message); // "Invalid Status value: invalid"
|
|
124
|
+
// Usage:
|
|
125
|
+
const result = User.fromStringifiedJSON('{"id":1,"email":"test@example.com"}');
|
|
126
|
+
if (Result.isOk(result)) {
|
|
127
|
+
const user = result.value;
|
|
128
|
+
} else {
|
|
129
|
+
console.error(result.error); // [{ field: "email", message: "must be a valid email" }]
|
|
222
130
|
}
|
|
223
131
|
```
|
|
224
132
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
<InteractiveMacro code={`/** @derive(Deserialize) */
|
|
228
|
-
enum Priority {
|
|
229
|
-
Low = 1,
|
|
230
|
-
Medium = 2,
|
|
231
|
-
High = 3,
|
|
232
|
-
}`} />
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
const priority = Priority.fromJSON(3);
|
|
236
|
-
console.log(priority); // Priority.High
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
## Type Alias Support
|
|
240
|
-
|
|
241
|
-
Deserialize works with type aliases. For object types, validation and type conversion is applied:
|
|
242
|
-
|
|
243
|
-
<MacroExample before={data.examples.typeAlias.before} after={data.examples.typeAlias.after} />
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
const json = {
|
|
247
|
-
id: "123",
|
|
248
|
-
name: "Alice",
|
|
249
|
-
createdAt: "2024-01-15T00:00:00.000Z"
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const profile = UserProfile.fromJSON(json);
|
|
253
|
-
console.log(profile.createdAt instanceof Date); // true
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
For union types, basic validation is applied:
|
|
257
|
-
|
|
258
|
-
<InteractiveMacro code={`/** @derive(Deserialize) */
|
|
259
|
-
type ApiStatus = "loading" | "success" | "error";`} />
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
const status = ApiStatus.fromJSON("success");
|
|
263
|
-
console.log(status); // "success"
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## Combining with Serialize
|
|
267
|
-
|
|
268
|
-
Use both Serialize and Deserialize for complete JSON round-trip support:
|
|
269
|
-
|
|
270
|
-
<InteractiveMacro code={`/** @derive(Serialize, Deserialize) */
|
|
271
|
-
/** @serde({ rename_all: "camelCase" }) */
|
|
272
|
-
class UserProfile {
|
|
273
|
-
user_name: string;
|
|
274
|
-
created_at: Date;
|
|
275
|
-
is_active: boolean;
|
|
276
|
-
}`} />
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
// Create and serialize
|
|
280
|
-
const profile = new UserProfile();
|
|
281
|
-
profile.user_name = "Alice";
|
|
282
|
-
profile.created_at = new Date();
|
|
283
|
-
profile.is_active = true;
|
|
284
|
-
|
|
285
|
-
const json = JSON.stringify(profile);
|
|
286
|
-
// {"userName":"Alice","createdAt":"2024-...","isActive":true}
|
|
287
|
-
|
|
288
|
-
// Deserialize back
|
|
289
|
-
const restored = UserProfile.fromJSON(JSON.parse(json));
|
|
290
|
-
console.log(restored.user_name); // "Alice"
|
|
291
|
-
console.log(restored.created_at instanceof Date); // true
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
## Error Handling
|
|
295
|
-
|
|
296
|
-
Handle deserialization errors gracefully:
|
|
297
|
-
|
|
298
|
-
<InteractiveMacro code={`/** @derive(Deserialize) */
|
|
299
|
-
class User {
|
|
300
|
-
name: string;
|
|
301
|
-
email: string;
|
|
302
|
-
}`} />
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
function parseUser(json: unknown): User | null {
|
|
306
|
-
try {
|
|
307
|
-
return User.fromJSON(json);
|
|
308
|
-
} catch (error) {
|
|
309
|
-
console.error("Failed to parse user:", error.message);
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
133
|
+
## Required Imports
|
|
313
134
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
```
|
|
135
|
+
The generated code automatically imports:
|
|
136
|
+
- `Result` from `macroforge/utils`
|
|
137
|
+
- `DeserializeContext`, `DeserializeError`, `PendingRef` from `macroforge/serde`
|