@fluojs/serialization 1.0.0-beta.3 → 1.0.0-beta.5

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
@@ -4,18 +4,12 @@
4
4
 
5
5
  fluo를 위한 클래스 기반 응답 직렬화 및 데코레이터 인지형 재귀 출력 가공 엔진입니다.
6
6
 
7
- `@fluojs/serialization`은 애플리케이션의 **출력 경계(Output Boundary)**를 담당합니다. 내부 클래스 인스턴스나 복잡한 객체 그래프를 데코레이터 규칙이 반영된 일반 응답 형태로 변환하는 선언적인 방법을 제공합니다. 이를 통해 API 응답에 의도한 데이터만 노출되도록 보장합니다.
8
-
9
7
  ## 목차
10
8
 
11
9
  - [설치](#설치)
12
10
  - [사용 시점](#사용-시점)
13
11
  - [빠른 시작](#빠른-시작)
14
12
  - [주요 패턴](#주요-패턴)
15
- - [민감한 데이터 제외](#민감한-데이터-제외)
16
- - [값 변환 (Transforming)](#값-변환-transforming)
17
- - [순환 참조 처리](#순환-참조-처리)
18
- - [HTTP 인터셉터와 함께 사용](#http-인터셉터와-함께-사용)
19
13
  - [공개 API 개요](#공개-api-개요)
20
14
  - [관련 패키지](#관련-패키지)
21
15
  - [예제 소스](#예제-소스)
@@ -28,94 +22,70 @@ pnpm add @fluojs/serialization
28
22
 
29
23
  ## 사용 시점
30
24
 
31
- - JSON 응답에 포함될 클래스 속성을 정밀하게 제어하고 싶을 때.
32
- - 비밀번호나 내부 ID와 같은 민감한 필드를 출력에서 숨겨야 때.
33
- - 직렬화 과정에서 속성 값을 변환해야 (예: 날짜 형식 지정, 내부 열거형 매핑).
34
- - 무한 루프를 유발할 있는 복잡한 객체 그래프를 안전하게 직렬화해야 할 때.
25
+ - output DTO가 제어된 일부 필드만 노출해야
26
+ - password hash나 내부 identifier 같은 민감한 값이 response boundary를 벗어나면 안 될 때
27
+ - response data가 serialization lightweight synchronous transform을 거쳐야
28
+ - HTTP interceptor가 같은 serialization rule을 자동으로 적용하게 하고 싶을
35
29
 
36
30
  ## 빠른 시작
37
31
 
38
- DTO나 엔티티 클래스에 데코레이터를 적용하고 `serialize` 함수 또는 `SerializerInterceptor`를 사용합니다.
39
-
40
- ```typescript
32
+ ```ts
41
33
  import { Expose, Exclude, Transform, serialize } from '@fluojs/serialization';
42
34
 
43
35
  class UserEntity {
44
36
  @Expose()
45
- id: string = '';
37
+ id = '';
46
38
 
47
39
  @Expose()
48
- @Transform((val) => val.toUpperCase())
49
- username: string = '';
40
+ @Transform((value) => value.toUpperCase())
41
+ username = '';
50
42
 
51
43
  @Exclude()
52
- passwordHash: string = '';
53
-
54
- constructor(partial: Partial<UserEntity>) {
55
- Object.assign(this, partial);
56
- }
44
+ passwordHash = '';
57
45
  }
58
46
 
59
- const user = new UserEntity({ id: '1', username: 'fluo', passwordHash: 'secret' });
60
- const result = serialize(user);
47
+ const user = Object.assign(new UserEntity(), {
48
+ id: '1',
49
+ username: 'fluo',
50
+ passwordHash: 'secret',
51
+ });
61
52
 
62
- console.log(result);
63
- // 출력: { id: "1", username: "FLUO" }
64
- // passwordHash는 제외됩니다.
53
+ console.log(serialize(user));
54
+ // { id: '1', username: 'FLUO' }
65
55
  ```
66
56
 
67
57
  ## 주요 패턴
68
58
 
69
- ### 민감한 데이터 제외
70
-
71
- `@Exclude()`를 사용하여 특정 속성이 출력에 절대 나타나지 않도록 합니다. 클래스 레벨에서 `@Expose({ excludeExtraneous: true })`를 사용하면 명시적으로 허용된 필드만 포함하는 "화이트리스트" 전략을 구현할 수 있습니다.
59
+ ### 노출 전용 출력 DTO
72
60
 
73
- ```typescript
74
- import { Expose, Exclude } from '@fluojs/serialization';
61
+ ```ts
62
+ import { Expose } from '@fluojs/serialization';
75
63
 
76
64
  @Expose({ excludeExtraneous: true })
77
65
  class SecureDto {
78
66
  @Expose()
79
- publicData: string = 'visible';
67
+ publicData = 'visible';
80
68
 
81
- internalData: string = 'hidden'; // excludeExtraneous가 true이므로 숨겨짐
69
+ internalData = 'hidden';
82
70
  }
83
71
  ```
84
72
 
85
- ### 값 변환 (Transforming)
73
+ ### 값 변환
86
74
 
87
- `@Transform()`을 사용하여 직렬화 중에 값을 수정합니다. 변환 함수는 현재 값을 인자로 받아 새로운 값을 반환해야 합니다.
88
-
89
- ```typescript
75
+ ```ts
90
76
  import { Transform } from '@fluojs/serialization';
91
77
 
92
78
  class ProductDto {
93
79
  @Transform((price) => `$${price.toFixed(2)}`)
94
- price: number = 0;
80
+ price = 0;
95
81
  }
96
82
  ```
97
83
 
98
- ### 순환 참조 처리
99
-
100
- fluo의 직렬화 엔진은 순환 참조를 자동으로 감지하고, 반복되는 참조에 대해 `undefined`를 반환하여 절단함으로써 무한 루프와 스택 오버플로를 방지하고 일반 응답 형태를 유지합니다.
101
-
102
- ### 상속된 데코레이터 계약
103
-
104
- 기반 클래스에 선언한 직렬화 메타데이터는 파생 DTO에도 상속됩니다. 공통 필드에 적용한 `@Expose()`, `@Exclude()`, `@Transform()` 규칙은 서브클래스 인스턴스를 직렬화할 때도 그대로 반영됩니다.
105
-
106
- ### 일반 객체 안전성
107
-
108
- `serialize()`는 일반 객체와 null-prototype 레코드를 데코레이터가 붙은 클래스 인스턴스로 오인하지 않습니다. 사용자 정의 `constructor` 필드나 안전하지 않은 `constructor` 값을 가진 객체도 예외 없이 안전하게 순회합니다.
109
-
110
- ### 비JSON leaf 값
111
-
112
- `serialize()`는 데코레이터 메타데이터를 적용하고 배열/일반 객체를 재귀적으로 순회하지만, 모든 leaf 값을 엄격한 JSON 타입으로 강제 변환하지는 않습니다. `Date`, `bigint`, 함수, `symbol` 같은 값은 `@Transform(...)`이나 최종 HTTP 응답 작성 전에 직접 정규화하지 않으면 그대로 통과할 수 있습니다.
84
+ 같은 필드가 base class와 derived class 모두에서 decorate되면 transform은 base에서 derived 순서로 실행됩니다.
113
85
 
114
86
  ### HTTP 인터셉터와 함께 사용
115
87
 
116
- fluo HTTP 애플리케이션에서는 `SerializerInterceptor`를 사용하여 컨트롤러에서 나가는 모든 응답을 자동으로 직렬화할 수 있습니다.
117
-
118
- ```typescript
88
+ ```ts
119
89
  import { Controller, Get, UseInterceptors } from '@fluojs/http';
120
90
  import { SerializerInterceptor } from '@fluojs/serialization';
121
91
 
@@ -124,30 +94,45 @@ import { SerializerInterceptor } from '@fluojs/serialization';
124
94
  class UsersController {
125
95
  @Get('/')
126
96
  findAll() {
127
- return [new UserEntity({ ... }), new UserEntity({ ... })];
97
+ return [new UserEntity()];
128
98
  }
129
99
  }
130
100
  ```
131
101
 
132
102
  `SerializerInterceptor`는 일반 HTTP 응답 writer가 아직 소유한 값만 직렬화합니다. 핸들러나 응답 헬퍼가 SSE 스트림처럼 `RequestContext.response`를 직접 커밋한 경우, 인터셉터는 해당 핸들러 소유 값을 그대로 반환하여 request pipeline의 응답 소유권을 보존합니다.
133
103
 
104
+ ### 순환 참조 처리
105
+
106
+ fluo의 직렬화 엔진은 활성 순환 참조를 자동으로 감지하고 `undefined`로 절단하여 무한 루프와 스택 오버플로를 방지합니다. 이미 직렬화가 끝난 공유 참조는 삭제하지 않고 직렬화된 그래프 안에서 재사용합니다. 예를 들어 두 sibling 필드가 같은 원본 객체를 가리키면 두 직렬화 결과도 같은 직렬화 객체를 가리키며, 현재 직렬화 중인 객체를 다시 만나는 활성 cycle만 `undefined`로 절단됩니다.
107
+
108
+ ### 상속된 데코레이터 계약
109
+
110
+ 기반 클래스에 선언한 직렬화 메타데이터는 파생 DTO에도 상속됩니다. 공통 필드에 적용한 `@Expose()`, `@Exclude()`, `@Transform()` 규칙은 서브클래스 인스턴스를 직렬화할 때도 그대로 반영됩니다.
111
+
112
+ Decorated metadata가 없는 class instance도 재귀적으로 순회하므로, parent object에 serialization metadata가 없어도 decorated nested descendant는 반영됩니다.
113
+
114
+ ### 일반 객체 안전성
115
+
116
+ `serialize()`는 일반 객체와 null-prototype 레코드를 데코레이터가 붙은 클래스 인스턴스로 오인하지 않습니다. Enumerable symbol key도 직렬화하며, own `__proto__`, `constructor`, `prototype` key는 prototype mutation이 아니라 data로 취급합니다. 사용자 정의 `constructor` 필드나 안전하지 않은 `constructor` 값을 가진 객체도 예외 없이 안전하게 순회합니다.
117
+
118
+ ### 비JSON leaf 값
119
+
120
+ `serialize()`는 데코레이터 메타데이터를 적용하고 배열/일반 객체를 재귀적으로 순회하지만, 모든 leaf 값을 엄격한 JSON 타입으로 강제 변환하지는 않습니다. `Date`, `Map`, `Set`, `URL`, `URLSearchParams`, `RegExp`, `Error`, `ArrayBuffer`, typed array, `WeakMap`, `WeakSet`, `Promise` 같은 opaque built-in은 DTO 같은 클래스 인스턴스로 펼치지 않고 그대로 통과합니다. `bigint`, 함수, `symbol` 같은 값도 `@Transform(...)`이나 최종 HTTP 응답 작성 전에 직접 정규화하지 않으면 그대로 통과할 수 있습니다.
121
+
134
122
  ## 공개 API 개요
135
123
 
136
- ### 데코레이터
137
- - `@Expose(options?)`: 포함할 속성을 표시합니다. 클래스에 사용하여 기본 동작을 설정할 수도 있습니다.
138
- - `@Exclude()`: 직렬화 중에 무시할 속성을 표시합니다.
139
- - `@Transform(fn)`: 속성에 대한 변환 함수를 등록합니다.
124
+ - **데코레이터**: `Expose`, `Exclude`, `Transform`
125
+ - **엔진**: `serialize(value)`
126
+ - **HTTP 통합**: `SerializerInterceptor`
140
127
 
141
- ### 엔진
142
- - `serialize(value)`: 객체/배열을 재귀적으로 순회하며 직렬화 규칙과 데코레이터를 적용합니다.
143
- - `SerializerInterceptor`: 핸들러의 반환 값에 대해 `serialize`를 호출하는 fluo HTTP 인터셉터입니다.
128
+ `Expose`는 class와 field에 적용할 수 있습니다. `Exclude`와 `Transform`은 field에 적용합니다.
144
129
 
145
130
  ## 관련 패키지
146
131
 
147
- - `@fluojs/http`: `SerializerInterceptor`를 통한 자동 출력 가공을 지원합니다.
148
- - `@fluojs/validation`: **입력** 측면(일반 객체를 클래스 인스턴스로 변환)담당하는 대응 패키지입니다.
132
+ - `@fluojs/http`: HTTP handler에 `SerializerInterceptor`를 적용합니다.
133
+ - `@fluojs/validation`: input-side DTO materialization과 validation담당합니다.
149
134
 
150
135
  ## 예제 소스
151
136
 
152
- - `packages/serialization/src/serialize.test.ts`: 다양한 직렬화 시나리오에 대한 상세 예제.
153
- - `packages/serialization/src/serializer-interceptor.test.ts`: HTTP 컨텍스트 내에서의 사용법.
137
+ - `packages/serialization/src/serialize.test.ts`
138
+ - `packages/serialization/src/serializer-interceptor.test.ts`
package/README.md CHANGED
@@ -81,6 +81,8 @@ class ProductDto {
81
81
  }
82
82
  ```
83
83
 
84
+ When the same field is decorated in a base class and a derived class, transforms run in declaration order from base to derived.
85
+
84
86
  ### HTTP response shaping with an interceptor
85
87
 
86
88
  ```ts
@@ -101,19 +103,21 @@ class UsersController {
101
103
 
102
104
  ### Cycle-safe serialization
103
105
 
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.
106
+ 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
107
 
106
108
  ### Inherited decorator contracts
107
109
 
108
110
  Serialization metadata declared on a base class is inherited by derived DTOs. `@Expose()`, `@Exclude()`, and `@Transform()` rules applied to shared base fields still take effect when you serialize subclass instances.
109
111
 
112
+ Undecorated class instances are still traversed recursively, so decorated nested descendants are respected even when the parent object has no serialization metadata.
113
+
110
114
  ### Plain-object safety
111
115
 
112
- `serialize()` treats plain objects and null-prototype records as data containers, not decorated class instances. Objects with custom or unsafe `constructor` fields are walked safely without throwing.
116
+ `serialize()` treats plain objects and null-prototype records as data containers, not decorated class instances. Enumerable symbol keys are serialized, own `__proto__`, `constructor`, and `prototype` keys are treated as data rather than prototype mutations, and objects with custom or unsafe `constructor` fields are walked safely without throwing.
113
117
 
114
118
  ### Non-JSON leaf values
115
119
 
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.
120
+ `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
121
 
118
122
  ## Public API Overview
119
123
 
@@ -121,6 +125,8 @@ Serialization metadata declared on a base class is inherited by derived DTOs. `@
121
125
  - **Engine**: `serialize(value)`
122
126
  - **HTTP integration**: `SerializerInterceptor`
123
127
 
128
+ `Expose` can be applied to classes and fields. `Exclude` and `Transform` apply to fields.
129
+
124
130
  ## Related Packages
125
131
 
126
132
  - `@fluojs/http`: applies `SerializerInterceptor` to HTTP handlers
@@ -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;AA6DD,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;AA8DD;;;;;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,11 +1,24 @@
1
- import { getOwnStandardConstructorMetadataBag, metadataSymbol } from '@fluojs/core/internal';
1
+ import { ensureMetadataSymbol, getOwnStandardConstructorMetadataBag } 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');
17
+ ensureMetadataSymbol();
4
18
  function getStandardMetadataBag(metadata) {
5
19
  if (metadata === null || metadata === undefined) {
6
20
  throw new Error('Decorator metadata is not available. Ensure your environment supports TC39 decorator metadata (Stage 3).');
7
21
  }
8
- void metadataSymbol;
9
22
  return metadata;
10
23
  }
11
24
  function getFieldMetadataMap(metadata) {
@@ -43,19 +56,48 @@ function getConstructorMetadataBags(constructor) {
43
56
  }
44
57
  return bags;
45
58
  }
59
+
60
+ /**
61
+ * Update class serialization options.
62
+ *
63
+ * @param metadata The metadata.
64
+ * @param partial The partial.
65
+ */
46
66
  export function updateClassSerializationOptions(metadata, partial) {
47
67
  Object.assign(getClassMetadataObject(metadata), partial);
48
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
+ */
49
77
  export function updateFieldSerializationMetadata(metadata, propertyKey, update) {
50
78
  const map = getFieldMetadataMap(metadata);
51
79
  map.set(propertyKey, update(map.get(propertyKey)));
52
80
  }
81
+
82
+ /**
83
+ * Get class serialization options.
84
+ *
85
+ * @param constructor The constructor.
86
+ * @returns The get class serialization options result.
87
+ */
53
88
  export function getClassSerializationOptions(constructor) {
54
89
  return getConstructorMetadataBags(constructor).reduce((options, bag) => ({
55
90
  ...options,
56
91
  ...bag[standardSerializationClassMetadataKey]
57
92
  }), {});
58
93
  }
94
+
95
+ /**
96
+ * Get field serialization metadata.
97
+ *
98
+ * @param constructor The constructor.
99
+ * @returns The get field serialization metadata result.
100
+ */
59
101
  export function getFieldSerializationMetadata(constructor) {
60
102
  const merged = new Map();
61
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.3",
12
+ "version": "1.0.0-beta.5",
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.2",
40
- "@fluojs/http": "^1.0.0-beta.2"
39
+ "@fluojs/core": "^1.0.0-beta.4",
40
+ "@fluojs/http": "^1.0.0-beta.10"
41
41
  },
42
42
  "devDependencies": {
43
43
  "vitest": "^3.2.4"