Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .fvmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"flutter": "3.41.2"
"flutter": "3.47.5"
}
5 changes: 3 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

## Repository layout
- `packages/ack`: core runtime validation library.
- `packages/ack_annotations`: annotations for schema-first `@AckInfer()`,
class-first `@AckModel()`, and frozen legacy `@AckType()` generation.
- `packages/ack`: runtime validation and schema-generation annotations.
- `packages/ack_annotations`: compatibility re-exports for schema-first
`@AckInfer()`, class-first `@AckModel()`, and frozen legacy `@AckType()`.
- `packages/ack_generator`: build_runner generator + unit/integration tests.
- `packages/ack_firebase_ai`: Firebase AI schema adapter.
- `packages/ack_json_schema_builder`: JSON Schema adapter.
Expand Down
4 changes: 4 additions & 0 deletions .github/flutter-releases.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"description": "Flutter releases that this repository's workflows may install. Each entry pins the SHA-256 checksum of the official Linux archive and the Dart SDK that ships inside it. Copy both values from https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json before adding a version.",
"releases": {
"3.47.5": {
"dart": "3.13.4",
"sha256": "2132e990f236f8d22e7c6314b29a191a95b10d7cbcfec9b4e2e303d996652cbb"
},
"3.35.0": {
"dart": "3.9.0",
"sha256": "ff2d9474d768fdb61c1b8d6b76fd4eb98235c271e68b10abf41bd9fb41bc273d"
Expand Down
23 changes: 18 additions & 5 deletions .github/workflows/preflight.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ jobs:
- name: Resolve workspace dependencies
run: dart pub get

# The complete workspace cannot resolve on the minimum SDK, because
# flutter_test pins an older test_api than ack_generator's analyzer floor
# allows. The pure-Dart members still must resolve, so they get their own
# staged workspace.
# Runtime packages keep their Dart 3.9 minimum. The generator and its
# example require Dart 3.13 and use a separate staged workspace.
- name: Stage the pure-Dart workspace
run: dart scripts/stage_min_sdk_workspace.dart "${{ runner.temp }}/min-dart"
run: dart scripts/stage_min_sdk_workspace.dart "${{ runner.temp }}/min-dart" --runtime-only

- name: Stage the generator workspace
run: dart scripts/stage_min_sdk_workspace.dart "${{ runner.temp }}/min-generator"

- name: Set up the minimum Dart SDK
uses: dart-lang/setup-dart@6afc89df92d6eb3834022f73cd65adc8cdfcb92d # v1
Expand Down Expand Up @@ -90,6 +91,18 @@ jobs:
echo "::endgroup::"
done

- name: Set up the minimum generator SDK
uses: dart-lang/setup-dart@6afc89df92d6eb3834022f73cd65adc8cdfcb92d # v1
with:
sdk: '3.13.0'

- name: Check the generator on its minimum SDK
working-directory: ${{ runner.temp }}/min-generator/packages/ack_generator
run: |
dart pub get
dart analyze --fatal-infos
dart test

minimum-flutter-sdk:
name: Minimum Flutter SDK
runs-on: ubuntu-24.04
Expand Down
33 changes: 13 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ For AI agents: start at [`/llms.txt`](https://concepta.dev/documentation/ack/ref
- **Validate external payloads**: Guard API and user inputs by validating required fields, types, and constraints at boundaries
- **Single source of truth**: Define data structures and rules in one place
- **Less boilerplate**: Minimize repetitive validation and JSON conversion code
- **Type safety**: Generate immutable models for hand-written Ack schemas with `@AckInfer()`
- **Class-first generation**: Derive validated codec schemas from hand-written Dart classes with `@AckModel()`
- **Type safety**: Generate immutable models from Ack schemas with `@Schemable()`
- **Class-first generation**: Derive validated codec schemas from primary-constructor classes with `@Schemable()`

## Packages

This repository is a monorepo containing:

- **[ack](./packages/ack)**: Core validation library with a fluent schema-building API, codecs, and JSON Schema export
- **[ack_annotations](./packages/ack_annotations)**: The `@AckInfer()`, `@AckModel()`, and deprecated legacy `@AckType()` annotations
- **[ack_annotations](./packages/ack_annotations)**: Compatibility re-exports for annotations now owned by `ack`
- **[ack_generator](./packages/ack_generator)**: Generates models from schemas and schemas from hand-written models
- **[ack_firebase_ai](./packages/ack_firebase_ai)**: Firebase AI (Gemini) schema converter for structured-output generation
- **[ack_json_schema_builder](./packages/ack_json_schema_builder)**: Converter to `json_schema_builder` schemas
Expand Down Expand Up @@ -122,18 +122,17 @@ if (result.isOk) {

## Code generation

Generate immutable models for hand-written schemas with `@AckInfer()`. Add
`ack_annotations` to `dependencies` and `ack_generator` + `build_runner` to
Generate immutable models for hand-written schemas with `@Schemable()`. Add
`ack` to `dependencies` and `ack_generator` + `build_runner` to
`dev_dependencies`, then annotate a top-level schema:

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

part 'user.ack.dart';
part 'user.ack.g.dart';

@AckInfer()
@Schemable()
final userSchema = Ack.object({
'name': Ack.string().minLength(2),
'email': Ack.string().email(),
Expand All @@ -155,7 +154,7 @@ print(user.name); // String
print(user.toJson()); // {'name': 'Alice', 'email': 'alice@example.com'}
```

`@AckInfer()` supports objects, primitives, lists, enums, bidirectional codecs,
`@Schemable()` supports objects, primitives, lists, enums, bidirectional codecs,
named recursion, and discriminated unions. One-way transforms are rejected
because a generated model must be encodable. See the
[Model Code Generation guide](docs/core-concepts/typesafe-schemas.mdx).
Expand All @@ -168,7 +167,6 @@ removed in Ack 2.0:

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

part 'legacy_user.g.dart';

Expand All @@ -177,32 +175,27 @@ final userSchema = Ack.object({'name': Ack.string()});
```

This still generates `UserType`, including its Map interface, typed getters,
`parse` / `safeParse`, and `.args`. New code should use `@AckInfer()` or
`@AckModel()`.
`parse` / `safeParse`, and `.args`. New code should use `@Schemable()`.

| Ack 1.1 source | Optional immutable-model migration |
|---|---|
| Keep `@AckType()` | Rename it to `@AckInfer()` |
| Keep `@AckType()` | Rename it to `@Schemable()` |
| Keep `part 'file.g.dart';` | Add `file.ack.dart` and `file.ack.g.dart` parts |
| Use `*Type`, Map access, and `.args` | Use the generated class, typed fields, `parse`, `fromJson`, and `toJson` |

Legacy and modern declarations may coexist when they are unrelated. A nested
reference graph cannot cross between them; migrate that connected graph
together.

Already own the model class? Use `@AckModel()` to derive a codec schema from
Already own the model class? Use `@Schemable()` to derive a codec schema from
constructor-backed fields while keeping the class hand-written. A class named
`Account` receives an `AccountSchema` facade for parsing, encoding, schema
export, and nested composition; the backing codec remains private:

```dart
@AckModel()
final class Account with _$AccountAck {
const Account({required this.name});

@MinLength(2)
final String name;

@Schemable()
final class Account({@MinLength(2) required final String name})
with _$AccountAck {
static final fromJson = AccountSchema.fromJson;
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ For `Ack.discriminated(...)` constraints with `@AckInfer`, see
**Example:**
```dart
import 'package:ack/ack.dart';
import 'package:ack_annotations/ack_annotations.dart';
import 'package:ack/annotations.dart';

part 'user.ack.dart';
part 'user.ack.g.dart';
Expand Down
Loading
Loading