@macroforge/mcp-server 0.1.77 → 0.1.79

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.
@@ -9,21 +9,24 @@ independent copies of values.
9
9
  | Type | Generated Code | Description |
10
10
  |------|----------------|-------------|
11
11
  | Class | `classNameClone(value)` + `static clone(value)` | Standalone function + static wrapper method |
12
- | Enum | `enumNameClone(value: EnumName): EnumName` | Standalone function (enums are primitives, returns value as-is) |
13
- | Interface | `interfaceNameClone(value: InterfaceName): InterfaceName` | Standalone function creating a new object literal |
14
- | Type Alias | `typeNameClone(value: TypeName): TypeName` | Standalone function with spread copy for objects |
12
+ | Enum | `enumNameClone(value): EnumName` | Standalone function (enums are primitives, returns value as-is) |
13
+ | Interface | `ifaceNameClone(value): InterfaceName` | Standalone function creating a new object literal |
14
+ | Type Alias | `typeNameClone(value): TypeName` | Standalone function with spread copy for objects |
15
15
 
16
+ Names use **camelCase** conversion (e.g., `Point` -> `pointClone`).
16
17
 
17
- ## Cloning Strategy
18
18
 
19
- The generated clone performs a **shallow copy** of all fields:
19
+ ## Cloning Strategy
20
20
 
21
- - **Primitives** (`string`, `number`, `boolean`): Copied by value
22
- - **Objects**: Reference is copied (not deep cloned)
23
- - **Arrays**: Reference is copied (not deep cloned)
21
+ The generated clone is **type-aware** when a type registry is available:
24
22
 
25
- For deep cloning of nested objects, those objects should also derive `Clone`
26
- and the caller should clone them explicitly.
23
+ - **Primitives** (`string`, `number`, `boolean`, `bigint`): Copied by value
24
+ - **`Date`**: Deep cloned via `new Date(x.getTime())`
25
+ - **Arrays**: Spread copy `[...arr]`, or deep map if element type has `Clone`
26
+ - **`Map`/`Set`**: New collection, deep copy if value type has `Clone`
27
+ - **Objects with `@derive(Clone)`**: Deep cloned via their standalone clone function
28
+ - **Optional fields**: Null-checked -- `null`/`undefined` pass through unchanged
29
+ - **Other objects**: Shallow copy (reference)
27
30
 
28
31
  ## Example
29
32
 
@@ -11,11 +11,13 @@ method `static toString(value)` returning a string like `"ClassName { field1: va
11
11
  **Enums**: Generates a standalone function `enumNameToString(value)` that performs
12
12
  reverse lookup on numeric enums.
13
13
 
14
- **Interfaces**: Generates a standalone function `interfaceNameToString(value)`.
14
+ **Interfaces**: Generates a standalone function `ifaceNameToString(value)`.
15
15
 
16
16
  **Type Aliases**: Generates a standalone function using JSON.stringify for
17
17
  complex types, or field enumeration for object types.
18
18
 
19
+ Names use **camelCase** conversion (e.g., `User` -> `userToString`).
20
+
19
21
 
20
22
  ## Field-Level Options
21
23
 
@@ -8,10 +8,12 @@ a standard way to create "zero" or "empty" instances of types.
8
8
 
9
9
  | Type | Generated Code | Description |
10
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 |
11
+ | Class | `static defaultValue()` + `classNameDefaultValue()` | Static factory method + standalone function |
12
+ | Enum | `enumNameDefaultValue(): EnumName` | Standalone function returning `@default` variant |
13
+ | Interface | `ifaceNameDefaultValue(): InterfaceName` | Standalone function returning object literal |
14
+ | Type Alias | `typeNameDefaultValue(): TypeName` | Standalone function with type-appropriate default |
15
+
16
+ Names use **camelCase** conversion (e.g., `UserSettings` -> `userSettingsDefaultValue`).
15
17
 
16
18
 
17
19
  ## Default Values by Type
@@ -72,6 +74,10 @@ class UserSettings {
72
74
  return instance;
73
75
  }
74
76
  }
77
+
78
+ export function userSettingsDefaultValue(): UserSettings {
79
+ return UserSettings.defaultValue();
80
+ }
75
81
  ```
76
82
 
77
83
  ## Enum Defaults
@@ -99,10 +105,6 @@ enum Status {
99
105
  export function statusDefaultValue(): Status {
100
106
  return Status.Pending;
101
107
  }
102
-
103
- namespace Status {
104
- export const defaultValue = statusDefaultValue;
105
- }
106
108
  ```
107
109
 
108
110
  ## Error Handling
@@ -7,5 +7,6 @@ Uses deferred patching to handle references:
7
7
  3. After all objects are created, `ctx.applyPatches()` resolves all pending references
8
8
 
9
9
  References only apply to object-shaped, serializable values. The generator avoids probing for
10
- `__ref` on primitive-like fields (including literal unions and `T | null` where `T` is primitive-like),
11
- and it parses `Date` / `Date | null` from ISO strings without treating them as references.
10
+ `__ref` on primitive-like fields (including literal unions and `T | null` where `T` is
11
+ primitive-like), and it parses `Date` / `Date | null` from ISO strings without treating them as
12
+ references.
@@ -53,7 +53,7 @@ class User {
53
53
  */
54
54
  static deserialize(
55
55
  input: unknown,
56
- opts?: @{DESERIALIZE_OPTIONS}
56
+ opts?: @DESERIALIZE_OPTIONS
57
57
  ): Result<
58
58
  User,
59
59
  Array<{
@@ -65,9 +65,9 @@ class User {
65
65
  // Auto-detect: if string, parse as JSON first
66
66
  const data = typeof input === 'string' ? JSON.parse(input) : input;
67
67
 
68
- const ctx = @{DESERIALIZE_CONTEXT}.create();
68
+ const ctx = @DESERIALIZE_CONTEXT.create();
69
69
  const resultOrRef = User.deserializeWithContext(data, ctx);
70
- if (@{PENDING_REF}.is(resultOrRef)) {
70
+ if (@PENDING_REF.is(resultOrRef)) {
71
71
  return Result.err([
72
72
  {
73
73
  field: '_root',
@@ -81,7 +81,7 @@ class User {
81
81
  }
82
82
  return Result.ok(resultOrRef);
83
83
  } catch (e) {
84
- if (e instanceof @{DESERIALIZE_ERROR}) {
84
+ if (e instanceof @DESERIALIZE_ERROR) {
85
85
  return Result.err(e.errors);
86
86
  }
87
87
  const message = e instanceof Error ? e.message : String(e);
@@ -95,12 +95,12 @@ class User {
95
95
  }
96
96
 
97
97
  /** @internal */
98
- static deserializeWithContext(value: any, ctx: @{DESERIALIZE_CONTEXT}): User | @{PENDING_REF} {
98
+ static deserializeWithContext(value: any, ctx: @DESERIALIZE_CONTEXT): User | @PENDING_REF {
99
99
  if (value?.__ref !== undefined) {
100
100
  return ctx.getOrDefer(value.__ref);
101
101
  }
102
102
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
103
- throw new @{DESERIALIZE_ERROR}([
103
+ throw new @DESERIALIZE_ERROR([
104
104
  {
105
105
  field: '_root',
106
106
  message: 'User.deserializeWithContext: expected an object'
@@ -134,7 +134,7 @@ class User {
134
134
  });
135
135
  }
136
136
  if (errors.length > 0) {
137
- throw new @{DESERIALIZE_ERROR}(errors);
137
+ throw new @deserialize_error_expr(errors);
138
138
  }
139
139
  const instance = Object.create(User.prototype) as User;
140
140
  if (obj.__id !== undefined) {
@@ -160,7 +160,7 @@ class User {
160
160
  instance.age = __raw_age;
161
161
  }
162
162
  if (errors.length > 0) {
163
- throw new @{DESERIALIZE_ERROR}(errors);
163
+ throw new @deserialize_error_expr(errors);
164
164
  }
165
165
  return instance;
166
166
  }
@@ -214,4 +214,5 @@ if (Result.isOk(result)) {
214
214
  ## Required Imports
215
215
 
216
216
  The generated code automatically imports:
217
- - `DeserializeContext`, `DeserializeError`, `PendingRef` from `macroforge/serde`
217
+
218
+ - `DeserializeContext`, `DeserializeError`, `PendingRef` from `macroforge/serde`
@@ -1,21 +1,21 @@
1
1
  # Deserialize
2
2
 
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.
3
+ The `Deserialize` macro generates JSON deserialization methods with **cycle and forward-reference
4
+ support**, plus comprehensive runtime validation. This enables safe parsing of complex JSON
5
+ structures including circular references.
6
6
 
7
7
  ## Generated Output
8
8
 
9
- | Type | Generated Code | Description |
10
- |------|----------------|-------------|
11
- | Class | `classNameDeserialize(input)` + `static deserialize(input)` | Standalone function + static factory method |
12
- | Enum | `enumNameDeserialize(input)`, `enumNameDeserializeWithContext(data)`, `enumNameIs(value)` | Standalone functions |
13
- | Interface | `interfaceNameDeserialize(input)`, etc. | Standalone functions |
14
- | Type Alias | `typeNameDeserialize(input)`, etc. | Standalone functions |
9
+ | Type | Generated Code | Description |
10
+ | ---------- | ----------------------------------------------------------------------------------------- | ------------------------------------------- |
11
+ | Class | `classNameDeserialize(input)` + `static deserialize(input)` | Standalone function + static factory method |
12
+ | Enum | `enumNameDeserialize(input)`, `enumNameDeserializeWithContext(data)`, `enumNameIs(value)` | Standalone functions |
13
+ | Interface | `interfaceNameDeserialize(input)`, etc. | Standalone functions |
14
+ | Type Alias | `typeNameDeserialize(input)`, etc. | Standalone functions |
15
15
 
16
16
  ## Return Type
17
17
 
18
18
  All public deserialization methods return `Result<T, Array<{ field: string; message: string }>>`:
19
19
 
20
20
  - `Result.ok(value)` - Successfully deserialized value
21
- - `Result.err(errors)` - Array of validation errors with field names and messages
21
+ - `Result.err(errors)` - Array of validation errors with field names and messages
@@ -3,25 +3,31 @@
3
3
  Union types are deserialized based on their member types:
4
4
 
5
5
  ### Literal Unions
6
- For unions of literal values (`"A" | "B" | 123`), the value is validated against
7
- the allowed literals directly.
6
+
7
+ For unions of literal values (`"A" | "B" | 123`), the value is validated against the allowed
8
+ literals directly.
8
9
 
9
10
  ### Primitive Unions
10
- For unions containing primitive types (`string | number`), the deserializer uses
11
- `typeof` checks to validate the value type. No `__type` discriminator is needed.
11
+
12
+ For unions containing primitive types (`string | number`), the deserializer uses `typeof` checks to
13
+ validate the value type. No `__type` discriminator is needed.
12
14
 
13
15
  ### Class/Interface Unions
14
- For unions of serializable types (`User | Admin`), the deserializer requires a
15
- `__type` field in the JSON to dispatch to the correct type's `deserializeWithContext` method.
16
+
17
+ For unions of serializable types (`User | Admin`), the deserializer requires a `__type` field in the
18
+ JSON to dispatch to the correct type's `deserializeWithContext` method.
16
19
 
17
20
  ### Generic Type Parameters
18
- For generic unions like `type Result<T> = T | Error`, the generic type parameter `T`
19
- is passed through as-is since its concrete type is only known at the call site.
21
+
22
+ For generic unions like `type Result<T> = T | Error`, the generic type parameter `T` is passed
23
+ through as-is since its concrete type is only known at the call site.
20
24
 
21
25
  ### Mixed Unions
26
+
22
27
  Mixed unions (e.g., `string | Date | User`) check in order:
28
+
23
29
  1. Literal values
24
30
  2. Primitives (via `typeof`)
25
31
  3. Date (via `instanceof` or ISO string parsing)
26
32
  4. Serializable types (via `__type` dispatch)
27
- 5. Generic type parameters (pass-through)
33
+ 5. Generic type parameters (pass-through)
@@ -3,19 +3,23 @@
3
3
  The macro supports 30+ validators via `@serde(validate(...))`:
4
4
 
5
5
  ### String Validators
6
+
6
7
  - `email`, `url`, `uuid` - Format validation
7
8
  - `minLength(n)`, `maxLength(n)`, `length(n)` - Length constraints
8
9
  - `pattern("regex")` - Regular expression matching
9
10
  - `nonEmpty`, `trimmed`, `lowercase`, `uppercase` - String properties
10
11
 
11
12
  ### Number Validators
13
+
12
14
  - `gt(n)`, `gte(n)`, `lt(n)`, `lte(n)`, `between(min, max)` - Range checks
13
15
  - `int`, `positive`, `nonNegative`, `finite` - Number properties
14
16
 
15
17
  ### Array Validators
18
+
16
19
  - `minItems(n)`, `maxItems(n)`, `itemsCount(n)` - Collection size
17
20
 
18
21
  ### Date Validators
22
+
19
23
  - `validDate`, `afterDate("ISO")`, `beforeDate("ISO")` - Date validation
20
24
 
21
25
  ## Field-Level Options
@@ -31,4 +35,4 @@ The `@serde` decorator supports:
31
35
  ## Container-Level Options
32
36
 
33
37
  - `denyUnknownFields` - Error on unrecognized JSON properties
34
- - `renameAll = "camelCase"` - Apply naming convention to all fields
38
+ - `renameAll = "camelCase"` - Apply naming convention to all fields
@@ -4,311 +4,6 @@ The `Deserialize` macro generates JSON deserialization methods with **cycle and
4
4
  forward-reference support**, plus comprehensive runtime validation. This enables
5
5
  safe parsing of complex JSON structures including circular references.
6
6
 
7
- ## Generated Output
8
-
9
- | Type | Generated Code | Description |
10
- |------|----------------|-------------|
11
- | Class | `classNameDeserialize(input)` + `static deserialize(input)` | Standalone function + static factory method |
12
- | Enum | `enumNameDeserialize(input)`, `enumNameDeserializeWithContext(data)`, `enumNameIs(value)` | Standalone functions |
13
- | Interface | `interfaceNameDeserialize(input)`, etc. | Standalone functions |
14
- | Type Alias | `typeNameDeserialize(input)`, etc. | Standalone functions |
15
-
16
- ## Return Type
17
-
18
- All public deserialization methods return `Result<T, Array<{ field: string; message: string }>>`:
19
-
20
- - `Result.ok(value)` - Successfully deserialized value
21
- - `Result.err(errors)` - Array of validation errors with field names and messages
22
-
23
- ## Cycle/Forward-Reference Support
24
-
25
- Uses deferred patching to handle references:
26
-
27
- 1. When encountering `{ "__ref": id }`, returns a `PendingRef` marker
28
- 2. Continues deserializing other fields
29
- 3. After all objects are created, `ctx.applyPatches()` resolves all pending references
30
-
31
- References only apply to object-shaped, serializable values. The generator avoids probing for
32
- `__ref` on primitive-like fields (including literal unions and `T | null` where `T` is primitive-like),
33
- and it parses `Date` / `Date | null` from ISO strings without treating them as references.
34
-
35
- ## Validation
36
-
37
- The macro supports 30+ validators via `@serde(validate(...))`:
38
-
39
- ### String Validators
40
- - `email`, `url`, `uuid` - Format validation
41
- - `minLength(n)`, `maxLength(n)`, `length(n)` - Length constraints
42
- - `pattern("regex")` - Regular expression matching
43
- - `nonEmpty`, `trimmed`, `lowercase`, `uppercase` - String properties
44
-
45
- ### Number Validators
46
- - `gt(n)`, `gte(n)`, `lt(n)`, `lte(n)`, `between(min, max)` - Range checks
47
- - `int`, `positive`, `nonNegative`, `finite` - Number properties
48
-
49
- ### Array Validators
50
- - `minItems(n)`, `maxItems(n)`, `itemsCount(n)` - Collection size
51
-
52
- ### Date Validators
53
- - `validDate`, `afterDate("ISO")`, `beforeDate("ISO")` - Date validation
54
-
55
- ## Field-Level Options
56
-
57
- The `@serde` decorator supports:
58
-
59
- - `skip` / `skipDeserializing` - Exclude field from deserialization
60
- - `rename = "jsonKey"` - Read from different JSON property
61
- - `default` / `default = expr` - Use default value if missing
62
- - `flatten` - Read fields from parent object level
63
- - `validate(...)` - Apply validators
64
-
65
- ## Container-Level Options
66
-
67
- - `denyUnknownFields` - Error on unrecognized JSON properties
68
- - `renameAll = "camelCase"` - Apply naming convention to all fields
69
-
70
- ## Union Type Deserialization
71
-
72
- Union types are deserialized based on their member types:
73
-
74
- ### Literal Unions
75
- For unions of literal values (`"A" | "B" | 123`), the value is validated against
76
- the allowed literals directly.
77
-
78
- ### Primitive Unions
79
- For unions containing primitive types (`string | number`), the deserializer uses
80
- `typeof` checks to validate the value type. No `__type` discriminator is needed.
81
-
82
- ### Class/Interface Unions
83
- For unions of serializable types (`User | Admin`), the deserializer requires a
84
- `__type` field in the JSON to dispatch to the correct type's `deserializeWithContext` method.
85
-
86
- ### Generic Type Parameters
87
- For generic unions like `type Result<T> = T | Error`, the generic type parameter `T`
88
- is passed through as-is since its concrete type is only known at the call site.
89
-
90
- ### Mixed Unions
91
- Mixed unions (e.g., `string | Date | User`) check in order:
92
- 1. Literal values
93
- 2. Primitives (via `typeof`)
94
- 3. Date (via `instanceof` or ISO string parsing)
95
- 4. Serializable types (via `__type` dispatch)
96
- 5. Generic type parameters (pass-through)
97
-
98
- ## Example
99
-
100
- ```typescript before
101
- /** @derive(Deserialize) @serde({ denyUnknownFields: true }) */
102
- class User {
103
- id: number;
104
-
105
- /** @serde({ validate: { email: true, maxLength: 255 } }) */
106
- email: string;
107
-
108
- /** @serde({ default: "guest" }) */
109
- name: string;
110
-
111
- /** @serde({ validate: { positive: true } }) */
112
- age?: number;
113
- }
114
- ```
115
-
116
- ```typescript after
117
- import { DeserializeContext } from 'macroforge/serde';
118
- import { DeserializeError } from 'macroforge/serde';
119
- import type { DeserializeOptions } from 'macroforge/serde';
120
- import { PendingRef } from 'macroforge/serde';
121
-
122
- /** @serde({ denyUnknownFields: true }) */
123
- class User {
124
- id: number;
125
-
126
- email: string;
127
-
128
- name: string;
129
-
130
- age?: number;
131
-
132
- constructor(props: {
133
- id: number;
134
- email: string;
135
- name?: string;
136
- age?: number;
137
- }) {
138
- this.id = props.id;
139
- this.email = props.email;
140
- this.name = props.name as string;
141
- this.age = props.age as number;
142
- }
143
-
144
- /**
145
- * Deserializes input to an instance of this class.
146
- * Automatically detects whether input is a JSON string or object.
147
- * @param input - JSON string or object to deserialize
148
- * @param opts - Optional deserialization options
149
- * @returns Result containing the deserialized instance or validation errors
150
- */
151
- static deserialize(
152
- input: unknown,
153
- opts?: @{DESERIALIZE_OPTIONS}
154
- ): Result<
155
- User,
156
- Array<{
157
- field: string;
158
- message: string;
159
- }>
160
- > {
161
- try {
162
- // Auto-detect: if string, parse as JSON first
163
- const data = typeof input === 'string' ? JSON.parse(input) : input;
164
-
165
- const ctx = @{DESERIALIZE_CONTEXT}.create();
166
- const resultOrRef = User.deserializeWithContext(data, ctx);
167
- if (@{PENDING_REF}.is(resultOrRef)) {
168
- return Result.err([
169
- {
170
- field: '_root',
171
- message: 'User.deserialize: root cannot be a forward reference'
172
- }
173
- ]);
174
- }
175
- ctx.applyPatches();
176
- if (opts?.freeze) {
177
- ctx.freezeAll();
178
- }
179
- return Result.ok(resultOrRef);
180
- } catch (e) {
181
- if (e instanceof @{DESERIALIZE_ERROR}) {
182
- return Result.err(e.errors);
183
- }
184
- const message = e instanceof Error ? e.message : String(e);
185
- return Result.err([
186
- {
187
- field: '_root',
188
- message
189
- }
190
- ]);
191
- }
192
- }
193
-
194
- /** @internal */
195
- static deserializeWithContext(value: any, ctx: @{DESERIALIZE_CONTEXT}): User | @{PENDING_REF} {
196
- if (value?.__ref !== undefined) {
197
- return ctx.getOrDefer(value.__ref);
198
- }
199
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
200
- throw new @{DESERIALIZE_ERROR}([
201
- {
202
- field: '_root',
203
- message: 'User.deserializeWithContext: expected an object'
204
- }
205
- ]);
206
- }
207
- const obj = value as Record<string, unknown>;
208
- const errors: Array<{
209
- field: string;
210
- message: string;
211
- }> = [];
212
- const knownKeys = new Set(['__type', '__id', '__ref', 'id', 'email', 'name', 'age']);
213
- for (const key of Object.keys(obj)) {
214
- if (!knownKeys.has(key)) {
215
- errors.push({
216
- field: key,
217
- message: 'unknown field'
218
- });
219
- }
220
- }
221
- if (!('id' in obj)) {
222
- errors.push({
223
- field: 'id',
224
- message: 'missing required field'
225
- });
226
- }
227
- if (!('email' in obj)) {
228
- errors.push({
229
- field: 'email',
230
- message: 'missing required field'
231
- });
232
- }
233
- if (errors.length > 0) {
234
- throw new @{DESERIALIZE_ERROR}(errors);
235
- }
236
- const instance = Object.create(User.prototype) as User;
237
- if (obj.__id !== undefined) {
238
- ctx.register(obj.__id as number, instance);
239
- }
240
- ctx.trackForFreeze(instance);
241
- {
242
- const __raw_id = obj['id'] as number;
243
- instance.id = __raw_id;
244
- }
245
- {
246
- const __raw_email = obj['email'] as string;
247
- instance.email = __raw_email;
248
- }
249
- if ('name' in obj && obj['name'] !== undefined) {
250
- const __raw_name = obj['name'] as string;
251
- instance.name = __raw_name;
252
- } else {
253
- instance.name = "guest";
254
- }
255
- if ('age' in obj && obj['age'] !== undefined) {
256
- const __raw_age = obj['age'] as number;
257
- instance.age = __raw_age;
258
- }
259
- if (errors.length > 0) {
260
- throw new @{DESERIALIZE_ERROR}(errors);
261
- }
262
- return instance;
263
- }
264
-
265
- static validateField<K extends keyof User>(
266
- field: K,
267
- value: User[K]
268
- ): Array<{
269
- field: string;
270
- message: string;
271
- }> {
272
- return [];
273
- }
274
-
275
- static validateFields(partial: Partial<User>): Array<{
276
- field: string;
277
- message: string;
278
- }> {
279
- return [];
280
- }
281
-
282
- static hasShape(obj: unknown): boolean {
283
- if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
284
- return false;
285
- }
286
- const o = obj as Record<string, unknown>;
287
- return 'id' in o && 'email' in o;
288
- }
289
-
290
- static is(obj: unknown): obj is User {
291
- if (obj instanceof User) {
292
- return true;
293
- }
294
- if (!User.hasShape(obj)) {
295
- return false;
296
- }
297
- const result = User.deserialize(obj);
298
- return Result.isOk(result);
299
- }
300
- }
301
-
302
- // Usage:
303
- const result = User.deserialize('{"id":1,"email":"test@example.com"}');
304
- if (Result.isOk(result)) {
305
- const user = result.value;
306
- } else {
307
- console.error(result.error); // [{ field: "email", message: "must be a valid email" }]
308
- }
309
- ```
310
-
311
- ## Required Imports
312
-
313
- The generated code automatically imports:
314
- - `DeserializeContext`, `DeserializeError`, `PendingRef` from `macroforge/serde`
7
+ See the original module documentation for full details on generated output,
8
+ return types, cycle/forward-reference support, validation, field-level options,
9
+ container-level options, and union type deserialization.
@@ -21,10 +21,13 @@ Uses the standard polynomial rolling hash algorithm:
21
21
  ```text
22
22
  hash = 17 // Initial seed
23
23
  for each field:
24
- hash = (hash * 31 + fieldHash) | 0 // Bitwise OR keeps it 32-bit integer
24
+ hash = (hash * 31 + fieldHash) | 0
25
25
  ```
26
26
 
27
- This algorithm is consistent with Java's `Objects.hash()` implementation.
27
+ The `| 0` (bitwise OR with zero) at the end of each step coerces the result
28
+ to a 32-bit signed integer, preventing floating-point drift from repeated
29
+ multiplication. This is equivalent to casting to `i32` in Rust and consistent
30
+ with Java's `Objects.hash()` implementation.
28
31
 
29
32
  ## Type-Specific Hashing
30
33
 
@@ -49,7 +52,7 @@ The `@hash` decorator supports:
49
52
  ## Example
50
53
 
51
54
  ```typescript before
52
- /** @derive(Hash, PartialEq) */
55
+ /** @derive(Hash) */
53
56
  class User {
54
57
  id: number;
55
58
  name: string;
@@ -69,10 +72,6 @@ class User {
69
72
  static hashCode(value: User): number {
70
73
  return userHashCode(value);
71
74
  }
72
-
73
- static equals(a: User, b: User): boolean {
74
- return userEquals(a, b);
75
- }
76
75
  }
77
76
 
78
77
  export function userHashCode(value: User): number {
@@ -92,11 +91,6 @@ export function userHashCode(value: User): number {
92
91
  0;
93
92
  return hash;
94
93
  }
95
-
96
- export function userEquals(a: User, b: User): boolean {
97
- if (a === b) return true;
98
- return a.id === b.id && a.name === b.name && a.cachedScore === b.cachedScore;
99
- }
100
94
  ```
101
95
 
102
96
  ## Hash Contract
@@ -40,12 +40,12 @@ The `@partialEq` decorator supports:
40
40
  ## Example
41
41
 
42
42
  ```typescript before
43
- /** @derive(PartialEq, Hash) */
43
+ /** @derive(PartialEq) */
44
44
  class User {
45
45
  id: number;
46
46
  name: string;
47
47
 
48
- /** @partialEq({ skip: true }) @hash({ skip: true }) */
48
+ /** @partialEq({ skip: true }) */
49
49
  cachedScore: number;
50
50
  }
51
51
  ```
@@ -60,34 +60,12 @@ class User {
60
60
  static equals(a: User, b: User): boolean {
61
61
  return userEquals(a, b);
62
62
  }
63
-
64
- static hashCode(value: User): number {
65
- return userHashCode(value);
66
- }
67
63
  }
68
64
 
69
65
  export function userEquals(a: User, b: User): boolean {
70
66
  if (a === b) return true;
71
67
  return a.id === b.id && a.name === b.name;
72
68
  }
73
-
74
- export function userHashCode(value: User): number {
75
- let hash = 17;
76
- hash =
77
- (hash * 31 +
78
- (Number.isInteger(value.id)
79
- ? value.id | 0
80
- : value.id
81
- .toString()
82
- .split('')
83
- .reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) |
84
- 0;
85
- hash =
86
- (hash * 31 +
87
- (value.name ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)) |
88
- 0;
89
- return hash;
90
- }
91
69
  ```
92
70
 
93
71
  ## Equality Contract
@@ -98,55 +76,5 @@ When implementing `PartialEq`, consider also implementing `Hash`:
98
76
  - **Symmetry**: `a.equals(b)` implies `b.equals(a)`
99
77
  - **Hash consistency**: Equal objects must have equal hash codes
100
78
 
101
- To maintain the hash contract, skip the same fields in both `PartialEq` and `Hash`:
102
-
103
- ```typescript before
104
- /** @derive(PartialEq, Hash) */
105
- class User {
106
- id: number;
107
- name: string;
108
-
109
- /** @partialEq({ skip: true }) @hash({ skip: true }) */
110
- cachedScore: number;
111
- }
112
- ```
113
-
114
- ```typescript after
115
- class User {
116
- id: number;
117
- name: string;
118
-
119
- cachedScore: number;
120
-
121
- static equals(a: User, b: User): boolean {
122
- return userEquals(a, b);
123
- }
124
-
125
- static hashCode(value: User): number {
126
- return userHashCode(value);
127
- }
128
- }
129
-
130
- export function userEquals(a: User, b: User): boolean {
131
- if (a === b) return true;
132
- return a.id === b.id && a.name === b.name;
133
- }
134
-
135
- export function userHashCode(value: User): number {
136
- let hash = 17;
137
- hash =
138
- (hash * 31 +
139
- (Number.isInteger(value.id)
140
- ? value.id | 0
141
- : value.id
142
- .toString()
143
- .split('')
144
- .reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))) |
145
- 0;
146
- hash =
147
- (hash * 31 +
148
- (value.name ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)) |
149
- 0;
150
- return hash;
151
- }
152
- ```
79
+ To maintain the hash contract, skip the same fields in both `PartialEq` and `Hash`,
80
+ as shown in the example above using `@partialEq({ skip: true }) @hash({ skip: true })`.
@@ -9,18 +9,20 @@ between values where some pairs may be incomparable.
9
9
  | Type | Generated Code | Description |
10
10
  |------|----------------|-------------|
11
11
  | Class | `classNamePartialCompare(a, b)` + `static compareTo(a, b)` | Standalone function + static wrapper method |
12
- | Enum | `enumNamePartialCompare(a: EnumName, b: EnumName): Option<number>` | Standalone function returning Option |
13
- | Interface | `interfaceNamePartialCompare(a: InterfaceName, b: InterfaceName): Option<number>` | Standalone function with Option |
14
- | Type Alias | `typeNamePartialCompare(a: TypeName, b: TypeName): Option<number>` | Standalone function with Option |
12
+ | Enum | `enumNamePartialCompare(a, b): number \| null` | Standalone function returning `number \| null` |
13
+ | Interface | `ifaceNamePartialCompare(a, b): number \| null` | Standalone function returning `number \| null` |
14
+ | Type Alias | `typeNamePartialCompare(a, b): number \| null` | Standalone function returning `number \| null` |
15
+
16
+ Names use **camelCase** conversion (e.g., `Temperature` → `temperaturePartialCompare`).
15
17
 
16
18
  ## Return Values
17
19
 
18
- Unlike `Ord`, `PartialOrd` returns an `Option<number>` to handle incomparable values:
20
+ Unlike `Ord`, `PartialOrd` returns `number | null` to handle incomparable values:
19
21
 
20
- - **Option.some(-1)**: `a` is less than `b`
21
- - **Option.some(0)**: `a` is equal to `b`
22
- - **Option.some(1)**: `a` is greater than `b`
23
- - **Option.none()**: Values are incomparable
22
+ - **-1**: `a` is less than `b`
23
+ - **0**: `a` is equal to `b`
24
+ - **1**: `a` is greater than `b`
25
+ - **null**: Values are incomparable
24
26
 
25
27
  ## When to Use PartialOrd vs Ord
26
28
 
@@ -36,8 +38,8 @@ Unlike `Ord`, `PartialOrd` returns an `Option<number>` to handle incomparable va
36
38
  Fields are compared **lexicographically** in declaration order:
37
39
 
38
40
  1. Compare first field
39
- 2. If incomparable, return `Option.none()`
40
- 3. If not equal, return that result wrapped in `Option.some()`
41
+ 2. If incomparable, return `null`
42
+ 3. If not equal, return that result
41
43
  4. Otherwise, compare next field
42
44
  5. Continue until a difference is found or all fields are equal
43
45
 
@@ -45,13 +47,13 @@ Fields are compared **lexicographically** in declaration order:
45
47
 
46
48
  | Type | Comparison Method |
47
49
  |------|-------------------|
48
- | `number`/`bigint` | Direct comparison, returns some() |
49
- | `string` | `localeCompare()` wrapped in some() |
50
- | `boolean` | false &lt; true, wrapped in some() |
51
- | null/undefined | Returns none() for mismatched nullability |
52
- | Arrays | Lexicographic, propagates none() on incomparable elements |
53
- | `Date` | Timestamp comparison, none() if invalid |
54
- | Objects | Unwraps nested Option from compareTo() |
50
+ | `number`/`bigint` | Direct subtraction (`a - b`) |
51
+ | `string` | `localeCompare()` |
52
+ | `boolean` | `false < true` (cast to number) |
53
+ | null/undefined | Returns `null` for mismatched nullability |
54
+ | Arrays | Lexicographic, propagates `null` on incomparable elements |
55
+ | `Date` | Timestamp comparison, `null` if invalid |
56
+ | Objects | Delegates to `compareTo()` if available |
55
57
 
56
58
  ## Field-Level Options
57
59
 
@@ -94,7 +94,7 @@ class User {
94
94
  @param value - The value to serialize
95
95
  @param ctx - The serialization context */
96
96
 
97
- static serializeWithContext(value: User, ctx: @{SERIALIZE_CONTEXT}): Record<string, unknown> {
97
+ static serializeWithContext(value: User, ctx: @SERIALIZE_CONTEXT): Record<string, unknown> {
98
98
  return userSerializeWithContext(value, ctx);
99
99
  }
100
100
  }
@@ -104,7 +104,7 @@ class User {
104
104
  @returns JSON string representation with cycle detection metadata */ export function userSerialize(
105
105
  value: User
106
106
  ): string {
107
- const ctx = @{SERIALIZE_CONTEXT}.create();
107
+ const ctx = @SERIALIZE_CONTEXT.create();
108
108
  return JSON.stringify(userSerializeWithContext(value, ctx));
109
109
  } /** @internal Serializes with an existing context for nested/cyclic object graphs.
110
110
  @param value - The value to serialize
@@ -123,7 +123,7 @@ export function userSerializeWithContext(
123
123
  result['userName'] = value.name;
124
124
  {
125
125
  const __flattened = userMetadataSerializeWithContext(value.metadata, ctx);
126
- const { __type: _, __id: __, ...rest } = __flattened as any;
126
+ const { __type: _, __id: __, ...rest } = __flattened as any; // tag field name is configurable via `tag` option
127
127
  Object.assign(result, rest);
128
128
  }
129
129
  return result;
@@ -21,7 +21,7 @@ Or build from source:
21
21
  Bash
22
22
 
23
23
  ```
24
- git clone https://github.com/rymskip/macroforge-ts.git
24
+ git clone https://github.com/macroforge-ts/macroforge-ts.git
25
25
  cd macroforge-ts/crates
26
26
  cargo build --release --bin macroforge
27
27
 
@@ -25,7 +25,7 @@ and editors.
25
25
  The language servers are functional and used during development of macroforge itself. However, they
26
26
  require manual installation:
27
27
 
28
- 1. Fork or clone the [macroforge-ts repository](https://github.com/rymskip/macroforge-ts)
28
+ 1. Fork or clone the [macroforge-ts repository](https://github.com/macroforge-ts/macroforge-ts)
29
29
  2. Build the extension you need
30
30
  3. Install it as a developer extension in your editor
31
31
 
@@ -25,7 +25,7 @@ This package is not yet published as an official extension. You'll need to build
25
25
  ### 1. Clone the Repository
26
26
 
27
27
  ```bash
28
- git clone https://github.com/rymskip/macroforge-ts.git
28
+ git clone https://github.com/macroforge-ts/macroforge-ts.git
29
29
  cd macroforge-ts
30
30
  ```
31
31
 
@@ -23,7 +23,7 @@ manually.
23
23
  Bash
24
24
 
25
25
  ```
26
- git clone https://github.com/rymskip/macroforge-ts.git
26
+ git clone https://github.com/macroforge-ts/macroforge-ts.git
27
27
  cd macroforge-ts
28
28
  ```
29
29
 
@@ -20,7 +20,7 @@ These extensions are not yet in the Zed extension registry. You'll need to insta
20
20
  ### 1. Clone the Repository
21
21
 
22
22
  ```bash
23
- git clone https://github.com/rymskip/macroforge-ts.git
23
+ git clone https://github.com/macroforge-ts/macroforge-ts.git
24
24
  cd macroforge-ts
25
25
  ```
26
26
 
@@ -22,7 +22,7 @@ extensions.
22
22
  Bash
23
23
 
24
24
  ```
25
- git clone https://github.com/rymskip/macroforge-ts.git
25
+ git clone https://github.com/macroforge-ts/macroforge-ts.git
26
26
  cd macroforge-ts
27
27
  ```
28
28
 
@@ -73,7 +73,7 @@ Improvements to the developer experience.
73
73
 
74
74
  Interested in helping? We welcome contributions of all kinds:
75
75
 
76
- - Feature requests and feedback on [GitHub Issues](https://github.com/rymskip/macroforge-ts/issues)
76
+ - Feature requests and feedback on [GitHub Issues](https://github.com/macroforge-ts/macroforge-ts/issues)
77
77
  - Pull requests for new macros or improvements
78
78
  - Documentation improvements
79
79
  - Framework integrations
@@ -63,65 +63,12 @@
63
63
  "path": "builtin-macros/serialize.md",
64
64
  "use_cases": "toJSON, serialization, json, api, data transfer"
65
65
  },
66
- {
67
- "id": "deserialize/overview",
68
- "title": "Deserialize: Overview",
69
- "category": "builtin-macros",
70
- "category_title": "Built-in Macros",
71
- "path": "builtin-macros/deserialize/overview.md",
72
- "use_cases": "fromJSON, deserialization, deserialize, classnamedeserialize(input), enumnamedeserialize(input)",
73
- "parent_id": "deserialize"
74
- },
75
- {
76
- "id": "deserialize/cycleforward-reference-support",
77
- "title": "Deserialize: Cycle/Forward-Reference Support",
78
- "category": "builtin-macros",
79
- "category_title": "Built-in Macros",
80
- "path": "builtin-macros/deserialize/cycleforward-reference-support.md",
81
- "use_cases": "fromJSON, deserialization, pendingref, ctx.applypatches(), __ref",
82
- "parent_id": "deserialize"
83
- },
84
- {
85
- "id": "deserialize/validation",
86
- "title": "Deserialize: Validation",
87
- "category": "builtin-macros",
88
- "category_title": "Built-in Macros",
89
- "path": "builtin-macros/deserialize/validation.md",
90
- "use_cases": "fromJSON, deserialization, @serde(validate(...)), email, url, uuid",
91
- "parent_id": "deserialize"
92
- },
93
- {
94
- "id": "deserialize/union-type-deserialization",
95
- "title": "Deserialize: Union Type Deserialization",
96
- "category": "builtin-macros",
97
- "category_title": "Built-in Macros",
98
- "path": "builtin-macros/deserialize/union-type-deserialization.md",
99
- "use_cases": "fromJSON, deserialization, typeof, __type",
100
- "parent_id": "deserialize"
101
- },
102
- {
103
- "id": "deserialize/example",
104
- "title": "Deserialize: Example",
105
- "category": "builtin-macros",
106
- "category_title": "Built-in Macros",
107
- "path": "builtin-macros/deserialize/example.md",
108
- "use_cases": "fromJSON, deserialization",
109
- "parent_id": "deserialize"
110
- },
111
66
  {
112
67
  "id": "deserialize",
113
68
  "title": "Deserialize",
114
69
  "category": "builtin-macros",
115
70
  "category_title": "Built-in Macros",
116
71
  "path": "builtin-macros/deserialize.md",
117
- "use_cases": "fromJSON, deserialization, parsing, validation, json",
118
- "is_chunked": true,
119
- "chunk_ids": [
120
- "deserialize/overview",
121
- "deserialize/cycleforward-reference-support",
122
- "deserialize/validation",
123
- "deserialize/union-type-deserialization",
124
- "deserialize/example"
125
- ]
72
+ "use_cases": "fromJSON, deserialization, parsing, validation, json"
126
73
  }
127
74
  ]
package/package.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "main": "dist/index.js",
26
26
  "name": "@macroforge/mcp-server",
27
27
  "peerDependencies": {
28
- "macroforge": "^0.1.77"
28
+ "macroforge": "file:../../crates/macroforge_ts"
29
29
  },
30
30
  "peerDependenciesMeta": {
31
31
  "macroforge": {
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",
37
- "url": "git+https://github.com/macroforge-ts/mcp-server.git"
37
+ "url": "git+https://github.com/macroforge-ts/macroforge-ts.git"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "deno install --node-modules-dir --quiet && deno run -A npm:typescript@5/tsc && chmod +x dist/index.js",
@@ -46,5 +46,5 @@
46
46
  "test": "node --test tests/**/*.test.js"
47
47
  },
48
48
  "type": "module",
49
- "version": "0.1.77"
49
+ "version": "0.1.79"
50
50
  }