dataforge 1.0.0-dev.0
dataforge: ^1.0.0-dev.0 copied to clipboard
Build runner generator for deeply immutable Dataforge v1 value models.
Dataforge Generator #
The build_runner adapter for Dataforge v1 deeply immutable value models.
1.0.0-dev.0is a preview, not the 1.0 GA release. APIs, diagnostics and generated output may still change in later preview versions.
Installation #
dependencies:
dataforge_annotation: ^1.0.0-dev.0
dev_dependencies:
build_runner: ^2.4.13
dataforge: ^1.0.0-dev.0
All Dataforge packages in an application must resolve to the same preview version. Dart 3.9 or later is required.
Model declaration #
import 'package:dataforge_annotation/dataforge_annotation.dart';
part 'user.data.dart';
@Dataforge()
abstract final class User with _$User {
const User._();
factory User({
required String name,
@DataforgeDefault(<String>[]) List<String> tags,
@JsonKey(name: 'display_name') String? displayName,
}) = _User;
factory User.fromJson(Map<String, Object?> json) = _User.fromJson;
}
The declaration contract is strict:
- the public model is
abstract final; - value properties come from one unnamed redirecting factory;
- the generated target is
_Userand the generated mixin is_$User; const User._();provides the base constructor for the final implementation;- the
partURI must end in.data.dartand match the source file; - JSON input is
Map<String, Object?>; - a
fromJsonfactory, when enabled, redirects to_User.fromJson.
Invalid declarations fail generation with a source-located DF diagnostic.
The generator never rewrites the model source.
Generate #
dart run build_runner build
For continuous generation:
dart run build_runner watch
The published builder always emits .data.dart part files. The output suffix is
not configurable in this preview.
Value semantics #
Generated implementations are final. Constructor, copyWith, default and
decode boundaries recursively freeze the complete declared type tree:
List,SetandMapinputs are copied and exposed through unmodifiable collections;- nested collections and records are traversed recursively;
- equality and hash use the same leaf witnesses as freeze;
copyWithmay safely share values already frozen under compatible witness semantics.
Mutating caller-owned collections after construction cannot change a model. Set elements and Map keys that collapse under witness equality are rejected instead of being silently discarded.
Annotations #
@Dataforge #
@Dataforge(
name: '',
includeFromJson: true,
includeToJson: true,
)
name changes the private implementation base name. JSON directions can be
disabled independently.
@DataforgeDefault #
Dart does not allow a default directly on a redirecting factory parameter. Use compile-time metadata instead:
factory Page({
@DataforgeDefault(1) int number,
@DataforgeDefault(<String>[]) List<String> labels,
}) = _Page;
The resolved frontend validates that the constant is assignable and can be rendered without losing meaning.
@JsonKey #
factory User({
@JsonKey(
name: 'user_name',
alternateNames: ['username'],
includeIfNull: false,
)
String? name,
}) = _User;
ignore: true excludes a field from both JSON directions. An ignored required
field must still have a construction default.
Strict JSON #
Generated codecs do not coerce unrelated runtime types. They validate required
fields, nullability, unknown keys, aliases, duplicate decoded keys and custom
witness output. Failures use DataforgeDecodeException or
DataforgeEncodeException with a stable DFJ code and JSONPath.
DateTime is encoded as normalized UTC ISO-8601 text. Record values have
recursive non-JSON value semantics, but a Record subtree used by JSON requires
an exact DataforgeType<RecordShape> witness; otherwise generation reports
DF1006.
Generic and custom values #
Any behavior that depends on a type parameter or unsupported custom leaf needs
an explicit DataforgeType<T> witness:
@Dataforge()
abstract final class Box<T> with _$Box<T> {
const Box._();
factory Box({
required DataforgeType<T> type,
required T value,
}) = _Box<T>;
factory Box.fromJson(
Map<String, Object?> json, {
required DataforgeType<T> type,
}) = _Box<T>.fromJson;
}
A custom witness is part of the value contract. Its identity tree and its
freeze/equality/hash/codec behavior must remain immutable for its entire
lifetime. freeze must isolate mutable input; equality and hash must agree.
Standalone CLI #
The dataforge_cli package provides a standalone resolved adapter:
dataforge generate .
dataforge check .
Both adapters use the same resolved facade and renderer. Representative fixtures assert byte parity, but the full GA type/platform parity matrix is not complete yet.
Troubleshooting #
- Run
dart pub getin the nearest package root. - Check the exact
part,_$Model,_Modeland private base constructor names. - Use
@DataforgeDefaultrather than a redirecting-factory default. - Provide one compatible witness for every type-dependent semantic subtree.
- Follow the first source-located
DFdiagnostic; later errors are often consequences of the same declaration mismatch.
See the 1.0 RFC and support matrix for the preview boundary.