@fluojs/serialization 1.0.2 → 1.0.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 +4 -3
- package/README.md +4 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +6 -6
- package/dist/serialize.d.ts +5 -2
- package/dist/serialize.d.ts.map +1 -1
- package/dist/serialize.js +5 -2
- package/dist/serializer-interceptor.d.ts +3 -1
- package/dist/serializer-interceptor.d.ts.map +1 -1
- package/dist/serializer-interceptor.js +3 -1
- package/package.json +3 -3
package/README.ko.md
CHANGED
|
@@ -103,7 +103,7 @@ class UsersController {
|
|
|
103
103
|
|
|
104
104
|
### 순환 참조 처리
|
|
105
105
|
|
|
106
|
-
fluo의 직렬화 엔진은 활성 순환 참조를 자동으로 감지하고 `undefined`로 절단하여 무한 루프와 스택 오버플로를 방지합니다. 이미 직렬화가 끝난 공유 참조는 삭제하지 않고 직렬화된 그래프 안에서 재사용합니다. 예를 들어 두 sibling
|
|
106
|
+
fluo의 직렬화 엔진은 활성 순환 참조를 자동으로 감지하고 `undefined`로 절단하여 무한 루프와 스택 오버플로를 방지합니다. 같은 reference tracker가 class instance, plain object, 배열, mixed object-array graph 전체에서 공유됩니다. 자기 자신을 참조하는 배열과 object-array cycle은 활성 back edge에서 절단되고, 이미 직렬화가 끝난 공유 참조는 삭제하지 않고 직렬화된 그래프 안에서 재사용합니다. 예를 들어 두 sibling 필드 또는 객체 필드와 배열 항목이 같은 원본 객체를 가리키면 두 직렬화 결과도 같은 직렬화 객체를 가리키며, 현재 직렬화 중인 값을 다시 만나는 활성 cycle만 `undefined`로 절단됩니다.
|
|
107
107
|
|
|
108
108
|
### 상속된 데코레이터 계약
|
|
109
109
|
|
|
@@ -124,8 +124,9 @@ Decorated metadata가 없는 class instance도 재귀적으로 순회하므로,
|
|
|
124
124
|
## 공개 API 개요
|
|
125
125
|
|
|
126
126
|
- **데코레이터**: `Expose`, `Exclude`, `Transform`
|
|
127
|
-
- **엔진**: `serialize(value)
|
|
128
|
-
- **HTTP 통합**: `SerializerInterceptor
|
|
127
|
+
- **엔진**: `serialize(value)`는 class instance, 배열, plain object, mixed graph를 재귀적으로 순회하며, 직접 transform하지 않은 opaque built-in 및 non-JSON leaf 값은 보존합니다.
|
|
128
|
+
- **HTTP 통합**: `SerializerInterceptor`는 아직 commit되지 않은 handler 결과를 직렬화하고, response가 이미 commit된 뒤에는 handler 소유 값을 그대로 반환합니다.
|
|
129
|
+
- **타입**: `TransformFunction`은 `Transform(...)`에 전달하는 callback 타입으로 root entrypoint에서 export됩니다.
|
|
129
130
|
|
|
130
131
|
`Expose`는 class와 field에 적용할 수 있습니다. `Exclude`와 `Transform`은 field에 적용합니다.
|
|
131
132
|
|
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ class UsersController {
|
|
|
103
103
|
|
|
104
104
|
### Cycle-safe serialization
|
|
105
105
|
|
|
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.
|
|
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. The same reference tracker is shared across class instances, plain objects, arrays, and mixed object-array graphs. Self-referential arrays and object-array cycles are cut at the active back edge, while completed shared references are reused in the serialized graph rather than dropped: if two sibling fields, or an object field and an array entry, point at the same source object, both serialized fields point at the same serialized object. Only a value that is encountered again while it is already being serialized is cut to `undefined`.
|
|
107
107
|
|
|
108
108
|
### Inherited decorator contracts
|
|
109
109
|
|
|
@@ -124,8 +124,9 @@ Undecorated class instances are still traversed recursively, so decorated nested
|
|
|
124
124
|
## Public API Overview
|
|
125
125
|
|
|
126
126
|
- **Decorators**: `Expose`, `Exclude`, `Transform`
|
|
127
|
-
- **Engine**: `serialize(value)`
|
|
128
|
-
- **HTTP integration**: `SerializerInterceptor`
|
|
127
|
+
- **Engine**: `serialize(value)` recursively walks class instances, arrays, plain objects, and mixed graphs while preserving opaque built-ins and non-JSON leaf values unless you transform them
|
|
128
|
+
- **HTTP integration**: `SerializerInterceptor` serializes uncommitted handler results and returns handler-owned values unchanged after the response has already been committed
|
|
129
|
+
- **Types**: `TransformFunction` is exported from the root entrypoint for callbacks passed to `Transform(...)`
|
|
129
130
|
|
|
130
131
|
`Expose` can be applied to classes and fields. `Exclude` and `Transform` apply to fields.
|
|
131
132
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './decorators/exclude.js';
|
|
2
2
|
export * from './decorators/expose.js';
|
|
3
3
|
export * from './decorators/transform.js';
|
|
4
|
+
export type { TransformFunction } from './metadata.js';
|
|
4
5
|
export * from './serialize.js';
|
|
5
6
|
export * from './serializer-interceptor.js';
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/metadata.d.ts
CHANGED
package/dist/metadata.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,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;AA4DD;;;;;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,CAQ7F;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,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getOwnStandardConstructorMetadataBag } from '@fluojs/core/internal';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Defines the transform function type.
|
|
@@ -14,7 +14,6 @@ import { ensureMetadataSymbol, getOwnStandardConstructorMetadataBag } from '@flu
|
|
|
14
14
|
|
|
15
15
|
const standardSerializationClassMetadataKey = Symbol.for('fluo.standard.serialization.class');
|
|
16
16
|
const standardSerializationFieldMetadataKey = Symbol.for('fluo.standard.serialization.field');
|
|
17
|
-
ensureMetadataSymbol();
|
|
18
17
|
function getStandardMetadataBag(metadata) {
|
|
19
18
|
if (metadata === null || metadata === undefined) {
|
|
20
19
|
throw new Error('Decorator metadata is not available. Ensure your environment supports TC39 decorator metadata (Stage 3).');
|
|
@@ -86,10 +85,11 @@ export function updateFieldSerializationMetadata(metadata, propertyKey, update)
|
|
|
86
85
|
* @returns The get class serialization options result.
|
|
87
86
|
*/
|
|
88
87
|
export function getClassSerializationOptions(constructor) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
88
|
+
const options = {};
|
|
89
|
+
for (const bag of getConstructorMetadataBags(constructor)) {
|
|
90
|
+
Object.assign(options, bag[standardSerializationClassMetadataKey]);
|
|
91
|
+
}
|
|
92
|
+
return options;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
package/dist/serialize.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Serializes class instances
|
|
2
|
+
* Serializes class instances, arrays, plain records, and mixed object graphs
|
|
3
|
+
* into plain response-shaped values.
|
|
3
4
|
*
|
|
4
5
|
* Serialization honors `@Expose()`, `@Exclude()`, and `@Transform()` metadata.
|
|
5
|
-
* Cycles and repeated references are handled without unbounded recursion
|
|
6
|
+
* Cycles and repeated references are handled without unbounded recursion across
|
|
7
|
+
* both object and array boundaries. Active cycles are cut to `undefined`, while
|
|
8
|
+
* completed shared references reuse the previously serialized value.
|
|
6
9
|
* 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.
|
|
7
10
|
*
|
|
8
11
|
* @typeParam T Input value type.
|
package/dist/serialize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAwQA
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAwQA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAOxD"}
|
package/dist/serialize.js
CHANGED
|
@@ -166,10 +166,13 @@ function serializeInternal(value, context) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* Serializes class instances
|
|
169
|
+
* Serializes class instances, arrays, plain records, and mixed object graphs
|
|
170
|
+
* into plain response-shaped values.
|
|
170
171
|
*
|
|
171
172
|
* Serialization honors `@Expose()`, `@Exclude()`, and `@Transform()` metadata.
|
|
172
|
-
* Cycles and repeated references are handled without unbounded recursion
|
|
173
|
+
* Cycles and repeated references are handled without unbounded recursion across
|
|
174
|
+
* both object and array boundaries. Active cycles are cut to `undefined`, while
|
|
175
|
+
* completed shared references reuse the previously serialized value.
|
|
173
176
|
* 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.
|
|
174
177
|
*
|
|
175
178
|
* @typeParam T Input value type.
|
|
@@ -5,7 +5,9 @@ import type { CallHandler, Interceptor, InterceptorContext } from '@fluojs/http'
|
|
|
5
5
|
* @remarks
|
|
6
6
|
* Use this at the controller or route level when handlers return class instances
|
|
7
7
|
* and you want `@Expose()`, `@Exclude()`, and `@Transform()` metadata applied
|
|
8
|
-
* automatically.
|
|
8
|
+
* automatically. If the handler already committed `RequestContext.response`,
|
|
9
|
+
* the interceptor returns the handler-owned value unchanged so streaming and
|
|
10
|
+
* manually written responses keep their response ownership.
|
|
9
11
|
*/
|
|
10
12
|
export declare class SerializerInterceptor implements Interceptor {
|
|
11
13
|
intercept(context: InterceptorContext, next: CallHandler): Promise<unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializer-interceptor.d.ts","sourceRoot":"","sources":["../src/serializer-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIjF
|
|
1
|
+
{"version":3,"file":"serializer-interceptor.d.ts","sourceRoot":"","sources":["../src/serializer-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIjF;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,YAAW,WAAW;IACjD,SAAS,CAAC,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;CASlF"}
|
|
@@ -6,7 +6,9 @@ import { serialize } from './serialize.js';
|
|
|
6
6
|
* @remarks
|
|
7
7
|
* Use this at the controller or route level when handlers return class instances
|
|
8
8
|
* and you want `@Expose()`, `@Exclude()`, and `@Transform()` metadata applied
|
|
9
|
-
* automatically.
|
|
9
|
+
* automatically. If the handler already committed `RequestContext.response`,
|
|
10
|
+
* the interceptor returns the handler-owned value unchanged so streaming and
|
|
11
|
+
* manually written responses keep their response ownership.
|
|
10
12
|
*/
|
|
11
13
|
export class SerializerInterceptor {
|
|
12
14
|
async intercept(context, next) {
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"output",
|
|
10
10
|
"transform"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.0.
|
|
12
|
+
"version": "1.0.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.
|
|
40
|
-
"@fluojs/http": "^1.
|
|
39
|
+
"@fluojs/core": "^1.0.3",
|
|
40
|
+
"@fluojs/http": "^1.1.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"vitest": "^3.2.4"
|