@macroforge/mcp-server 0.1.17

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.
Files changed (46) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +47 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/tools/docs-loader.d.ts +30 -0
  6. package/dist/tools/docs-loader.d.ts.map +1 -0
  7. package/dist/tools/docs-loader.js +112 -0
  8. package/dist/tools/docs-loader.js.map +1 -0
  9. package/dist/tools/index.d.ts +6 -0
  10. package/dist/tools/index.d.ts.map +1 -0
  11. package/dist/tools/index.js +348 -0
  12. package/dist/tools/index.js.map +1 -0
  13. package/docs/api/api-overview.md +75 -0
  14. package/docs/api/expand-sync.md +121 -0
  15. package/docs/api/native-plugin.md +106 -0
  16. package/docs/api/position-mapper.md +127 -0
  17. package/docs/api/transform-sync.md +98 -0
  18. package/docs/builtin-macros/clone.md +180 -0
  19. package/docs/builtin-macros/debug.md +222 -0
  20. package/docs/builtin-macros/default.md +192 -0
  21. package/docs/builtin-macros/deserialize.md +662 -0
  22. package/docs/builtin-macros/hash.md +205 -0
  23. package/docs/builtin-macros/macros-overview.md +169 -0
  24. package/docs/builtin-macros/ord.md +258 -0
  25. package/docs/builtin-macros/partial-eq.md +306 -0
  26. package/docs/builtin-macros/partial-ord.md +268 -0
  27. package/docs/builtin-macros/serialize.md +321 -0
  28. package/docs/concepts/architecture.md +139 -0
  29. package/docs/concepts/derive-system.md +173 -0
  30. package/docs/concepts/how-macros-work.md +124 -0
  31. package/docs/custom-macros/custom-overview.md +84 -0
  32. package/docs/custom-macros/rust-setup.md +146 -0
  33. package/docs/custom-macros/ts-macro-derive.md +307 -0
  34. package/docs/custom-macros/ts-quote.md +696 -0
  35. package/docs/getting-started/first-macro.md +120 -0
  36. package/docs/getting-started/installation.md +110 -0
  37. package/docs/integration/cli.md +207 -0
  38. package/docs/integration/configuration.md +116 -0
  39. package/docs/integration/integration-overview.md +51 -0
  40. package/docs/integration/typescript-plugin.md +96 -0
  41. package/docs/integration/vite-plugin.md +126 -0
  42. package/docs/language-servers/ls-overview.md +47 -0
  43. package/docs/language-servers/svelte-ls.md +80 -0
  44. package/docs/language-servers/zed-extensions.md +84 -0
  45. package/docs/sections.json +258 -0
  46. package/package.json +48 -0
@@ -0,0 +1,205 @@
1
+ # Hash
2
+
3
+ *The `Hash` macro generates a `hashCode()` method that computes a numeric hash value based on field values.*
4
+
5
+ ## Basic Usage
6
+
7
+ ```typescript
8
+ /** @derive(Hash) */
9
+ class Point {
10
+ x: number;
11
+ y: number;
12
+
13
+ constructor(x: number, y: number) {
14
+ this.x = x;
15
+ this.y = y;
16
+ }
17
+ }
18
+
19
+ const p1 = new Point(10, 20);
20
+ const p2 = new Point(10, 20);
21
+ const p3 = new Point(5, 5);
22
+
23
+ console.log(p1.hashCode()); // Same hash
24
+ console.log(p2.hashCode()); // Same hash (equal values = equal hash)
25
+ console.log(p3.hashCode()); // Different hash
26
+ ```
27
+
28
+ ## Generated Code
29
+
30
+ The Hash macro generates a method that combines field hashes using the FNV-1a style algorithm:
31
+
32
+ ```typescript
33
+ hashCode(): number {
34
+ let hash = 17;
35
+ hash = (hash * 31 + (Number.isInteger(this.x) ? this.x | 0 : this.x.toString().split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) | 0;
36
+ hash = (hash * 31 + (Number.isInteger(this.y) ? this.y | 0 : this.y.toString().split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) | 0;
37
+ return hash;
38
+ }
39
+ ```
40
+
41
+ ## Hash Algorithm
42
+
43
+ The generated hash function uses the following algorithm for different types:
44
+
45
+ - `number` → Integers use bitwise OR (`| 0`), floats are stringified and hashed
46
+
47
+ - `string` → Character-by-character hash: `(h * 31 + charCode) | 0`
48
+
49
+ - `boolean` → `1231` for true, `1237` for false (Java convention)
50
+
51
+ - `bigint` → Converted to string and hashed character-by-character
52
+
53
+ - `Date` → Uses `getTime() | 0` for timestamp hash
54
+
55
+ - `Array` → Combines element hashes with `h * 31 + elementHash`
56
+
57
+ - `Map/Set` → Combines all entry hashes
58
+
59
+ - `Object` → Calls `hashCode()` if available, otherwise JSON stringifies and hashes
60
+
61
+ - `null` → Returns 0
62
+
63
+ - `undefined` → Returns 1
64
+
65
+ ## Field Options
66
+
67
+ ### @hash(skip)
68
+
69
+ Use `@hash(skip)` to exclude a field from hash computation:
70
+
71
+ ```typescript
72
+ /** @derive(Hash) */
73
+ class User {
74
+ id: number;
75
+ name: string;
76
+
77
+ /** @hash(skip) */
78
+ lastLogin: Date; // Not included in hash
79
+
80
+ constructor(id: number, name: string, lastLogin: Date) {
81
+ this.id = id;
82
+ this.name = name;
83
+ this.lastLogin = lastLogin;
84
+ }
85
+ }
86
+
87
+ const user1 = new User(1, "Alice", new Date("2024-01-01"));
88
+ const user2 = new User(1, "Alice", new Date("2024-12-01"));
89
+
90
+ console.log(user1.hashCode() === user2.hashCode()); // true (lastLogin is skipped)
91
+ ```
92
+
93
+ ## Use with PartialEq
94
+
95
+ Hash is often used together with PartialEq. Objects that are equal should have the same hash code:
96
+
97
+ ```typescript
98
+ /** @derive(Hash, PartialEq) */
99
+ class Product {
100
+ sku: string;
101
+ name: string;
102
+
103
+ constructor(sku: string, name: string) {
104
+ this.sku = sku;
105
+ this.name = name;
106
+ }
107
+ }
108
+
109
+ const p1 = new Product("ABC123", "Widget");
110
+ const p2 = new Product("ABC123", "Widget");
111
+
112
+ // Equal objects have equal hash codes
113
+ console.log(p1.equals(p2)); // true
114
+ console.log(p1.hashCode() === p2.hashCode()); // true
115
+ ```
116
+
117
+ ## Interface Support
118
+
119
+ Hash also works with interfaces. For interfaces, a namespace is generated with a `hashCode` function:
120
+
121
+ ```typescript
122
+ /** @derive(Hash) */
123
+ interface Point {
124
+ x: number;
125
+ y: number;
126
+ }
127
+
128
+ // Generated:
129
+ // export namespace Point {
130
+ // export function hashCode(self: Point): number {
131
+ // let hash = 17;
132
+ // hash = (hash * 31 + (self.x | 0)) | 0;
133
+ // hash = (hash * 31 + (self.y | 0)) | 0;
134
+ // return hash;
135
+ // }
136
+ // }
137
+
138
+ const p: Point = { x: 10, y: 20 };
139
+ console.log(Point.hashCode(p)); // numeric hash value
140
+ ```
141
+
142
+ ## Enum Support
143
+
144
+ Hash works with enums. For string enums, it hashes the string value; for numeric enums, it uses the numeric value directly:
145
+
146
+ ```typescript
147
+ /** @derive(Hash) */
148
+ enum Status {
149
+ Active = "active",
150
+ Inactive = "inactive",
151
+ Pending = "pending",
152
+ }
153
+
154
+ // Generated:
155
+ // export namespace Status {
156
+ // export function hashCode(value: Status): number {
157
+ // if (typeof value === "string") {
158
+ // let hash = 0;
159
+ // for (let i = 0; i < value.length; i++) {
160
+ // hash = (hash * 31 + value.charCodeAt(i)) | 0;
161
+ // }
162
+ // return hash;
163
+ // }
164
+ // return value | 0;
165
+ // }
166
+ // }
167
+
168
+ console.log(Status.hashCode(Status.Active)); // consistent hash
169
+ console.log(Status.hashCode(Status.Inactive)); // different hash
170
+ ```
171
+
172
+ ## Type Alias Support
173
+
174
+ Hash works with type aliases. For object types, it hashes each field:
175
+
176
+ ```typescript
177
+ /** @derive(Hash) */
178
+ type Coordinates = {
179
+ lat: number;
180
+ lng: number;
181
+ };
182
+
183
+ // Generated:
184
+ // export namespace Coordinates {
185
+ // export function hashCode(value: Coordinates): number {
186
+ // let hash = 17;
187
+ // hash = (hash * 31 + (value.lat | 0)) | 0;
188
+ // hash = (hash * 31 + (value.lng | 0)) | 0;
189
+ // return hash;
190
+ // }
191
+ // }
192
+
193
+ const loc: Coordinates = { lat: 40.7128, lng: -74.0060 };
194
+ console.log(Coordinates.hashCode(loc));
195
+ ```
196
+
197
+ For union types, it uses JSON stringification as a fallback:
198
+
199
+ ```typescript
200
+ /** @derive(Hash) */
201
+ type Result = "success" | "error" | "pending";
202
+
203
+ console.log(Result.hashCode("success")); // hash of "success" string
204
+ console.log(Result.hashCode("error")); // hash of "error" string
205
+ ```
@@ -0,0 +1,169 @@
1
+ # Built-in Macros
2
+
3
+ *Macroforge comes with five built-in derive macros that cover the most common code generation needs. All macros work with classes, interfaces, enums, and type aliases.*
4
+
5
+ ## Overview
6
+
7
+ | `Debug`
8
+ | `toString(): string`
9
+ | Human-readable string representation
10
+
11
+ | `Clone`
12
+ | `clone(): T`
13
+ | Creates a copy of the object
14
+
15
+ | `PartialEq`
16
+ | `equals(other: T): boolean`
17
+ | Value equality comparison
18
+
19
+ | `Serialize`
20
+ | `toJSON(): Record<string, unknown>`
21
+ | JSON serialization with type handling
22
+
23
+ | `Deserialize`
24
+ | `fromJSON(data: unknown): T`
25
+ | JSON deserialization with validation
26
+
27
+ ## Using Built-in Macros
28
+
29
+ Built-in macros don't require imports. Just use them with `@derive`:
30
+
31
+ ```typescript
32
+ /** @derive(Debug, Clone, PartialEq) */
33
+ class User {
34
+ name: string;
35
+ age: number;
36
+
37
+ constructor(name: string, age: number) {
38
+ this.name = name;
39
+ this.age = age;
40
+ }
41
+ }
42
+ ```
43
+
44
+ ## Interface Support
45
+
46
+ All built-in macros work with interfaces. For interfaces, methods are generated as functions in a namespace with the same name, using `self` as the first parameter:
47
+
48
+ ```typescript
49
+ /** @derive(Debug, Clone, PartialEq) */
50
+ interface Point {
51
+ x: number;
52
+ y: number;
53
+ }
54
+
55
+ // Generated namespace:
56
+ // namespace Point {
57
+ // export function toString(self: Point): string { ... }
58
+ // export function clone(self: Point): Point { ... }
59
+ // export function equals(self: Point, other: Point): boolean { ... }
60
+ // export function hashCode(self: Point): number { ... }
61
+ // }
62
+
63
+ const point: Point = { x: 10, y: 20 };
64
+
65
+ // Use the namespace functions
66
+ console.log(Point.toString(point)); // "Point { x: 10, y: 20 }"
67
+ const copy = Point.clone(point); // { x: 10, y: 20 }
68
+ console.log(Point.equals(point, copy)); // true
69
+ ```
70
+
71
+ ## Enum Support
72
+
73
+ All built-in macros work with enums. For enums, methods are generated as functions in a namespace with the same name:
74
+
75
+ ```typescript
76
+ /** @derive(Debug, Clone, PartialEq, Serialize, Deserialize) */
77
+ enum Status {
78
+ Active = "active",
79
+ Inactive = "inactive",
80
+ Pending = "pending",
81
+ }
82
+
83
+ // Generated namespace:
84
+ // namespace Status {
85
+ // export function toString(value: Status): string { ... }
86
+ // export function clone(value: Status): Status { ... }
87
+ // export function equals(a: Status, b: Status): boolean { ... }
88
+ // export function hashCode(value: Status): number { ... }
89
+ // export function toJSON(value: Status): string | number { ... }
90
+ // export function fromJSON(data: unknown): Status { ... }
91
+ // }
92
+
93
+ // Use the namespace functions
94
+ console.log(Status.toString(Status.Active)); // "Status.Active"
95
+ console.log(Status.equals(Status.Active, Status.Active)); // true
96
+ const json = Status.toJSON(Status.Pending); // "pending"
97
+ const parsed = Status.fromJSON("active"); // Status.Active
98
+ ```
99
+
100
+ ## Type Alias Support
101
+
102
+ All built-in macros work with type aliases. For object type aliases, field-aware methods are generated in a namespace:
103
+
104
+ ```typescript
105
+ /** @derive(Debug, Clone, PartialEq, Serialize, Deserialize) */
106
+ type Point = {
107
+ x: number;
108
+ y: number;
109
+ };
110
+
111
+ // Generated namespace:
112
+ // namespace Point {
113
+ // export function toString(value: Point): string { ... }
114
+ // export function clone(value: Point): Point { ... }
115
+ // export function equals(a: Point, b: Point): boolean { ... }
116
+ // export function hashCode(value: Point): number { ... }
117
+ // export function toJSON(value: Point): Record<string, unknown> { ... }
118
+ // export function fromJSON(data: unknown): Point { ... }
119
+ // }
120
+
121
+ const point: Point = { x: 10, y: 20 };
122
+ console.log(Point.toString(point)); // "Point { x: 10, y: 20 }"
123
+ const copy = Point.clone(point); // { x: 10, y: 20 }
124
+ console.log(Point.equals(point, copy)); // true
125
+ ```
126
+
127
+ Union type aliases also work, using JSON-based implementations:
128
+
129
+ ```typescript
130
+ /** @derive(Debug, PartialEq) */
131
+ type ApiStatus = "loading" | "success" | "error";
132
+
133
+ const status: ApiStatus = "success";
134
+ console.log(ApiStatus.toString(status)); // "ApiStatus(\\"success\\")"
135
+ console.log(ApiStatus.equals("success", "success")); // true
136
+ ```
137
+
138
+ ## Combining Macros
139
+
140
+ All macros can be used together. They don't conflict and each generates independent methods:
141
+
142
+ ```typescript
143
+ const user = new User("Alice", 30);
144
+
145
+ // Debug
146
+ console.log(user.toString());
147
+ // "User { name: Alice, age: 30 }"
148
+
149
+ // Clone
150
+ const copy = user.clone();
151
+ console.log(copy.name); // "Alice"
152
+
153
+ // Eq
154
+ console.log(user.equals(copy)); // true
155
+ ```
156
+
157
+ ## Detailed Documentation
158
+
159
+ Each macro has its own options and behaviors:
160
+
161
+ - [**Debug**]({base}/docs/builtin-macros/debug) - Customizable field renaming and skipping
162
+
163
+ - [**Clone**]({base}/docs/builtin-macros/clone) - Shallow copying for all field types
164
+
165
+ - [**PartialEq**]({base}/docs/builtin-macros/partial-eq) - Value-based equality comparison
166
+
167
+ - [**Serialize**]({base}/docs/builtin-macros/serialize) - JSON serialization with serde-style options
168
+
169
+ - [**Deserialize**]({base}/docs/builtin-macros/deserialize) - JSON deserialization with validation
@@ -0,0 +1,258 @@
1
+ # Ord
2
+
3
+ *The `Ord` macro generates a `compareTo()` method that implements total ordering, always returning `-1`, `0`, or `1`.*
4
+
5
+ ## Basic Usage
6
+
7
+ ```typescript
8
+ /** @derive(Ord) */
9
+ class Version {
10
+ major: number;
11
+ minor: number;
12
+ patch: number;
13
+
14
+ constructor(major: number, minor: number, patch: number) {
15
+ this.major = major;
16
+ this.minor = minor;
17
+ this.patch = patch;
18
+ }
19
+ }
20
+
21
+ const v1 = new Version(1, 0, 0);
22
+ const v2 = new Version(1, 2, 0);
23
+ const v3 = new Version(1, 2, 0);
24
+
25
+ console.log(v1.compareTo(v2)); // -1 (v1 < v2)
26
+ console.log(v2.compareTo(v1)); // 1 (v2 > v1)
27
+ console.log(v2.compareTo(v3)); // 0 (v2 == v3)
28
+ ```
29
+
30
+ ## Generated Code
31
+
32
+ The Ord macro generates a compareTo method using lexicographic field comparison:
33
+
34
+ ```typescript
35
+ compareTo(other: Version): number {
36
+ if (this === other) return 0;
37
+ const typedOther = other;
38
+ const cmp0 = (this.major < typedOther.major ? -1 : this.major > typedOther.major ? 1 : 0);
39
+ if (cmp0 !== 0) return cmp0;
40
+ const cmp1 = (this.minor < typedOther.minor ? -1 : this.minor > typedOther.minor ? 1 : 0);
41
+ if (cmp1 !== 0) return cmp1;
42
+ const cmp2 = (this.patch < typedOther.patch ? -1 : this.patch > typedOther.patch ? 1 : 0);
43
+ if (cmp2 !== 0) return cmp2;
44
+ return 0;
45
+ }
46
+ ```
47
+
48
+ ## Comparison Logic
49
+
50
+ The Ord macro compares fields in declaration order (lexicographic ordering). For each type:
51
+
52
+ - `number` / `bigint` → Direct numeric comparison
53
+
54
+ - `string` → Uses `localeCompare()` clamped to -1/0/1
55
+
56
+ - `boolean` → `false < true`
57
+
58
+ - `Date` → Compares timestamps via `getTime()`
59
+
60
+ - `Array` → Lexicographic: compares element-by-element, then length
61
+
62
+ - `Map/Set` → Size and content comparison
63
+
64
+ - `Object` → Calls `compareTo()` if available, otherwise 0
65
+
66
+ - `null/undefined` → Treated as equal (returns 0)
67
+
68
+ ## Return Values
69
+
70
+ The `compareTo()` method always returns:
71
+
72
+ - `-1` → `this` is less than `other`
73
+
74
+ - `0` → `this` equals `other`
75
+
76
+ - `1` → `this` is greater than `other`
77
+
78
+ Unlike `PartialOrd`, the `Ord` macro never returns `null` - it provides total ordering.
79
+
80
+ ## Field Options
81
+
82
+ ### @ord(skip)
83
+
84
+ Use `@ord(skip)` to exclude a field from ordering comparison:
85
+
86
+ ```typescript
87
+ /** @derive(Ord) */
88
+ class Task {
89
+ priority: number;
90
+ name: string;
91
+
92
+ /** @ord(skip) */
93
+ createdAt: Date; // Not used for ordering
94
+
95
+ constructor(priority: number, name: string, createdAt: Date) {
96
+ this.priority = priority;
97
+ this.name = name;
98
+ this.createdAt = createdAt;
99
+ }
100
+ }
101
+
102
+ const t1 = new Task(1, "Bug fix", new Date("2024-01-01"));
103
+ const t2 = new Task(1, "Bug fix", new Date("2024-12-01"));
104
+
105
+ console.log(t1.compareTo(t2)); // 0 (createdAt is skipped)
106
+ ```
107
+
108
+ ## Sorting Arrays
109
+
110
+ The generated `compareTo()` method works directly with `Array.sort()`:
111
+
112
+ ```typescript
113
+ /** @derive(Ord) */
114
+ class Score {
115
+ points: number;
116
+ name: string;
117
+
118
+ constructor(points: number, name: string) {
119
+ this.points = points;
120
+ this.name = name;
121
+ }
122
+ }
123
+
124
+ const scores = [
125
+ new Score(100, "Alice"),
126
+ new Score(50, "Bob"),
127
+ new Score(150, "Charlie"),
128
+ new Score(50, "Alice") // Same points, different name
129
+ ];
130
+
131
+ // Sort ascending
132
+ scores.sort((a, b) => a.compareTo(b));
133
+ // Result: [Bob(50), Alice(50), Alice(100), Charlie(150)]
134
+
135
+ // Sort descending
136
+ scores.sort((a, b) => b.compareTo(a));
137
+ // Result: [Charlie(150), Alice(100), Alice(50), Bob(50)]
138
+ ```
139
+
140
+ ## Interface Support
141
+
142
+ Ord works with interfaces. For interfaces, a namespace is generated with a `compareTo` function:
143
+
144
+ ```typescript
145
+ /** @derive(Ord) */
146
+ interface Point {
147
+ x: number;
148
+ y: number;
149
+ }
150
+
151
+ // Generated:
152
+ // export namespace Point {
153
+ // export function compareTo(self: Point, other: Point): number {
154
+ // if (self === other) return 0;
155
+ // const cmp0 = (self.x < other.x ? -1 : self.x > other.x ? 1 : 0);
156
+ // if (cmp0 !== 0) return cmp0;
157
+ // const cmp1 = (self.y < other.y ? -1 : self.y > other.y ? 1 : 0);
158
+ // if (cmp1 !== 0) return cmp1;
159
+ // return 0;
160
+ // }
161
+ // }
162
+
163
+ const points: Point[] = [
164
+ { x: 5, y: 10 },
165
+ { x: 1, y: 20 },
166
+ { x: 5, y: 5 }
167
+ ];
168
+
169
+ points.sort((a, b) => Point.compareTo(a, b));
170
+ // Result: [{ x: 1, y: 20 }, { x: 5, y: 5 }, { x: 5, y: 10 }]
171
+ ```
172
+
173
+ ## Enum Support
174
+
175
+ Ord works with enums. For numeric enums, it compares the numeric values; for string enums, it uses string comparison:
176
+
177
+ ```typescript
178
+ /** @derive(Ord) */
179
+ enum Priority {
180
+ Low = 0,
181
+ Medium = 1,
182
+ High = 2,
183
+ Critical = 3
184
+ }
185
+
186
+ // Generated:
187
+ // export namespace Priority {
188
+ // export function compareTo(a: Priority, b: Priority): number {
189
+ // return a < b ? -1 : a > b ? 1 : 0;
190
+ // }
191
+ // }
192
+
193
+ console.log(Priority.compareTo(Priority.Low, Priority.High)); // -1
194
+ console.log(Priority.compareTo(Priority.Critical, Priority.Low)); // 1
195
+ console.log(Priority.compareTo(Priority.Medium, Priority.Medium)); // 0
196
+ ```
197
+
198
+ ## Type Alias Support
199
+
200
+ Ord works with type aliases. For object types, it uses lexicographic field comparison:
201
+
202
+ ```typescript
203
+ /** @derive(Ord) */
204
+ type Coordinate = {
205
+ x: number;
206
+ y: number;
207
+ };
208
+
209
+ // Generated:
210
+ // export namespace Coordinate {
211
+ // export function compareTo(a: Coordinate, b: Coordinate): number {
212
+ // if (a === b) return 0;
213
+ // const cmp0 = (a.x < b.x ? -1 : a.x > b.x ? 1 : 0);
214
+ // if (cmp0 !== 0) return cmp0;
215
+ // const cmp1 = (a.y < b.y ? -1 : a.y > b.y ? 1 : 0);
216
+ // if (cmp1 !== 0) return cmp1;
217
+ // return 0;
218
+ // }
219
+ // }
220
+
221
+ const c1: Coordinate = { x: 10, y: 20 };
222
+ const c2: Coordinate = { x: 10, y: 30 };
223
+
224
+ console.log(Coordinate.compareTo(c1, c2)); // -1 (c1 < c2)
225
+ ```
226
+
227
+ ## Ord vs PartialOrd
228
+
229
+ Use `Ord` when all values of a type are comparable. Use `PartialOrd` when some values might be incomparable (e.g., different types at runtime).
230
+
231
+ ```typescript
232
+ // Ord: Total ordering - never returns null
233
+ /** @derive(Ord) */
234
+ class Version {
235
+ major: number;
236
+ minor: number;
237
+ constructor(major: number, minor: number) {
238
+ this.major = major;
239
+ this.minor = minor;
240
+ }
241
+ }
242
+
243
+ const v1 = new Version(1, 0);
244
+ const v2 = new Version(2, 0);
245
+ console.log(v1.compareTo(v2)); // Always -1, 0, or 1
246
+
247
+ // PartialOrd: Partial ordering - can return null
248
+ /** @derive(PartialOrd) */
249
+ class Value {
250
+ data: number;
251
+ constructor(data: number) {
252
+ this.data = data;
253
+ }
254
+ }
255
+
256
+ const val = new Value(10);
257
+ console.log(val.compareTo("not a Value")); // null (incomparable)
258
+ ```