@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,299 +1,96 @@
|
|
|
1
1
|
# PartialOrd
|
|
2
|
-
*The `PartialOrd` macro generates a `compareTo()` method for **partial ordering** comparison. This is analogous to Rust's `PartialOrd` trait, enabling comparison between values where some pairs may be incomparable.*
|
|
3
|
-
## Basic Usage
|
|
4
|
-
**Before:**
|
|
5
|
-
```
|
|
6
|
-
/** @derive(PartialOrd) */
|
|
7
|
-
class Temperature {
|
|
8
|
-
celsius: number;
|
|
9
2
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
```
|
|
15
|
-
**After:**
|
|
16
|
-
```
|
|
17
|
-
import { Option } from 'macroforge/utils';
|
|
3
|
+
The `PartialOrd` macro generates a `compareTo()` method for **partial ordering**
|
|
4
|
+
comparison. This is analogous to Rust's `PartialOrd` trait, enabling comparison
|
|
5
|
+
between values where some pairs may be incomparable.
|
|
18
6
|
|
|
19
|
-
|
|
20
|
-
celsius: number;
|
|
7
|
+
## Generated Output
|
|
21
8
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
9
|
+
| Type | Generated Code | Description |
|
|
10
|
+
|------|----------------|-------------|
|
|
11
|
+
| Class | `compareTo(other): Option<number>` | Instance method with optional result |
|
|
12
|
+
| Enum | `partialCompareEnumName(a: EnumName, b: EnumName): Option<number>` | Standalone function returning Option |
|
|
13
|
+
| Interface | `partialCompareInterfaceName(a: InterfaceName, b: InterfaceName): Option<number>` | Standalone function with Option |
|
|
14
|
+
| Type Alias | `partialCompareTypeName(a: TypeName, b: TypeName): Option<number>` | Standalone function with Option |
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
if (this === other) return Option.some(0);
|
|
28
|
-
if (!(other instanceof Temperature)) return Option.none();
|
|
29
|
-
const typedOther = other as Temperature;
|
|
30
|
-
const cmp0 =
|
|
31
|
-
this.celsius < typedOther.celsius ? -1 : this.celsius > typedOther.celsius ? 1 : 0;
|
|
32
|
-
if (cmp0 === null) return Option.none();
|
|
33
|
-
if (cmp0 !== 0) return Option.some(cmp0);
|
|
34
|
-
return Option.some(0);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
``` ```
|
|
38
|
-
const t1 = new Temperature(20);
|
|
39
|
-
const t2 = new Temperature(30);
|
|
40
|
-
const t3 = new Temperature(20);
|
|
16
|
+
## Configuration
|
|
41
17
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
18
|
+
The `functionNamingStyle` option in `macroforge.json` controls naming:
|
|
19
|
+
- `"suffix"` (default): Suffixes with type name (e.g., `partialCompareMyType`)
|
|
20
|
+
- `"prefix"`: Prefixes with type name (e.g., `myTypePartialCompare`)
|
|
21
|
+
- `"generic"`: Uses TypeScript generics (e.g., `partialCompare<T extends MyType>`)
|
|
22
|
+
- `"namespace"`: Legacy namespace wrapping
|
|
45
23
|
|
|
46
|
-
|
|
47
|
-
console.log(t1.compareTo("not a Temperature")); // null
|
|
48
|
-
``` ## Return Values
|
|
49
|
-
The `compareTo()` method returns:
|
|
50
|
-
- `-1` → `this` is less than `other`
|
|
51
|
-
- `0` → `this` equals `other`
|
|
52
|
-
- `1` → `this` is greater than `other`
|
|
53
|
-
- `null` → Values are incomparable (e.g., different types)
|
|
54
|
-
## Comparison Logic
|
|
55
|
-
The PartialOrd macro compares fields in declaration order with type checking:
|
|
56
|
-
- `number` / `bigint` → Direct numeric comparison
|
|
57
|
-
- `string` → Uses `localeCompare()`
|
|
58
|
-
- `boolean` → `false < true`
|
|
59
|
-
- `Date` → Compares timestamps; returns `null` if not both Date instances
|
|
60
|
-
- `Array` → Lexicographic comparison; returns `null` if not both arrays
|
|
61
|
-
- `Object` → Calls `compareTo()` if available
|
|
62
|
-
- **Type mismatch** → Returns `null`
|
|
63
|
-
## Field Options
|
|
64
|
-
### @ord(skip)
|
|
65
|
-
Use `@ord(skip)` to exclude a field from ordering comparison:
|
|
66
|
-
**Before:**
|
|
67
|
-
```
|
|
68
|
-
/** @derive(PartialOrd) */
|
|
69
|
-
class Item {
|
|
70
|
-
price: number;
|
|
71
|
-
name: string;
|
|
24
|
+
## Return Values
|
|
72
25
|
|
|
73
|
-
|
|
74
|
-
description: string;
|
|
26
|
+
Unlike `Ord`, `PartialOrd` returns an `Option<number>` to handle incomparable values:
|
|
75
27
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
**After:**
|
|
84
|
-
```
|
|
85
|
-
import { Option } from 'macroforge/utils';
|
|
28
|
+
- **Option.some(-1)**: `this` is less than `other`
|
|
29
|
+
- **Option.some(0)**: `this` is equal to `other`
|
|
30
|
+
- **Option.some(1)**: `this` is greater than `other`
|
|
31
|
+
- **Option.none()**: Values are incomparable
|
|
86
32
|
|
|
87
|
-
|
|
88
|
-
price: number;
|
|
89
|
-
name: string;
|
|
33
|
+
## When to Use PartialOrd vs Ord
|
|
90
34
|
|
|
91
|
-
|
|
35
|
+
- **PartialOrd**: When some values may not be comparable
|
|
36
|
+
- Example: Floating-point NaN values
|
|
37
|
+
- Example: Mixed-type unions
|
|
38
|
+
- Example: Type mismatches between objects
|
|
92
39
|
|
|
93
|
-
|
|
94
|
-
this.price = price;
|
|
95
|
-
this.name = name;
|
|
96
|
-
this.description = description;
|
|
97
|
-
}
|
|
40
|
+
- **Ord**: When all values are guaranteed comparable (total ordering)
|
|
98
41
|
|
|
99
|
-
|
|
100
|
-
if (this === other) return Option.some(0);
|
|
101
|
-
if (!(other instanceof Item)) return Option.none();
|
|
102
|
-
const typedOther = other as Item;
|
|
103
|
-
const cmp0 = this.price < typedOther.price ? -1 : this.price > typedOther.price ? 1 : 0;
|
|
104
|
-
if (cmp0 === null) return Option.none();
|
|
105
|
-
if (cmp0 !== 0) return Option.some(cmp0);
|
|
106
|
-
const cmp1 = this.name.localeCompare(typedOther.name);
|
|
107
|
-
if (cmp1 === null) return Option.none();
|
|
108
|
-
if (cmp1 !== 0) return Option.some(cmp1);
|
|
109
|
-
return Option.some(0);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
``` ```
|
|
113
|
-
const i1 = new Item(10, "Widget", "A useful widget");
|
|
114
|
-
const i2 = new Item(10, "Widget", "Different description");
|
|
42
|
+
## Comparison Strategy
|
|
115
43
|
|
|
116
|
-
|
|
117
|
-
``` ## Handling Null Results
|
|
118
|
-
When using PartialOrd, always handle the `null` case:
|
|
119
|
-
**Source:**
|
|
120
|
-
```
|
|
121
|
-
/** @derive(PartialOrd) */
|
|
122
|
-
class Value {
|
|
123
|
-
amount: number;
|
|
44
|
+
Fields are compared **lexicographically** in declaration order:
|
|
124
45
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
function safeCompare(a: Value, b: unknown): string {
|
|
131
|
-
const result = a.compareTo(b);
|
|
132
|
-
if (result === null) {
|
|
133
|
-
return "incomparable";
|
|
134
|
-
} else if (result < 0) {
|
|
135
|
-
return "less than";
|
|
136
|
-
} else if (result > 0) {
|
|
137
|
-
return "greater than";
|
|
138
|
-
} else {
|
|
139
|
-
return "equal";
|
|
140
|
-
}
|
|
141
|
-
}
|
|
46
|
+
1. Compare first field
|
|
47
|
+
2. If incomparable, return `Option.none()`
|
|
48
|
+
3. If not equal, return that result wrapped in `Option.some()`
|
|
49
|
+
4. Otherwise, compare next field
|
|
50
|
+
5. Continue until a difference is found or all fields are equal
|
|
142
51
|
|
|
143
|
-
|
|
144
|
-
console.log(safeCompare(v, new Value(50))); // "greater than"
|
|
145
|
-
console.log(safeCompare(v, "string")); // "incomparable"
|
|
146
|
-
``` ## Sorting with PartialOrd
|
|
147
|
-
When sorting, handle `null` values appropriately:
|
|
148
|
-
**Source:**
|
|
149
|
-
```
|
|
150
|
-
/** @derive(PartialOrd) */
|
|
151
|
-
class Score {
|
|
152
|
-
value: number;
|
|
52
|
+
## Type-Specific Comparisons
|
|
153
53
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
];
|
|
54
|
+
| Type | Comparison Method |
|
|
55
|
+
|------|-------------------|
|
|
56
|
+
| `number`/`bigint` | Direct comparison, returns some() |
|
|
57
|
+
| `string` | `localeCompare()` wrapped in some() |
|
|
58
|
+
| `boolean` | false < true, wrapped in some() |
|
|
59
|
+
| null/undefined | Returns none() for mismatched nullability |
|
|
60
|
+
| Arrays | Lexicographic, propagates none() on incomparable elements |
|
|
61
|
+
| `Date` | Timestamp comparison, none() if invalid |
|
|
62
|
+
| Objects | Unwraps nested Option from compareTo() |
|
|
164
63
|
|
|
165
|
-
|
|
166
|
-
scores.sort((a, b) => a.compareTo(b) ?? 0);
|
|
167
|
-
// Result: [Score(50), Score(75), Score(100)]
|
|
168
|
-
``` ## Interface Support
|
|
169
|
-
PartialOrd works with interfaces. For interfaces, a namespace is generated with a `compareTo` function:
|
|
170
|
-
**Before:**
|
|
171
|
-
```
|
|
172
|
-
/** @derive(PartialOrd) */
|
|
173
|
-
interface Measurement {
|
|
174
|
-
value: number;
|
|
175
|
-
unit: string;
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
**After:**
|
|
179
|
-
```
|
|
180
|
-
import { Option } from 'macroforge/utils';
|
|
64
|
+
## Field-Level Options
|
|
181
65
|
|
|
182
|
-
|
|
183
|
-
value: number;
|
|
184
|
-
unit: string;
|
|
185
|
-
}
|
|
66
|
+
The `@ord` decorator supports:
|
|
186
67
|
|
|
187
|
-
|
|
188
|
-
export function compareTo(self: Measurement, other: Measurement): Option<number> {
|
|
189
|
-
if (self === other) return Option.some(0);
|
|
190
|
-
const cmp0 = self.value < other.value ? -1 : self.value > other.value ? 1 : 0;
|
|
191
|
-
if (cmp0 === null) return Option.none();
|
|
192
|
-
if (cmp0 !== 0) return Option.some(cmp0);
|
|
193
|
-
const cmp1 = self.unit.localeCompare(other.unit);
|
|
194
|
-
if (cmp1 === null) return Option.none();
|
|
195
|
-
if (cmp1 !== 0) return Option.some(cmp1);
|
|
196
|
-
return Option.some(0);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
``` ```
|
|
200
|
-
const m1: Measurement = { value: 10, unit: "kg" };
|
|
201
|
-
const m2: Measurement = { value: 10, unit: "lb" };
|
|
68
|
+
- `skip` - Exclude the field from ordering comparison
|
|
202
69
|
|
|
203
|
-
|
|
204
|
-
``` ## Enum Support
|
|
205
|
-
PartialOrd works with enums:
|
|
206
|
-
**Before:**
|
|
207
|
-
```
|
|
208
|
-
/** @derive(PartialOrd) */
|
|
209
|
-
enum Size {
|
|
210
|
-
Small = 1,
|
|
211
|
-
Medium = 2,
|
|
212
|
-
Large = 3
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
**After:**
|
|
216
|
-
```
|
|
217
|
-
import { Option } from 'macroforge/utils';
|
|
70
|
+
## Example
|
|
218
71
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
72
|
+
```typescript
|
|
73
|
+
@derive(PartialOrd)
|
|
74
|
+
class Temperature {
|
|
75
|
+
value: number | null; // null represents "unknown"
|
|
76
|
+
unit: string;
|
|
223
77
|
}
|
|
224
78
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
console.log(Size.compareTo(Size.Large, Size.Small)); // 1
|
|
239
|
-
``` ## Type Alias Support
|
|
240
|
-
PartialOrd works with type aliases:
|
|
241
|
-
**Before:**
|
|
79
|
+
// Generated:
|
|
80
|
+
// compareTo(other: unknown): Option<number> {
|
|
81
|
+
// if (this === other) return Option.some(0);
|
|
82
|
+
// if (!(other instanceof Temperature)) return Option.none();
|
|
83
|
+
// const typedOther = other as Temperature;
|
|
84
|
+
// const cmp0 = ...; // Compare value field
|
|
85
|
+
// if (cmp0 === null) return Option.none();
|
|
86
|
+
// if (cmp0 !== 0) return Option.some(cmp0);
|
|
87
|
+
// const cmp1 = ...; // Compare unit field
|
|
88
|
+
// if (cmp1 === null) return Option.none();
|
|
89
|
+
// if (cmp1 !== 0) return Option.some(cmp1);
|
|
90
|
+
// return Option.some(0);
|
|
91
|
+
// }
|
|
242
92
|
```
|
|
243
|
-
/** @derive(PartialOrd) */
|
|
244
|
-
type Interval = {
|
|
245
|
-
start: number;
|
|
246
|
-
end: number;
|
|
247
|
-
};
|
|
248
|
-
```
|
|
249
|
-
**After:**
|
|
250
|
-
```
|
|
251
|
-
import { Option } from 'macroforge/utils';
|
|
252
|
-
|
|
253
|
-
type Interval = {
|
|
254
|
-
start: number;
|
|
255
|
-
end: number;
|
|
256
|
-
};
|
|
257
93
|
|
|
258
|
-
|
|
259
|
-
export function compareTo(a: Interval, b: Interval): Option<number> {
|
|
260
|
-
if (a === b) return Option.some(0);
|
|
261
|
-
const cmp0 = a.start < b.start ? -1 : a.start > b.start ? 1 : 0;
|
|
262
|
-
if (cmp0 === null) return Option.none();
|
|
263
|
-
if (cmp0 !== 0) return Option.some(cmp0);
|
|
264
|
-
const cmp1 = a.end < b.end ? -1 : a.end > b.end ? 1 : 0;
|
|
265
|
-
if (cmp1 === null) return Option.none();
|
|
266
|
-
if (cmp1 !== 0) return Option.some(cmp1);
|
|
267
|
-
return Option.some(0);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
``` ```
|
|
271
|
-
const i1: Interval = { start: 0, end: 10 };
|
|
272
|
-
const i2: Interval = { start: 0, end: 20 };
|
|
273
|
-
|
|
274
|
-
console.log(Interval.compareTo(i1, i2)); // -1
|
|
275
|
-
``` ## PartialOrd vs Ord
|
|
276
|
-
Choose between `Ord` and `PartialOrd` based on your use case:
|
|
277
|
-
- **Ord** → Use when all values are always comparable (never returns null)
|
|
278
|
-
- **PartialOrd** → Use when comparing with `unknown` types or when some values might be incomparable
|
|
279
|
-
**Source:**
|
|
280
|
-
```
|
|
281
|
-
// PartialOrd is safer for public APIs that accept unknown input
|
|
282
|
-
/** @derive(PartialOrd) */
|
|
283
|
-
class SafeValue {
|
|
284
|
-
data: number;
|
|
285
|
-
constructor(data: number) {
|
|
286
|
-
this.data = data;
|
|
287
|
-
}
|
|
94
|
+
## Required Import
|
|
288
95
|
|
|
289
|
-
|
|
290
|
-
isGreaterThan(other: unknown): boolean {
|
|
291
|
-
const result = this.compareTo(other);
|
|
292
|
-
return result !== null && result > 0;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
``` ```
|
|
296
|
-
const safe = new SafeValue(100);
|
|
297
|
-
console.log(safe.isGreaterThan(new SafeValue(50))); // true
|
|
298
|
-
console.log(safe.isGreaterThan("invalid")); // false
|
|
299
|
-
```
|
|
96
|
+
The generated code automatically adds an import for `Option` from `macroforge/utils`.
|