@macroforge/mcp-server 0.1.34 → 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/docs/builtin-macros/clone.md +42 -183
- package/docs/builtin-macros/debug.md +33 -239
- package/docs/builtin-macros/default.md +78 -257
- package/docs/builtin-macros/deserialize.md +94 -999
- package/docs/builtin-macros/hash.md +62 -260
- package/docs/builtin-macros/ord.md +70 -251
- package/docs/builtin-macros/partial-eq.md +55 -262
- package/docs/builtin-macros/partial-ord.md +69 -272
- package/docs/builtin-macros/serialize.md +63 -382
- package/docs/concepts/derive-system.md +1 -4
- package/package.json +2 -2
|
@@ -1,285 +1,78 @@
|
|
|
1
1
|
# PartialEq
|
|
2
|
-
*The `PartialEq` macro generates an `equals()` method for field-by-field structural equality comparison. This is analogous to Rust's `PartialEq` trait, enabling value-based equality semantics instead of reference equality.*
|
|
3
|
-
## Basic Usage
|
|
4
|
-
**Before:**
|
|
5
|
-
```
|
|
6
|
-
/** @derive(PartialEq) */
|
|
7
|
-
class Point {
|
|
8
|
-
x: number;
|
|
9
|
-
y: number;
|
|
10
2
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
```
|
|
17
|
-
**After:**
|
|
18
|
-
```
|
|
19
|
-
class Point {
|
|
20
|
-
x: number;
|
|
21
|
-
y: number;
|
|
3
|
+
The `PartialEq` macro generates an `equals()` method for field-by-field
|
|
4
|
+
structural equality comparison. This is analogous to Rust's `PartialEq` trait,
|
|
5
|
+
enabling value-based equality semantics instead of reference equality.
|
|
22
6
|
|
|
23
|
-
|
|
24
|
-
this.x = x;
|
|
25
|
-
this.y = y;
|
|
26
|
-
}
|
|
7
|
+
## Generated Output
|
|
27
8
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
``` ```
|
|
36
|
-
const p1 = new Point(10, 20);
|
|
37
|
-
const p2 = new Point(10, 20);
|
|
38
|
-
const p3 = new Point(5, 5);
|
|
9
|
+
| Type | Generated Code | Description |
|
|
10
|
+
|------|----------------|-------------|
|
|
11
|
+
| Class | `equals(other: unknown): boolean` | Instance method with instanceof check |
|
|
12
|
+
| Enum | `equalsEnumName(a: EnumName, b: EnumName): boolean` | Standalone function using strict equality |
|
|
13
|
+
| Interface | `equalsInterfaceName(a: InterfaceName, b: InterfaceName): boolean` | Standalone function comparing fields |
|
|
14
|
+
| Type Alias | `equalsTypeName(a: TypeName, b: TypeName): boolean` | Standalone function with type-appropriate comparison |
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
console.log(p1.equals(p3)); // false (different values)
|
|
42
|
-
console.log(p1 === p2); // false (different references)
|
|
43
|
-
``` ## How It Works
|
|
44
|
-
The PartialEq macro performs field-by-field comparison using these strategies:
|
|
45
|
-
- **Primitives** (string, number, boolean, null, undefined) → Strict equality (`===`)
|
|
46
|
-
- **Date** → Compares timestamps via `getTime()`
|
|
47
|
-
- **Array** → Length check + element-by-element comparison
|
|
48
|
-
- **Map** → Size check + entry comparison
|
|
49
|
-
- **Set** → Size check + membership verification
|
|
50
|
-
- **Objects** → Calls `equals()` if available, otherwise `===`
|
|
51
|
-
## Field Options
|
|
52
|
-
### @partialEq(skip)
|
|
53
|
-
Use `@partialEq(skip)` to exclude a field from equality comparison:
|
|
54
|
-
**Before:**
|
|
55
|
-
```
|
|
56
|
-
/** @derive(PartialEq) */
|
|
57
|
-
class User {
|
|
58
|
-
id: number;
|
|
59
|
-
name: string;
|
|
16
|
+
## Configuration
|
|
60
17
|
|
|
61
|
-
|
|
62
|
-
|
|
18
|
+
The `functionNamingStyle` option in `macroforge.json` controls naming:
|
|
19
|
+
- `"suffix"` (default): Suffixes with type name (e.g., `equalsMyType`)
|
|
20
|
+
- `"prefix"`: Prefixes with type name (e.g., `myTypeEquals`)
|
|
21
|
+
- `"generic"`: Uses TypeScript generics (e.g., `equals<T extends MyType>`)
|
|
22
|
+
- `"namespace"`: Legacy namespace wrapping
|
|
63
23
|
|
|
64
|
-
|
|
65
|
-
this.id = id;
|
|
66
|
-
this.name = name;
|
|
67
|
-
this.createdAt = createdAt;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
**After:**
|
|
72
|
-
```
|
|
73
|
-
class User {
|
|
74
|
-
id: number;
|
|
75
|
-
name: string;
|
|
24
|
+
## Comparison Strategy
|
|
76
25
|
|
|
77
|
-
|
|
26
|
+
The generated equality check:
|
|
78
27
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.createdAt = createdAt;
|
|
83
|
-
}
|
|
28
|
+
1. **Identity check**: `this === other` returns true immediately
|
|
29
|
+
2. **Type check**: For classes, uses `instanceof`; returns false if wrong type
|
|
30
|
+
3. **Field comparison**: Compares each non-skipped field
|
|
84
31
|
|
|
85
|
-
|
|
86
|
-
if (this === other) return true;
|
|
87
|
-
if (!(other instanceof User)) return false;
|
|
88
|
-
const typedOther = other as User;
|
|
89
|
-
return this.id === typedOther.id && this.name === typedOther.name;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
``` ```
|
|
93
|
-
const user1 = new User(1, "Alice", new Date("2024-01-01"));
|
|
94
|
-
const user2 = new User(1, "Alice", new Date("2024-12-01"));
|
|
32
|
+
## Type-Specific Comparisons
|
|
95
33
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
constructor(name: string) {
|
|
105
|
-
this.name = name;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
``` ```
|
|
109
|
-
const user = new User("Alice");
|
|
34
|
+
| Type | Comparison Method |
|
|
35
|
+
|------|-------------------|
|
|
36
|
+
| Primitives | Strict equality (`===`) |
|
|
37
|
+
| Arrays | Length + element-by-element (recursive) |
|
|
38
|
+
| `Date` | `getTime()` comparison |
|
|
39
|
+
| `Map` | Size + entry-by-entry comparison |
|
|
40
|
+
| `Set` | Size + membership check |
|
|
41
|
+
| Objects | Calls `equals()` if available, else `===` |
|
|
110
42
|
|
|
111
|
-
|
|
112
|
-
console.log(user.equals("Alice")); // false (not a User instance)
|
|
113
|
-
``` ## With Nested Objects
|
|
114
|
-
For objects with nested fields, PartialEq recursively calls `equals()` if available:
|
|
115
|
-
**Source:**
|
|
116
|
-
```
|
|
117
|
-
/** @derive(PartialEq) */
|
|
118
|
-
class Address {
|
|
119
|
-
city: string;
|
|
120
|
-
zip: string;
|
|
43
|
+
## Field-Level Options
|
|
121
44
|
|
|
122
|
-
|
|
123
|
-
this.city = city;
|
|
124
|
-
this.zip = zip;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** @derive(PartialEq) */
|
|
129
|
-
class Person {
|
|
130
|
-
name: string;
|
|
131
|
-
address: Address;
|
|
45
|
+
The `@partialEq` decorator supports:
|
|
132
46
|
|
|
133
|
-
|
|
134
|
-
this.name = name;
|
|
135
|
-
this.address = address;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
``` ```
|
|
139
|
-
const addr1 = new Address("NYC", "10001");
|
|
140
|
-
const addr2 = new Address("NYC", "10001");
|
|
47
|
+
- `skip` - Exclude the field from equality comparison
|
|
141
48
|
|
|
142
|
-
|
|
143
|
-
const p2 = new Person("Alice", addr2);
|
|
49
|
+
## Example
|
|
144
50
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
/** @derive(PartialEq) */
|
|
151
|
-
interface Point {
|
|
152
|
-
x: number;
|
|
153
|
-
y: number;
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
**After:**
|
|
157
|
-
```
|
|
158
|
-
interface Point {
|
|
159
|
-
x: number;
|
|
160
|
-
y: number;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export namespace Point {
|
|
164
|
-
export function equals(self: Point, other: Point): boolean {
|
|
165
|
-
if (self === other) return true;
|
|
166
|
-
return self.x === other.x && self.y === other.y;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
``` ```
|
|
170
|
-
const p1: Point = { x: 10, y: 20 };
|
|
171
|
-
const p2: Point = { x: 10, y: 20 };
|
|
172
|
-
const p3: Point = { x: 5, y: 5 };
|
|
173
|
-
|
|
174
|
-
console.log(Point.equals(p1, p2)); // true
|
|
175
|
-
console.log(Point.equals(p1, p3)); // false
|
|
176
|
-
``` ## Enum Support
|
|
177
|
-
PartialEq works with enums. For enums, strict equality comparison is used:
|
|
178
|
-
**Before:**
|
|
179
|
-
```
|
|
180
|
-
/** @derive(PartialEq) */
|
|
181
|
-
enum Status {
|
|
182
|
-
Active = 'active',
|
|
183
|
-
Inactive = 'inactive',
|
|
184
|
-
Pending = 'pending'
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
**After:**
|
|
188
|
-
```
|
|
189
|
-
enum Status {
|
|
190
|
-
Active = 'active',
|
|
191
|
-
Inactive = 'inactive',
|
|
192
|
-
Pending = 'pending'
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export namespace Status {
|
|
196
|
-
export function equals(a: Status, b: Status): boolean {
|
|
197
|
-
return a === b;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
``` ```
|
|
201
|
-
console.log(Status.equals(Status.Active, Status.Active)); // true
|
|
202
|
-
console.log(Status.equals(Status.Active, Status.Inactive)); // false
|
|
203
|
-
``` ## Type Alias Support
|
|
204
|
-
PartialEq works with type aliases. For object types, field-by-field comparison is used:
|
|
205
|
-
**Before:**
|
|
206
|
-
```
|
|
207
|
-
/** @derive(PartialEq) */
|
|
208
|
-
type Point = {
|
|
209
|
-
x: number;
|
|
210
|
-
y: number;
|
|
211
|
-
};
|
|
212
|
-
```
|
|
213
|
-
**After:**
|
|
214
|
-
```
|
|
215
|
-
type Point = {
|
|
216
|
-
x: number;
|
|
217
|
-
y: number;
|
|
218
|
-
};
|
|
51
|
+
```typescript
|
|
52
|
+
@derive(PartialEq, Hash)
|
|
53
|
+
class User {
|
|
54
|
+
id: number;
|
|
55
|
+
name: string;
|
|
219
56
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return a.x === b.x && a.y === b.y;
|
|
224
|
-
}
|
|
57
|
+
@partialEq(skip) // Don't compare cached values
|
|
58
|
+
@hash(skip)
|
|
59
|
+
cachedScore: number;
|
|
225
60
|
}
|
|
226
|
-
``` ```
|
|
227
|
-
const p1: Point = { x: 10, y: 20 };
|
|
228
|
-
const p2: Point = { x: 10, y: 20 };
|
|
229
61
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
console.log(ApiStatus.equals("success", "error")); // false
|
|
239
|
-
``` ## Common Patterns
|
|
240
|
-
### Finding Items in Arrays
|
|
241
|
-
**Source:**
|
|
62
|
+
// Generated:
|
|
63
|
+
// equals(other: unknown): boolean {
|
|
64
|
+
// if (this === other) return true;
|
|
65
|
+
// if (!(other instanceof User)) return false;
|
|
66
|
+
// const typedOther = other as User;
|
|
67
|
+
// return this.id === typedOther.id &&
|
|
68
|
+
// this.name === typedOther.name;
|
|
69
|
+
// }
|
|
242
70
|
```
|
|
243
|
-
/** @derive(PartialEq) */
|
|
244
|
-
class Product {
|
|
245
|
-
sku: string;
|
|
246
|
-
name: string;
|
|
247
71
|
|
|
248
|
-
|
|
249
|
-
this.sku = sku;
|
|
250
|
-
this.name = name;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
``` ```
|
|
254
|
-
const products = [
|
|
255
|
-
new Product("A1", "Widget"),
|
|
256
|
-
new Product("B2", "Gadget"),
|
|
257
|
-
new Product("C3", "Gizmo")
|
|
258
|
-
];
|
|
259
|
-
|
|
260
|
-
const target = new Product("B2", "Gadget");
|
|
261
|
-
const found = products.find(p => p.equals(target));
|
|
72
|
+
## Equality Contract
|
|
262
73
|
|
|
263
|
-
|
|
264
|
-
``` ### Use with Hash
|
|
265
|
-
When using objects as keys in Map-like structures, combine PartialEq with Hash:
|
|
266
|
-
**Source:**
|
|
267
|
-
```
|
|
268
|
-
/** @derive(PartialEq, Hash) */
|
|
269
|
-
class Key {
|
|
270
|
-
id: number;
|
|
271
|
-
type: string;
|
|
272
|
-
|
|
273
|
-
constructor(id: number, type: string) {
|
|
274
|
-
this.id = id;
|
|
275
|
-
this.type = type;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
``` ```
|
|
279
|
-
const k1 = new Key(1, "user");
|
|
280
|
-
const k2 = new Key(1, "user");
|
|
74
|
+
When implementing `PartialEq`, consider also implementing `Hash`:
|
|
281
75
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
```
|
|
76
|
+
- **Reflexivity**: `a.equals(a)` is always true
|
|
77
|
+
- **Symmetry**: `a.equals(b)` implies `b.equals(a)`
|
|
78
|
+
- **Hash consistency**: Equal objects must have equal hash codes
|