adorn-api 1.1.1 → 1.1.2

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.
@@ -121,7 +121,15 @@ function isPlainObject(value) {
121
121
  !(value instanceof Date));
122
122
  }
123
123
  function toPlainObject(value) {
124
- // 1. Handle lazy-load wrappers (BelongsToReference)
124
+ // 1. Check if value has custom toJSON method (e.g., metal-orm entities)
125
+ if (value !== null &&
126
+ typeof value === "object" &&
127
+ typeof value.toJSON === "function") {
128
+ // Use the custom toJSON which handles circular refs and includes properly
129
+ const jsonResult = value.toJSON();
130
+ return jsonResult;
131
+ }
132
+ // 2. Handle lazy-load wrappers (BelongsToReference)
125
133
  if (typeof value === "object" && typeof value.load === "function") {
126
134
  const wrapper = value;
127
135
  if (wrapper.current !== undefined && wrapper.current !== null) {
@@ -132,11 +140,11 @@ function toPlainObject(value) {
132
140
  }
133
141
  return null;
134
142
  }
135
- // 2. Handle plain objects
143
+ // 3. Handle plain objects
136
144
  if (isPlainObject(value)) {
137
145
  return value;
138
146
  }
139
- // 3. Convert class instances to plain objects
147
+ // 4. Convert class instances to plain objects
140
148
  if (typeof value === "object") {
141
149
  const result = {};
142
150
  for (const key of Object.getOwnPropertyNames(value)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adorn-api",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Decorator-first web framework with OpenAPI 3.1 schema generation.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -138,7 +138,16 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
138
138
  }
139
139
 
140
140
  function toPlainObject(value: unknown): Record<string, unknown> | null {
141
- // 1. Handle lazy-load wrappers (BelongsToReference)
141
+ // 1. Check if value has custom toJSON method (e.g., metal-orm entities)
142
+ if (value !== null &&
143
+ typeof value === "object" &&
144
+ typeof (value as { toJSON?: () => unknown }).toJSON === "function") {
145
+ // Use the custom toJSON which handles circular refs and includes properly
146
+ const jsonResult = (value as { toJSON: () => unknown }).toJSON();
147
+ return jsonResult as Record<string, unknown>;
148
+ }
149
+
150
+ // 2. Handle lazy-load wrappers (BelongsToReference)
142
151
  if (typeof value === "object" && typeof (value as Record<string, unknown>).load === "function") {
143
152
  const wrapper = value as { current: unknown; loaded: boolean; load: () => unknown };
144
153
  if (wrapper.current !== undefined && wrapper.current !== null) {
@@ -149,11 +158,11 @@ function toPlainObject(value: unknown): Record<string, unknown> | null {
149
158
  }
150
159
  return null;
151
160
  }
152
- // 2. Handle plain objects
161
+ // 3. Handle plain objects
153
162
  if (isPlainObject(value)) {
154
163
  return value;
155
164
  }
156
- // 3. Convert class instances to plain objects
165
+ // 4. Convert class instances to plain objects
157
166
  if (typeof value === "object") {
158
167
  const result: Record<string, unknown> = {};
159
168
  for (const key of Object.getOwnPropertyNames(value)) {
@@ -1,6 +1,7 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { serializeResponse } from "../../src/adapter/express/response-serializer";
3
3
  import { t } from "../../src/core/schema";
4
+ import { Dto, Field } from "../../src/core/decorators";
4
5
 
5
6
  describe("serializeResponse", () => {
6
7
  describe("Buffer with format: byte", () => {
@@ -145,3 +146,49 @@ describe("t.bytes() helper", () => {
145
146
  });
146
147
  });
147
148
  });
149
+
150
+ describe("serializeResponse with toJSON", () => {
151
+ it("should use toJSON method when available", () => {
152
+ // Create an object with non-enumerable properties and custom toJSON
153
+ const entity = {
154
+ id: 1,
155
+ name: "Test",
156
+ // toJSON is the standard way to customize JSON serialization
157
+ toJSON() {
158
+ return {
159
+ id: this.id,
160
+ name: this.name,
161
+ nested: { value: "included" } // This is non-enumerable in real entity
162
+ };
163
+ }
164
+ };
165
+
166
+ // Make nested non-enumerable to simulate metal-orm
167
+ Object.defineProperty(entity, 'nested', {
168
+ value: { value: "included" },
169
+ enumerable: false,
170
+ writable: true,
171
+ configurable: true
172
+ });
173
+
174
+ @Dto({})
175
+ class TestDto {
176
+ @Field(t.integer())
177
+ id!: number;
178
+
179
+ @Field(t.string())
180
+ name!: string;
181
+
182
+ @Field(t.object({}))
183
+ nested!: { value: string };
184
+ }
185
+
186
+ const result = serializeResponse(entity, TestDto);
187
+
188
+ expect(result).toEqual({
189
+ id: 1,
190
+ name: "Test",
191
+ nested: { value: "included" }
192
+ });
193
+ });
194
+ });