Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,263 changes: 0 additions & 1,263 deletions coverage/lcov.info

This file was deleted.

60 changes: 52 additions & 8 deletions docs/api-reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -198,30 +198,41 @@ the annotations below, run:
dart run build_runner build
```

### `@AckModel()`
### `@Schemable()`

**Target**: Dart classes

**Generates**: A schema constant only

Annotate a Dart class to automatically generate a validation schema. The generator analyzes your class fields and creates a schema constant named `<className>Schema` (e.g., `userSchema`).
Annotate a Dart class to automatically generate a validation schema. The generator reads one constructor contract, turns its named parameters into schema fields, and creates a schema constant named `<className>Schema` (for example, `userSchema`).

**Parameters:**
- `schemaName`: Custom name for the generated schema constant
- `description`: Documentation string for the schema
- `additionalProperties`: Allow extra fields not defined in the class
- `additionalProperties`: Allow extra fields not defined in the class (passthrough behavior)
- `additionalPropertiesField`: Field name to store extra properties (must be `Map<String, dynamic>`)
- `discriminatedKey`: Field name for type discrimination (use on abstract base classes)
- `discriminatedValue`: Discriminator value for this subtype (use on concrete implementations)
- `discriminatorKey`: Field name for type discrimination (use on sealed base classes)
- `discriminatorValue`: Discriminator value for this subtype (use on concrete implementations)
- `caseStyle`: Case transformation for constructor parameter names
- `useProviders`: Explicit `SchemaProvider<T>` registrations for custom non-schemable types

`@Schemable` libraries with a `part` directive must include an unprefixed
`import 'package:ack/ack.dart';` because generated code references `Ack`
directly.

**Example:**
```dart
@AckModel(description: 'User profile')
import 'package:ack/ack.dart';
import 'package:ack_annotations/ack_annotations.dart';

part 'user.g.dart';

@Schemable(description: 'User profile')
class User {
final String name;
final int age;

User({required this.name, required this.age});
const User({required this.name, required this.age});
}

// Generated:
Expand All @@ -235,13 +246,46 @@ if (result.isOk) {
}
```

`@AckModel()` still works as a compatibility alias, but new code should prefer `@Schemable()`.

### `@SchemaConstructor()`

**Target**: Constructors

Marks the constructor that defines the generated schema contract when the unnamed constructor is not the correct wire shape. The selected constructor must use named parameters only.

### `@SchemaKey()`

**Target**: Constructor parameters

Overrides the generated schema key for a specific constructor parameter.

### `@Description()`

**Target**: Constructor parameters

Attaches schema documentation to a generated field.

### `SchemaProvider<T>`

**Target**: Provider classes registered through `@Schemable(useProviders: ...)`

Supplies an explicit schema for a custom type that is not itself `@Schemable()`. Providers must:

- be concrete classes
- have a const unnamed constructor with no parameters
- implement `SchemaProvider<T>`
- return `AckSchema<T>` from the `schema` getter

Provider targets that already have generated schemas are rejected. Generated schemable types remain the source of truth for those models.

### `@AckType()`

**Target**: Schema variables and getters

**Generates**: ONLY an extension type (schema must already exist)

Annotate an existing schema variable or getter to generate an extension type wrapper. Unlike `@AckModel`, this does not create the schema—it only adds type-safe access to a schema you've already written.
Annotate an existing schema variable or getter to generate an extension type wrapper. Unlike `@Schemable`, this does not create the schema. It only adds type-safe access to a schema you've already written.

**Supported schema types:**
- `Ack.object({...})` → Object extension types
Expand Down
25 changes: 13 additions & 12 deletions docs/core-concepts/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,40 +97,41 @@ final signUpSchema = Ack.object({

The `ack_generator` package provides automatic schema generation from annotated classes. This feature is production-ready.

To use code generation, annotate your classes with `@AckModel`. The generator creates a validation schema from your class structure. Use `@AckField` for field-level constraints:
To use code generation, annotate your classes with `@Schemable()`. The generator reads a constructor contract, not field annotations:

```dart
import 'package:ack_annotations/ack_annotations.dart';

part 'user.g.dart';

@AckModel(
@Schemable(
description: 'User profile with flexible preferences',
additionalProperties: true,
additionalPropertiesField: 'preferences'
)
class User {
@AckField(constraints: ['minLength(2)', 'maxLength(50)'])
final String name;

@AckField(constraints: ['email'])
final String email;

@AckField(constraints: ['min(18)'])
final int? age; // Optional field

// Collects extra properties not defined in the schema
final Map<String, dynamic> preferences;

User({
required this.name,
required this.email,
this.age,
const User({
@MinLength(2) @MaxLength(50) required this.name,
@Email() required this.email,
@Min(18) this.age,
this.preferences = const {},
});
}
```

Key rules:

- The selected constructor must use named parameters.
- Use `@SchemaConstructor()` to choose a non-default constructor.
- Use parameter annotations such as `@SchemaKey(...)` and `@Description(...)` to customize generated fields.

After running `dart run build_runner build`, the generator creates in `user.g.dart`:

1. **Schema constant**: `final userSchema = Ack.object({...});`
Expand Down Expand Up @@ -172,5 +173,5 @@ annotated with `@AckType()` (see
## Summary

- Ack primarily uses **schema-level configuration** through methods and parameters like `.minLength()`, `.optional()`, `additionalProperties: ...`.
- **Manual schema definition** is the recommended approach for production applications.
- Ack supports both **manual schema definition** and **constructor-driven schema generation** for production applications.
- There is currently no central **global configuration** object provided by Ack itself.
9 changes: 5 additions & 4 deletions docs/core-concepts/json-serialization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,22 @@ if (result.isOk) {

## Using Code Generation

The `ack_generator` package automatically creates schemas from your annotated classes:
The `ack_generator` package automatically creates schemas from `@Schemable()`
classes using the selected constructor's named parameters:

```dart
// user.dart
import 'package:ack_annotations/ack_annotations.dart';

part 'user.g.dart';

@AckModel()
@Schemable()
class User {
final String name;
final String email;
final int age;

User({required this.name, required this.email, required this.age});
const User({required this.name, required this.email, required this.age});
}
```

Expand Down Expand Up @@ -143,7 +144,7 @@ if (result.isOk) {

### Generating Extension Types from Standalone Schemas with `@AckType()`

When you write schemas manually (without a backing class), annotate the variable or getter with `@AckType()` to generate an extension type wrapper. Unlike `@AckModel`, this **does not create the schema**—it only adds type-safe access to your hand-written schema.
When you write schemas manually (without a backing class), annotate the variable or getter with `@AckType()` to generate an extension type wrapper. Unlike `@Schemable`, this **does not create the schema**—it only adds type-safe access to your hand-written schema.

```dart
// user_schema.dart
Expand Down
47 changes: 27 additions & 20 deletions docs/core-concepts/typesafe-schemas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,40 @@ title: TypeSafe Schemas

Ack generates **extension types** that wrap validated data so you can work with
strongly typed objects instead of raw `Map<String, Object?>`. This guide shows
how `@AckModel()` and `@AckType()` annotations interact with the generator to
how `@Schemable()` and `@AckType()` annotations interact with the generator to
produce these typed views.

## Overview

- `@AckModel()` goes on a **Dart class** and produces a **schema constant** (e.g., `userSchema`).
- `@Schemable()` goes on a **Dart class** and produces a **schema constant** (e.g., `userSchema`).
- `@AckType()` goes on a **schema variable/getter** and produces an **extension type** for type-safe access (the schema already exists). The type name is derived from the variable name (e.g., `userSchema` → `UserType`).
- Both annotations live in `package:ack_annotations` and are processed by the
`ack_generator` builder via `dart run build_runner build`.

## Schema Generation from Classes with `@AckModel()`
## Schema Generation from Classes with `@Schemable()`

Use `@AckModel()` when you have a Dart class that should drive schema generation. The generator creates a validation schema based on your class structure.
Use `@Schemable()` when you have a Dart class that should drive schema generation. The generator creates a validation schema from the selected constructor's named parameters.

```dart
import 'package:ack/ack.dart';
import 'package:ack_annotations/ack_annotations.dart';

part 'user.g.dart';

@AckModel(description: 'Profile details')
@Schemable(description: 'Profile details')
class User {
final String name;
final int age;

User({required this.name, required this.age});
const User({required this.name, required this.age});
}
```

`@Schemable` source files that declare a `part` must include an unprefixed
`import 'package:ack/ack.dart';` because generated code references `Ack`
directly. Unsupported custom types must be provided through
`@Schemable(useProviders: const [...])`.

Running `dart run build_runner build` writes `user.g.dart` with:

- **Schema constant**: `final userSchema = Ack.object({...});`
Expand All @@ -53,28 +59,27 @@ For type-safe access without manual casting, define a schema variable in source

### Discriminated Hierarchies

Annotate an abstract base with `discriminatedKey` and each subtype with a
`discriminatedValue` to receive a generated discriminated schema that maps
Annotate an abstract base with `discriminatorKey` and each subtype with a
`discriminatorValue` to receive a generated discriminated schema that maps
discriminator values to subtype schemas.

```dart
@AckModel(discriminatedKey: 'type')
abstract class Shape {
String get type;
@Schemable(discriminatorKey: 'type')
sealed class Shape {
const Shape();
}

@AckModel(discriminatedValue: 'circle')
@Schemable(discriminatorValue: 'circle')
class Circle extends Shape {
@override
String get type => 'circle';
final double radius;
Circle(this.radius);

const Circle({required this.radius});
}
```

## Typed Schemas from Hand-Written Definitions with `@AckType()`

Use `@AckType()` when you write schemas manually but still want type-safe access via extension types. Unlike `@AckModel`, this annotation does **not** generate a schema—it only generates an extension type wrapper for an existing schema.
Use `@AckType()` when you write schemas manually but still want type-safe access via extension types. Unlike `@Schemable`, this annotation does **not** generate a schema. It only generates an extension type wrapper for an existing schema.

```dart
import 'package:ack/ack.dart';
Expand Down Expand Up @@ -103,7 +108,7 @@ After running `dart run build_runner build`, the part file contains:
- **Extension type** `AddressType(Map<String, Object?> _data)` for the nested schema.
- **Static helpers** `parse`/`safeParse` so you can do `final user = UserType.parse(json);`.

**Key difference from @AckModel**: The schemas (`userSchema`, `addressSchema`) already exist in your source code. The annotation only adds the extension type layer.
**Key difference from `@Schemable`**: The schemas (`userSchema`, `addressSchema`) already exist in your source code. The annotation only adds the extension type layer.

### Supported Schema Shapes

Expand Down Expand Up @@ -157,21 +162,23 @@ Extension types implement the underlying Dart type, so primitive wrappers still
behave like `String`, `int`, etc., while object wrappers implement
`Map<String, Object?>`.

## Choosing Between `@AckModel` and `@AckType`
## Choosing Between `@Schemable` and `@AckType`

| Annotation | Target | Generates Schema? | Generates Extension Type? | Use When |
|------------|--------|-------------------|---------------------------|----------|
| `@AckModel` | Dart class | ✅ Yes | ❌ No | You have a class definition and want schema generation |
| `@Schemable` | Dart class | ✅ Yes | ❌ No | You have a class definition and want schema generation |
| `@AckType` | Top-level schema variable/getter | ❌ No (uses existing) | ✅ Yes | You have a schema and want type-safe access |

**Examples:**

- Use **`@AckModel`** when you have a Dart class (or class hierarchy) that should drive schema generation.
- Use **`@Schemable`** when you have a Dart class (or class hierarchy) that should drive schema generation.

- Use **`@AckType`** on hand-written top-level schema variables or getters when you want type-safe extension type access.

- **Note:** `@AckType` is not supported on classes or generated schemas. If you need extension types, define the schema directly in your source file.

Legacy `@AckModel()` still works as a compatibility alias, but new code should prefer `@Schemable()`.

## Build Runner Checklist

1. Add `ack_generator` and `ack_annotations` to `pubspec.yaml`.
Expand Down
Loading
Loading