Skip to content

Commit a30ca48

Browse files
committed
docs(dart): document web int64 limits
1 parent 4717017 commit a30ca48

2 files changed

Lines changed: 240 additions & 0 deletions

File tree

docs/guide/dart/troubleshooting.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,48 @@ Checklist:
7878
4. `Timestamp` / `LocalDate` instead of raw `DateTime` for date/time fields.
7979
5. `compatible: true` on **both** sides if using schema evolution.
8080

81+
## Int64 or Uint64 values fail on web
82+
83+
On Dart VM builds, Dart `int` can represent signed 64-bit values. On Dart web
84+
builds, Dart `int` values are backed by JavaScript numbers and are only precise
85+
inside the JS-safe integer range:
86+
87+
```text
88+
-9007199254740991 <= value <= 9007199254740991
89+
```
90+
91+
If a generated serializer writes an `int64` field declared as Dart `int`,
92+
web builds reject values outside that range instead of silently writing
93+
corrupted bytes. To exchange full signed 64-bit values on web, declare the
94+
field as Fory's `Int64` wrapper:
95+
96+
```dart
97+
@ForyStruct()
98+
class LedgerEntry {
99+
LedgerEntry();
100+
101+
Int64 sequence = Int64(0); // full signed 64-bit range on VM and web
102+
}
103+
```
104+
105+
For unsigned 64-bit values, prefer `Uint64` rather than Dart `int`. Dart `int`
106+
cannot represent the full `uint64` range on either VM or web:
107+
108+
```dart
109+
@ForyStruct()
110+
class FileBlock {
111+
FileBlock();
112+
113+
Uint64 offset = Uint64(0); // full unsigned 64-bit range
114+
}
115+
```
116+
117+
`@Int64Type` changes the wire encoding for a Dart `int` field, but it does not
118+
remove the web integer precision limit. Use `Int64` for full-range signed
119+
values and `Uint64` for full-range unsigned values. See
120+
[Web Platform Support](web-platform-support.md) for the full browser support
121+
matrix and migration guidance.
122+
81123
## Running Tests Locally
82124

83125
Main Dart package:
@@ -101,3 +143,4 @@ dart test
101143
- [Cross-Language](cross-language.md)
102144
- [Code Generation](code-generation.md)
103145
- [Custom Serializers](custom-serializers.md)
146+
- [Web Platform Support](web-platform-support.md)

docs/guide/dart/web-platform-support.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,200 @@ license: |
1818
See the License for the specific language governing permissions and
1919
limitations under the License.
2020
---
21+
22+
Fory Dart supports browser and Flutter web builds through generated serializers
23+
and platform-specific runtime implementations. The public API is the same as VM
24+
builds, but web builds have stricter integer precision rules because Dart `int`
25+
is represented by JavaScript numbers.
26+
27+
## Supported Web Targets
28+
29+
The Dart runtime supports:
30+
31+
- Dart applications compiled to JavaScript for browsers.
32+
- Flutter web applications.
33+
- Generated `@ForyStruct` serializers and manually registered serializers.
34+
- Cross-language (`xlang`) payloads produced by other Fory runtimes.
35+
36+
The Dart runtime does not support Fory native-mode payloads. If a peer runtime
37+
is Java, Go, C++, Rust, Python, C#, JavaScript, or another Fory implementation,
38+
configure that peer to write xlang-compatible payloads before sending data to
39+
Dart.
40+
41+
## Code Generation Is Required
42+
43+
Web and Flutter builds cannot rely on `dart:mirrors`. Register generated
44+
serializers explicitly:
45+
46+
```dart
47+
import 'package:fory/fory.dart';
48+
49+
part 'account.fory.dart';
50+
51+
@ForyStruct()
52+
class Account {
53+
Account();
54+
55+
String name = '';
56+
Int64 sequence = Int64(0);
57+
}
58+
59+
void main() {
60+
final fory = Fory();
61+
AccountFory.register(
62+
fory,
63+
Account,
64+
namespace: 'example',
65+
typeName: 'Account',
66+
);
67+
68+
final bytes = fory.serialize(Account()..name = 'web');
69+
final account = fory.deserialize<Account>(bytes);
70+
print(account.name);
71+
}
72+
```
73+
74+
Generate the companion file before building or testing:
75+
76+
```bash
77+
cd dart/packages/fory
78+
dart run build_runner build --delete-conflicting-outputs
79+
```
80+
81+
The registration call is the same on VM and web. Do not call private generated
82+
helpers directly.
83+
84+
## 64-Bit Integer Rules
85+
86+
Dart VM `int` values are signed 64-bit values. Dart web `int` values are backed
87+
by JavaScript numbers and are precise only in the JS-safe integer range:
88+
89+
```text
90+
-9007199254740991 <= value <= 9007199254740991
91+
```
92+
93+
Use this rule when choosing field types:
94+
95+
| Logical value | Recommended Dart field type on web | Notes |
96+
| ---------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- |
97+
| Signed 64-bit value within JS-safe range | `int` | Works with default `int64` mapping and `@Int64Type` encodings. |
98+
| Full signed 64-bit range | `Int64` | Preserves values outside the JS-safe range. |
99+
| Unsigned 64-bit value | `Uint64` | Required for values that do not fit in signed or JS-safe Dart `int`. |
100+
| 8/16/32-bit integer | `Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32` or annotations | Use wrappers or numeric annotations to match peer runtimes exactly. |
101+
102+
`@Int64Type` controls the wire encoding of a Dart `int` field:
103+
104+
```dart
105+
@ForyStruct()
106+
class SafeCounter {
107+
SafeCounter();
108+
109+
@Int64Type(encoding: LongEncoding.tagged)
110+
int count = 0; // keep web values inside the JS-safe range
111+
}
112+
```
113+
114+
It does not make Dart `int` capable of storing every 64-bit value on web. For
115+
full-range signed values, use `Int64`:
116+
117+
```dart
118+
@ForyStruct()
119+
class FullRangeCounter {
120+
FullRangeCounter();
121+
122+
Int64 count = Int64(0);
123+
}
124+
```
125+
126+
For unsigned values, use `Uint64`:
127+
128+
```dart
129+
@ForyStruct()
130+
class StorageExtent {
131+
StorageExtent();
132+
133+
Uint64 byteOffset = Uint64(0);
134+
}
135+
```
136+
137+
## Custom Serializers
138+
139+
Custom serializers can use the same `Buffer`, `WriteContext`, and `ReadContext`
140+
APIs on VM and web. For 64-bit values:
141+
142+
- Use `buffer.writeInt64(Int64(...))` and `buffer.readInt64()` for full-range
143+
signed 64-bit values.
144+
- Use `buffer.writeUint64(Uint64(...))` and `buffer.readUint64()` for full-range
145+
unsigned 64-bit values.
146+
- Use `writeInt64FromInt`, `writeVarInt64FromInt`, and matching `AsInt` reads
147+
only when the value is intended to be a Dart `int` and therefore must stay
148+
JS-safe on web.
149+
150+
Example:
151+
152+
```dart
153+
final class OffsetSerializer extends Serializer<StorageExtent> {
154+
const OffsetSerializer();
155+
156+
@override
157+
void write(WriteContext context, StorageExtent value) {
158+
context.buffer.writeUint64(value.byteOffset);
159+
}
160+
161+
@override
162+
StorageExtent read(ReadContext context) {
163+
return StorageExtent()..byteOffset = context.buffer.readUint64();
164+
}
165+
}
166+
```
167+
168+
## Collections And Typed Arrays
169+
170+
`List`, `Set`, `Map`, `Uint8List`, numeric typed arrays, `Int64List`, and
171+
`Uint64List` are supported on web. The `Int64List` and `Uint64List`
172+
implementations preserve 64-bit values without depending on JavaScript integer
173+
precision. Use the Fory wrapper list types when the wire type is `int64_array`
174+
or `uint64_array`.
175+
176+
## Testing Browser Builds
177+
178+
Run the package tests in both VM and Chrome when changing code that must work on
179+
web:
180+
181+
```bash
182+
cd dart/packages/fory
183+
dart run build_runner build --delete-conflicting-outputs
184+
dart test
185+
dart test -p chrome
186+
```
187+
188+
If Chrome tests fail with a stale generated file or missing part file, rerun
189+
`build_runner` and then retry the test command from `dart/packages/fory`.
190+
191+
## Common Web Failures
192+
193+
### `Dart int value ... is outside the JS-safe signed int64 range`
194+
195+
The serializer is trying to write a Dart `int` as a signed 64-bit value on web,
196+
but the value is outside the range that JavaScript numbers can represent
197+
exactly. Change the field type to `Int64`, or keep the value inside the JS-safe
198+
range.
199+
200+
### `Int64 value ... is not a JS-safe int`
201+
202+
The deserializer read a full-range `Int64`, but the target field or custom
203+
serializer asked for a Dart `int`. Change the field type to `Int64`, or decode
204+
with `readInt64()` instead of an `AsInt` helper.
205+
206+
### `Uint64 value ... is not a JS-safe int`
207+
208+
The code is converting a `Uint64` to Dart `int` on web. Keep the value as
209+
`Uint64` unless the application has already validated that it is in the
210+
JS-safe non-negative range.
211+
212+
## Related Topics
213+
214+
- [Supported Types](supported-types.md)
215+
- [Field Configuration](field-configuration.md)
216+
- [Code Generation](code-generation.md)
217+
- [Troubleshooting](troubleshooting.md)

0 commit comments

Comments
 (0)