@macroforge/mcp-server 0.1.34 → 0.1.36

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.
@@ -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
- constructor(celsius: number) {
11
- this.celsius = celsius;
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
- class Temperature {
20
- celsius: number;
7
+ ## Generated Output
21
8
 
22
- constructor(celsius: number) {
23
- this.celsius = celsius;
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
- compareTo(other: unknown): Option<number> {
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
- console.log(t1.compareTo(t2)); // -1 (t1 < t2)
43
- console.log(t2.compareTo(t1)); // 1 (t2 > t1)
44
- console.log(t1.compareTo(t3)); // 0 (t1 == t3)
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
- // Returns null for incomparable types
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
- /** @ord(skip) */
74
- description: string;
26
+ Unlike `Ord`, `PartialOrd` returns an `Option<number>` to handle incomparable values:
75
27
 
76
- constructor(price: number, name: string, description: string) {
77
- this.price = price;
78
- this.name = name;
79
- this.description = description;
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
- class Item {
88
- price: number;
89
- name: string;
33
+ ## When to Use PartialOrd vs Ord
90
34
 
91
- description: string;
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
- constructor(price: number, name: string, description: string) {
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
- compareTo(other: unknown): Option<number> {
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
- console.log(i1.compareTo(i2)); // 0 (description is skipped)
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
- constructor(amount: number) {
126
- this.amount = amount;
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
- const v = new Value(100);
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
- constructor(value: number) {
155
- this.value = value;
156
- }
157
- }
158
- ``` ```
159
- const scores = [
160
- new Score(100),
161
- new Score(50),
162
- new Score(75)
163
- ];
54
+ | Type | Comparison Method |
55
+ |------|-------------------|
56
+ | `number`/`bigint` | Direct comparison, returns some() |
57
+ | `string` | `localeCompare()` wrapped in some() |
58
+ | `boolean` | false &lt; 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
- // Safe sort that handles null (treats null as equal)
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
- interface Measurement {
183
- value: number;
184
- unit: string;
185
- }
66
+ The `@ord` decorator supports:
186
67
 
187
- export namespace Measurement {
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
- console.log(Measurement.compareTo(m1, m2)); // 1 (kg > lb alphabetically)
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
- enum Size {
220
- Small = 1,
221
- Medium = 2,
222
- Large = 3
72
+ ```typescript
73
+ @derive(PartialOrd)
74
+ class Temperature {
75
+ value: number | null; // null represents "unknown"
76
+ unit: string;
223
77
  }
224
78
 
225
- export namespace Size {
226
- export function compareTo(a: Size, b: Size): Option<number> {
227
- if (typeof a === 'number' && typeof b === 'number') {
228
- return Option.some(a < b ? -1 : a > b ? 1 : 0);
229
- }
230
- if (typeof a === 'string' && typeof b === 'string') {
231
- return Option.some(a.localeCompare(b));
232
- }
233
- return a === b ? Option.some(0) : Option.none();
234
- }
235
- }
236
- ``` ```
237
- console.log(Size.compareTo(Size.Small, Size.Large)); // -1
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
- export namespace Interval {
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
- // Can safely compare with any value
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`.