@macroforge/mcp-server 0.1.76 → 0.1.78

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
@@ -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
  }
@@ -150,7 +150,7 @@ class User {
150
150
  */
151
151
  static deserialize(
152
152
  input: unknown,
153
- opts?: @{DESERIALIZE_OPTIONS}
153
+ opts?: @DESERIALIZE_OPTIONS
154
154
  ): Result<
155
155
  User,
156
156
  Array<{
@@ -162,9 +162,9 @@ class User {
162
162
  // Auto-detect: if string, parse as JSON first
163
163
  const data = typeof input === 'string' ? JSON.parse(input) : input;
164
164
 
165
- const ctx = @{DESERIALIZE_CONTEXT}.create();
165
+ const ctx = @DESERIALIZE_CONTEXT.create();
166
166
  const resultOrRef = User.deserializeWithContext(data, ctx);
167
- if (@{PENDING_REF}.is(resultOrRef)) {
167
+ if (@PENDING_REF.is(resultOrRef)) {
168
168
  return Result.err([
169
169
  {
170
170
  field: '_root',
@@ -178,7 +178,7 @@ class User {
178
178
  }
179
179
  return Result.ok(resultOrRef);
180
180
  } catch (e) {
181
- if (e instanceof @{DESERIALIZE_ERROR}) {
181
+ if (e instanceof @DESERIALIZE_ERROR) {
182
182
  return Result.err(e.errors);
183
183
  }
184
184
  const message = e instanceof Error ? e.message : String(e);
@@ -192,12 +192,12 @@ class User {
192
192
  }
193
193
 
194
194
  /** @internal */
195
- static deserializeWithContext(value: any, ctx: @{DESERIALIZE_CONTEXT}): User | @{PENDING_REF} {
195
+ static deserializeWithContext(value: any, ctx: @DESERIALIZE_CONTEXT): User | @PENDING_REF {
196
196
  if (value?.__ref !== undefined) {
197
197
  return ctx.getOrDefer(value.__ref);
198
198
  }
199
199
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
200
- throw new @{DESERIALIZE_ERROR}([
200
+ throw new @DESERIALIZE_ERROR([
201
201
  {
202
202
  field: '_root',
203
203
  message: 'User.deserializeWithContext: expected an object'
@@ -231,7 +231,7 @@ class User {
231
231
  });
232
232
  }
233
233
  if (errors.length > 0) {
234
- throw new @{DESERIALIZE_ERROR}(errors);
234
+ throw new @deserialize_error_expr(errors);
235
235
  }
236
236
  const instance = Object.create(User.prototype) as User;
237
237
  if (obj.__id !== undefined) {
@@ -257,7 +257,7 @@ class User {
257
257
  instance.age = __raw_age;
258
258
  }
259
259
  if (errors.length > 0) {
260
- throw new @{DESERIALIZE_ERROR}(errors);
260
+ throw new @deserialize_error_expr(errors);
261
261
  }
262
262
  return instance;
263
263
  }
@@ -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;
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.76"
28
+ "macroforge": "^0.1.78"
29
29
  },
30
30
  "peerDependenciesMeta": {
31
31
  "macroforge": {
@@ -46,5 +46,5 @@
46
46
  "test": "node --test tests/**/*.test.js"
47
47
  },
48
48
  "type": "module",
49
- "version": "0.1.76"
49
+ "version": "0.1.78"
50
50
  }