@fluojs/serialization 1.0.0-beta.2 → 1.0.0-beta.4

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.
package/README.ko.md CHANGED
@@ -97,7 +97,7 @@ class ProductDto {
97
97
 
98
98
  ### 순환 참조 처리
99
99
 
100
- fluo의 직렬화 엔진은 순환 참조를 자동으로 감지하고, 반복되는 참조에 대해 `undefined`를 반환하여 절단함으로써 무한 루프와 스택 오버플로를 방지하고 일반 응답 형태를 유지합니다.
100
+ fluo의 직렬화 엔진은 활성 순환 참조를 자동으로 감지하고 `undefined`로 절단하여 무한 루프와 스택 오버플로를 방지합니다. 이미 직렬화가 끝난 공유 참조는 삭제하지 않고 직렬화된 그래프 안에서 재사용합니다. 예를 들어 두 sibling 필드가 같은 원본 객체를 가리키면 두 직렬화 결과도 같은 직렬화 객체를 가리키며, 현재 직렬화 중인 객체를 다시 만나는 활성 cycle만 `undefined`로 절단됩니다.
101
101
 
102
102
  ### 상속된 데코레이터 계약
103
103
 
@@ -109,7 +109,7 @@ fluo의 직렬화 엔진은 순환 참조를 자동으로 감지하고, 반복
109
109
 
110
110
  ### 비JSON leaf 값
111
111
 
112
- `serialize()`는 데코레이터 메타데이터를 적용하고 배열/일반 객체를 재귀적으로 순회하지만, 모든 leaf 값을 엄격한 JSON 타입으로 강제 변환하지는 않습니다. `Date`, `bigint`, 함수, `symbol` 같은 값은 `@Transform(...)`이나 최종 HTTP 응답 작성 전에 직접 정규화하지 않으면 그대로 통과할 수 있습니다.
112
+ `serialize()`는 데코레이터 메타데이터를 적용하고 배열/일반 객체를 재귀적으로 순회하지만, 모든 leaf 값을 엄격한 JSON 타입으로 강제 변환하지는 않습니다. `Date`, `Map`, `Set`, `URL`, `URLSearchParams`, `RegExp`, `Error`, `ArrayBuffer`, typed array, `WeakMap`, `WeakSet`, `Promise` 같은 opaque built-in은 DTO 같은 클래스 인스턴스로 펼치지 않고 그대로 통과합니다. `bigint`, 함수, `symbol` 같은 값도 `@Transform(...)`이나 최종 HTTP 응답 작성 전에 직접 정규화하지 않으면 그대로 통과할 수 있습니다.
113
113
 
114
114
  ### HTTP 인터셉터와 함께 사용
115
115
 
package/README.md CHANGED
@@ -101,7 +101,7 @@ class UsersController {
101
101
 
102
102
  ### Cycle-safe serialization
103
103
 
104
- The serializer cuts cyclic references safely instead of recursing forever, so complex object graphs can still be turned into plain response-shaped objects without unbounded recursion.
104
+ The serializer cuts active cyclic references safely instead of recursing forever, so complex object graphs can still be turned into plain response-shaped objects without unbounded recursion. Completed shared references are reused in the serialized graph rather than dropped: if two sibling fields point at the same source object, both serialized fields point at the same serialized object. Only an object that is encountered again while it is already being serialized is cut to `undefined`.
105
105
 
106
106
  ### Inherited decorator contracts
107
107
 
@@ -113,7 +113,7 @@ Serialization metadata declared on a base class is inherited by derived DTOs. `@
113
113
 
114
114
  ### Non-JSON leaf values
115
115
 
116
- `serialize()` applies decorator metadata and recursively walks arrays/plain objects, but it does not coerce every leaf into strict JSON types. Values such as `Date`, `bigint`, functions, and symbols can pass through unchanged unless you normalize them with `@Transform(...)` or before writing the final HTTP response.
116
+ `serialize()` applies decorator metadata and recursively walks arrays/plain objects, but it does not coerce every leaf into strict JSON types. Opaque built-ins such as `Date`, `Map`, `Set`, `URL`, `URLSearchParams`, `RegExp`, `Error`, `ArrayBuffer`, typed arrays, `WeakMap`, `WeakSet`, and `Promise` pass through unchanged instead of being flattened as DTO-like class instances. Values such as `bigint`, functions, and symbols can also pass through unchanged unless you normalize them with `@Transform(...)` or before writing the final HTTP response.
117
117
 
118
118
  ## Public API Overview
119
119
 
@@ -1,15 +1,49 @@
1
1
  import { type MetadataPropertyKey } from '@fluojs/core';
2
+ /**
3
+ * Defines the transform function type.
4
+ */
2
5
  export type TransformFunction = (value: unknown) => unknown;
6
+ /**
7
+ * Describes the class serialization options contract.
8
+ */
3
9
  export interface ClassSerializationOptions {
4
10
  excludeExtraneous?: boolean;
5
11
  }
12
+ /**
13
+ * Describes the serialization field metadata contract.
14
+ */
6
15
  export interface SerializationFieldMetadata {
7
16
  excluded?: boolean;
8
17
  exposed?: boolean;
9
18
  transforms?: TransformFunction[];
10
19
  }
20
+ /**
21
+ * Update class serialization options.
22
+ *
23
+ * @param metadata The metadata.
24
+ * @param partial The partial.
25
+ */
11
26
  export declare function updateClassSerializationOptions(metadata: unknown, partial: ClassSerializationOptions): void;
27
+ /**
28
+ * Update field serialization metadata.
29
+ *
30
+ * @param metadata The metadata.
31
+ * @param propertyKey The property key.
32
+ * @param update The update.
33
+ */
12
34
  export declare function updateFieldSerializationMetadata(metadata: unknown, propertyKey: MetadataPropertyKey, update: (current: SerializationFieldMetadata | undefined) => SerializationFieldMetadata): void;
35
+ /**
36
+ * Get class serialization options.
37
+ *
38
+ * @param constructor The constructor.
39
+ * @returns The get class serialization options result.
40
+ */
13
41
  export declare function getClassSerializationOptions(constructor: Function): ClassSerializationOptions;
42
+ /**
43
+ * Get field serialization metadata.
44
+ *
45
+ * @param constructor The constructor.
46
+ * @returns The get field serialization metadata result.
47
+ */
14
48
  export declare function getFieldSerializationMetadata(constructor: Function): Map<MetadataPropertyKey, SerializationFieldMetadata>;
15
49
  //# sourceMappingURL=metadata.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAKxD,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAE5D,MAAM,WAAW,yBAAyB;IACxC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAClC;AAiED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,GAAG,IAAI,CAE3G;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,OAAO,EACjB,WAAW,EAAE,mBAAmB,EAChC,MAAM,EAAE,CAAC,OAAO,EAAE,0BAA0B,GAAG,SAAS,KAAK,0BAA0B,GACtF,IAAI,CAGN;AAED,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,QAAQ,GAAG,yBAAyB,CAK7F;AAED,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,QAAQ,GAAG,GAAG,CAAC,mBAAmB,EAAE,0BAA0B,CAAC,CAuBzH"}
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAKxD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAClC;AA6DD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,GAAG,IAAI,CAE3G;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,OAAO,EACjB,WAAW,EAAE,mBAAmB,EAChC,MAAM,EAAE,CAAC,OAAO,EAAE,0BAA0B,GAAG,SAAS,KAAK,0BAA0B,GACtF,IAAI,CAGN;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,QAAQ,GAAG,yBAAyB,CAK7F;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,QAAQ,GAAG,GAAG,CAAC,mBAAmB,EAAE,0BAA0B,CAAC,CAuBzH"}
package/dist/metadata.js CHANGED
@@ -1,4 +1,17 @@
1
- import { metadataSymbol } from '@fluojs/core/internal';
1
+ import { getOwnStandardConstructorMetadataBag, metadataSymbol } from '@fluojs/core/internal';
2
+
3
+ /**
4
+ * Defines the transform function type.
5
+ */
6
+
7
+ /**
8
+ * Describes the class serialization options contract.
9
+ */
10
+
11
+ /**
12
+ * Describes the serialization field metadata contract.
13
+ */
14
+
2
15
  const standardSerializationClassMetadataKey = Symbol.for('fluo.standard.serialization.class');
3
16
  const standardSerializationFieldMetadataKey = Symbol.for('fluo.standard.serialization.field');
4
17
  function getStandardMetadataBag(metadata) {
@@ -29,10 +42,7 @@ function getClassMetadataObject(metadata) {
29
42
  return created;
30
43
  }
31
44
  function getOwnMetadataBagFromConstructor(constructor) {
32
- if (!Object.prototype.hasOwnProperty.call(constructor, metadataSymbol)) {
33
- return undefined;
34
- }
35
- return constructor[metadataSymbol];
45
+ return getOwnStandardConstructorMetadataBag(constructor);
36
46
  }
37
47
  function getConstructorMetadataBags(constructor) {
38
48
  const bags = [];
@@ -46,19 +56,48 @@ function getConstructorMetadataBags(constructor) {
46
56
  }
47
57
  return bags;
48
58
  }
59
+
60
+ /**
61
+ * Update class serialization options.
62
+ *
63
+ * @param metadata The metadata.
64
+ * @param partial The partial.
65
+ */
49
66
  export function updateClassSerializationOptions(metadata, partial) {
50
67
  Object.assign(getClassMetadataObject(metadata), partial);
51
68
  }
69
+
70
+ /**
71
+ * Update field serialization metadata.
72
+ *
73
+ * @param metadata The metadata.
74
+ * @param propertyKey The property key.
75
+ * @param update The update.
76
+ */
52
77
  export function updateFieldSerializationMetadata(metadata, propertyKey, update) {
53
78
  const map = getFieldMetadataMap(metadata);
54
79
  map.set(propertyKey, update(map.get(propertyKey)));
55
80
  }
81
+
82
+ /**
83
+ * Get class serialization options.
84
+ *
85
+ * @param constructor The constructor.
86
+ * @returns The get class serialization options result.
87
+ */
56
88
  export function getClassSerializationOptions(constructor) {
57
89
  return getConstructorMetadataBags(constructor).reduce((options, bag) => ({
58
90
  ...options,
59
91
  ...bag[standardSerializationClassMetadataKey]
60
92
  }), {});
61
93
  }
94
+
95
+ /**
96
+ * Get field serialization metadata.
97
+ *
98
+ * @param constructor The constructor.
99
+ * @returns The get field serialization metadata result.
100
+ */
62
101
  export function getFieldSerializationMetadata(constructor) {
63
102
  const merged = new Map();
64
103
  for (const bag of getConstructorMetadataBags(constructor)) {
@@ -1,12 +1,13 @@
1
1
  /**
2
- * Serializes class instances and object graphs into JSON-safe plain values.
2
+ * Serializes class instances and object graphs into plain response-shaped values.
3
3
  *
4
4
  * Serialization honors `@Expose()`, `@Exclude()`, and `@Transform()` metadata.
5
5
  * Cycles and repeated references are handled without unbounded recursion.
6
+ * Opaque built-ins and non-JSON leaf values such as `Date`, `Map`, `Set`, `URL`, `Error`, `bigint`, functions, and symbols pass through unchanged unless you normalize them before or during serialization.
6
7
  *
7
8
  * @typeParam T Input value type.
8
9
  * @param value Value or object graph to serialize.
9
- * @returns A plain JSON-safe structure ready for HTTP response writing.
10
+ * @returns A plain recursively serialized structure whose opaque objects and non-JSON leaf values are preserved unless transformed.
10
11
  *
11
12
  * @example
12
13
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAiPA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAOxD"}
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAkQA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAOxD"}
package/dist/serialize.js CHANGED
@@ -9,6 +9,9 @@ function isPlainObject(value) {
9
9
  const prototype = Object.getPrototypeOf(value);
10
10
  return prototype === Object.prototype || prototype === null;
11
11
  }
12
+ function isOpaqueObject(value) {
13
+ return value instanceof Date || value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet || value instanceof URL || value instanceof URLSearchParams || value instanceof RegExp || value instanceof Error || value instanceof ArrayBuffer || ArrayBuffer.isView(value) || value instanceof Promise;
14
+ }
12
15
  function getSerializableConstructor(value) {
13
16
  const prototype = Object.getPrototypeOf(value);
14
17
  if (prototype === null || prototype === Object.prototype) {
@@ -147,10 +150,10 @@ function serializeInternal(value, context) {
147
150
  }
148
151
  });
149
152
  }
150
- if (value instanceof Date) {
151
- return value;
152
- }
153
153
  if (isObjectLike(value)) {
154
+ if (isOpaqueObject(value)) {
155
+ return value;
156
+ }
154
157
  if (isPlainObject(value)) {
155
158
  return serializeRecord(value, context);
156
159
  }
@@ -160,14 +163,15 @@ function serializeInternal(value, context) {
160
163
  }
161
164
 
162
165
  /**
163
- * Serializes class instances and object graphs into JSON-safe plain values.
166
+ * Serializes class instances and object graphs into plain response-shaped values.
164
167
  *
165
168
  * Serialization honors `@Expose()`, `@Exclude()`, and `@Transform()` metadata.
166
169
  * Cycles and repeated references are handled without unbounded recursion.
170
+ * Opaque built-ins and non-JSON leaf values such as `Date`, `Map`, `Set`, `URL`, `Error`, `bigint`, functions, and symbols pass through unchanged unless you normalize them before or during serialization.
167
171
  *
168
172
  * @typeParam T Input value type.
169
173
  * @param value Value or object graph to serialize.
170
- * @returns A plain JSON-safe structure ready for HTTP response writing.
174
+ * @returns A plain recursively serialized structure whose opaque objects and non-JSON leaf values are preserved unless transformed.
171
175
  *
172
176
  * @example
173
177
  * ```ts
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "output",
10
10
  "transform"
11
11
  ],
12
- "version": "1.0.0-beta.2",
12
+ "version": "1.0.0-beta.4",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -36,8 +36,8 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@fluojs/core": "^1.0.0-beta.1",
40
- "@fluojs/http": "^1.0.0-beta.1"
39
+ "@fluojs/core": "^1.0.0-beta.3",
40
+ "@fluojs/http": "^1.0.0-beta.9"
41
41
  },
42
42
  "devDependencies": {
43
43
  "vitest": "^3.2.4"