Skip to content

Commit f129b21

Browse files
authored
docs(javascript): add javascript docs (#3561)
## Why? ## What does this PR do? add javascript docs ## Related issues ## AI Contribution Checklist - [ ] Substantial AI assistance was used in this PR: `yes` / `no` - [ ] If `yes`, I included a completed [AI Contribution Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs) in this PR description and the required `AI Usage Disclosure`. - [ ] If `yes`, my PR description includes the required `ai_review` summary and screenshot evidence of the final clean AI review results from both fresh reviewers on the current PR diff or current HEAD after the latest code changes. ## Does this PR introduce any user-facing change? - [ ] Does this PR introduce any public API change? - [ ] Does this PR introduce any binary protocol compatibility change? ## Benchmark
1 parent 90a45dd commit f129b21

9 files changed

Lines changed: 1278 additions & 4 deletions

File tree

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
---
2+
title: Basic Serialization
3+
sidebar_position: 1
4+
id: basic_serialization
5+
license: |
6+
Licensed to the Apache Software Foundation (ASF) under one or more
7+
contributor license agreements. See the NOTICE file distributed with
8+
this work for additional information regarding copyright ownership.
9+
The ASF licenses this file to You under the Apache License, Version 2.0
10+
(the "License"); you may not use this file except in compliance with
11+
the License. You may obtain a copy of the License at
12+
13+
http://www.apache.org/licenses/LICENSE-2.0
14+
15+
Unless required by applicable law or agreed to in writing, software
16+
distributed under the License is distributed on an "AS IS" BASIS,
17+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
See the License for the specific language governing permissions and
19+
limitations under the License.
20+
---
21+
22+
This guide covers the core serialization flow in Apache Fory JavaScript.
23+
24+
## Create and Reuse a `Fory` Instance
25+
26+
```ts
27+
import Fory from "@apache-fory/core";
28+
29+
const fory = new Fory();
30+
```
31+
32+
Create one instance, register your schemas, and reuse it. A `Fory` instance caches generated serializers and type metadata, so recreating it for every request adds unnecessary overhead.
33+
34+
## Define a Schema with `Type.struct`
35+
36+
The most common path is to define a schema and register it.
37+
38+
```ts
39+
import Fory, { Type } from "@apache-fory/core";
40+
41+
const accountType = Type.struct(
42+
{ typeName: "example.account" },
43+
{
44+
id: Type.int64(),
45+
owner: Type.string(),
46+
active: Type.bool(),
47+
nickname: Type.string().setNullable(true),
48+
},
49+
);
50+
51+
const fory = new Fory();
52+
const { serialize, deserialize } = fory.register(accountType);
53+
```
54+
55+
## Serialize and Deserialize
56+
57+
```ts
58+
const bytes = serialize({
59+
id: 42n,
60+
owner: "Alice",
61+
active: true,
62+
nickname: null,
63+
});
64+
65+
const value = deserialize(bytes);
66+
console.log(value);
67+
// { id: 42n, owner: 'Alice', active: true, nickname: null }
68+
```
69+
70+
The returned `bytes` value is a `Uint8Array`/platform buffer and can be sent over the network or written to storage.
71+
72+
## Root-Level Dynamic Serialization
73+
74+
`Fory` can also serialize dynamic root values without first binding a schema-specific serializer.
75+
76+
```ts
77+
const fory = new Fory();
78+
79+
const bytes = fory.serialize(
80+
new Map([
81+
["name", "Alice"],
82+
["age", 30],
83+
]),
84+
);
85+
86+
const value = fory.deserialize(bytes);
87+
```
88+
89+
This is convenient for dynamic payloads, but explicit schemas are usually better for stable interfaces and cross-language contracts.
90+
91+
## Primitive Values
92+
93+
```ts
94+
const fory = new Fory();
95+
96+
fory.deserialize(fory.serialize(true));
97+
// true
98+
99+
fory.deserialize(fory.serialize("hello"));
100+
// 'hello'
101+
102+
fory.deserialize(fory.serialize(123));
103+
// 123
104+
105+
fory.deserialize(fory.serialize(123n));
106+
// 123n
107+
108+
fory.deserialize(fory.serialize(new Date("2021-10-20T09:13:00Z")));
109+
// Date
110+
```
111+
112+
### Number and `bigint` behavior
113+
114+
JavaScript has both `number` and `bigint`, but xlang distinguishes between 32-bit, 64-bit, floating-point, and tagged integer representations. For any cross-language or long-lived contract, prefer explicit field types in schemas instead of depending on dynamic root-type inference.
115+
116+
- use `Type.int32()` for 32-bit integers
117+
- use `Type.int64()` for 64-bit integers and pass `bigint`
118+
- use `Type.float32()` or `Type.float64()` for floating-point values
119+
120+
Dynamic root serialization is convenient, but the exact heuristic for whether a value comes back as `number` or `bigint` should not be treated as a stable API contract.
121+
122+
## Arrays, Maps, and Sets
123+
124+
```ts
125+
const inventoryType = Type.struct("example.inventory", {
126+
tags: Type.array(Type.string()),
127+
counts: Type.map(Type.string(), Type.int32()),
128+
labels: Type.set(Type.string()),
129+
});
130+
131+
const fory = new Fory({ ref: true });
132+
const { serialize, deserialize } = fory.register(inventoryType);
133+
134+
const bytes = serialize({
135+
tags: ["hot", "new"],
136+
counts: new Map([
137+
["apple", 3],
138+
["pear", 8],
139+
]),
140+
labels: new Set(["featured", "seasonal"]),
141+
});
142+
143+
const value = deserialize(bytes);
144+
```
145+
146+
## Nested Structs
147+
148+
```ts
149+
const addressType = Type.struct("example.address", {
150+
city: Type.string(),
151+
country: Type.string(),
152+
});
153+
154+
const userType = Type.struct("example.user", {
155+
name: Type.string(),
156+
address: Type.struct("example.address", {
157+
city: Type.string(),
158+
country: Type.string(),
159+
}),
160+
});
161+
162+
const fory = new Fory();
163+
const { serialize, deserialize } = fory.register(userType);
164+
165+
const bytes = serialize({
166+
name: "Alice",
167+
address: { city: "Hangzhou", country: "CN" },
168+
});
169+
170+
const user = deserialize(bytes);
171+
```
172+
173+
If a nested value can be missing, mark it nullable:
174+
175+
```ts
176+
const wrapperType = Type.struct("example.wrapper", {
177+
child: Type.struct("example.child", {
178+
name: Type.string(),
179+
}).setNullable(true),
180+
});
181+
```
182+
183+
## Decorator-Based Registration
184+
185+
TypeScript decorators are also supported.
186+
187+
```ts
188+
import Fory, { Type } from "@apache-fory/core";
189+
190+
@Type.struct("example.user")
191+
class User {
192+
@Type.int64()
193+
id!: bigint;
194+
195+
@Type.string()
196+
name!: string;
197+
}
198+
199+
const fory = new Fory();
200+
const { serialize, deserialize } = fory.register(User);
201+
202+
const user = new User();
203+
user.id = 1n;
204+
user.name = "Alice";
205+
206+
const copy = deserialize(serialize(user));
207+
console.log(copy instanceof User); // true
208+
```
209+
210+
## Nullability
211+
212+
Field nullability is explicit in schema-based structs.
213+
214+
```ts
215+
const nullableType = Type.struct("example.optional_user", {
216+
name: Type.string(),
217+
email: Type.string().setNullable(true),
218+
});
219+
```
220+
221+
If a field is not marked nullable and you try to write `null`, serialization throws.
222+
223+
## Debugging Generated Code
224+
225+
You can inspect generated serializer code with `hooks.afterCodeGenerated`.
226+
227+
```ts
228+
const fory = new Fory({
229+
hooks: {
230+
afterCodeGenerated(code) {
231+
console.log(code);
232+
return code;
233+
},
234+
},
235+
});
236+
```
237+
238+
This is useful when debugging schema behavior, field ordering, or generated fast paths.
239+
240+
## Related Topics
241+
242+
- [Type Registration](type-registration.md)
243+
- [Supported Types](supported-types.md)
244+
- [References](references.md)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
title: Cross-Language Serialization
3+
sidebar_position: 80
4+
id: cross_language
5+
license: |
6+
Licensed to the Apache Software Foundation (ASF) under one or more
7+
contributor license agreements. See the NOTICE file distributed with
8+
this work for additional information regarding copyright ownership.
9+
The ASF licenses this file to You under the Apache License, Version 2.0
10+
(the "License"); you may not use this file except in compliance with
11+
the License. You may obtain a copy of the License at
12+
13+
http://www.apache.org/licenses/LICENSE-2.0
14+
15+
Unless required by applicable law or agreed to in writing, software
16+
distributed under the License is distributed on an "AS IS" BASIS,
17+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
See the License for the specific language governing permissions and
19+
limitations under the License.
20+
---
21+
22+
Apache Fory JavaScript always uses the xlang wire format. Interoperability depends on matching schemas and compatible type mappings rather than on enabling a separate mode switch.
23+
24+
Current limits to keep in mind:
25+
26+
- JavaScript deserializes xlang payloads only
27+
- out-of-band mode is not currently supported in the JavaScript runtime
28+
29+
## Interop rules
30+
31+
For a payload to round-trip between JavaScript and another runtime:
32+
33+
1. **Register the same logical type identity** on both sides.
34+
- same numeric type ID, or
35+
- same namespace + type name
36+
2. **Use compatible field types** according to the [type mapping spec](../../specification/xlang_type_mapping.md).
37+
3. **Match nullability and reference-tracking expectations** where object graphs require them.
38+
4. **Use compatible mode** when independent schema evolution is expected.
39+
40+
## Interoperability workflow
41+
42+
When wiring JavaScript to another runtime in production code:
43+
44+
1. define the JavaScript schema with the same type identity used by the other runtime
45+
2. register the same type name or type ID in every participating runtime
46+
3. keep field types, nullability, enum layout, and schema-evolution settings aligned
47+
4. validate the end-to-end payload before relying on it in a shared contract
48+
49+
Example JavaScript side:
50+
51+
```ts
52+
import Fory, { Type } from "@apache-fory/core";
53+
54+
const messageType = Type.struct(
55+
{ typeName: "example.message" },
56+
{
57+
id: Type.int64(),
58+
content: Type.string(),
59+
},
60+
);
61+
62+
const fory = new Fory();
63+
const { serialize } = fory.register(messageType);
64+
65+
const bytes = serialize({
66+
id: 1n,
67+
content: "hello from JavaScript",
68+
});
69+
```
70+
71+
On the receiving side, register the same `example.message` type identity and compatible field types using that runtime's public API and guide:
72+
73+
- [Go guide](../go/index.md)
74+
- [Python guide](../python/index.md)
75+
- [Java guide](../java/index.md)
76+
- [Rust guide](../rust/index.md)
77+
78+
## Field naming and ordering
79+
80+
Cross-language matching depends on consistent field identity. When multiple runtimes model the same struct, use a naming scheme that maps cleanly across languages.
81+
82+
Practical guidance:
83+
84+
- prefer stable snake_case or clearly corresponding names across runtimes
85+
- avoid accidental renames without updating every participating runtime
86+
- use compatible mode when fields may be added or removed over time
87+
88+
## Numeric mapping guidance
89+
90+
JavaScript needs extra care because `number` is IEEE 754 double precision.
91+
92+
- use `Type.int64()` with `bigint` for true 64-bit integers
93+
- use `Type.float32()` or `Type.float64()` for floating-point fields
94+
- avoid assuming that a dynamic JavaScript `number` maps cleanly to every integer width in another language
95+
96+
## Time mapping guidance
97+
98+
- `Type.timestamp()` maps to a point in time and deserializes as `Date`
99+
- `Type.duration()` should be treated as a duration value, but JavaScript currently exposes typed duration fields as numeric time values rather than a dedicated duration class
100+
- `Type.date()` corresponds to a date-without-timezone type in the specification and deserializes as `Date`
101+
102+
## Polymorphism and `Type.any()`
103+
104+
Use `Type.any()` only when you genuinely need runtime-dispatched values.
105+
106+
```ts
107+
const wrapperType = Type.struct(
108+
{ typeId: 3001 },
109+
{
110+
payload: Type.any(),
111+
},
112+
);
113+
```
114+
115+
This works for polymorphic values, but explicit field schemas are easier to keep aligned across languages.
116+
117+
## Enums
118+
119+
Enums must also be registered consistently across languages.
120+
121+
```ts
122+
const Color = { Red: 1, Green: 2, Blue: 3 };
123+
const fory = new Fory();
124+
fory.register(Type.enum({ typeId: 210 }, Color));
125+
```
126+
127+
Use the same type ID or type name in Java, Python, Go, and other runtimes.
128+
129+
## Limits and safety
130+
131+
Deserialization guardrails such as `maxDepth`, `maxBinarySize`, and `maxCollectionSize` are local runtime protections. They do not affect the wire format, but they can reject payloads that exceed local policy.
132+
133+
## Related Topics
134+
135+
- [Supported Types](supported-types.md)
136+
- [Schema Evolution](schema-evolution.md)
137+
- [Xlang Serialization Specification](../../specification/xlang_serialization_spec.md)

0 commit comments

Comments
 (0)