d_rocket 1.0.5 copy "d_rocket: ^1.0.5" to clipboard
d_rocket: ^1.0.5 copied to clipboard

Dart/Flutter data-layer framework: @Serializable codegen, @RestClient with retry, deferred LINQ, and a code-first SQLite ORM with migrations. See README for platform support.

Changelog #

All notable changes to d_rocket are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

1.0.5 — 2026-06-15 #

Patch release. Adds optional, end-to-end encryption of the local SQLite database via SQLCipher. Existing callers that do not opt in are unaffected.

  • Encrypted database support. Db.open and Db.inMemory now accept an optional password parameter that is forwarded to the underlying SQLite engine as PRAGMA key after open. The same parameter is exposed on SqliteQueryProvider.file and SqliteQueryProvider.inMemory for advanced users. The default (password: null) preserves the 1.0.x behavior — plain SQLite, no key — so this change is fully backward compatible.

  • Single-quote escaping. Passwords that contain ' characters are escaped by doubling (O'BrienO''Brien) before being interpolated into the PRAGMA key = '...' literal. The escape is applied by a single private helper, SqliteQueryProvider._applyPragmaKey.

  • Open-time wrong-password detection. After running PRAGMA key, the provider issues a verification query (SELECT count(*) FROM sqlite_master) and closes the connection with a DatabaseException if the first encrypted page cannot be decrypted. The exception message is explicit about the three possible causes (wrong password, non-SQLCipher file, engine is not SQLCipher) and links to doc/13-faq.md for the engine setup. This avoids the alternative failure mode where the database appears to open successfully and then returns garbage on the first read.

  • Engine responsibility is the consumer's. d_rocket does not bundle a SQLCipher build. The consumer installs sqlcipher_flutter_libs on Flutter, or libsqlcipher system-wide on desktop, and the PRAGMA key is then effective. A vanilla SQLite engine silently ignores PRAGMA key, so the parameter is a no-op without a SQLCipher native library. The documentation in doc/13-faq.md (new "Security" section) covers the platform setup in detail.

  • New tests in test/sqlite/encrypted_db_test.dart:

    • The password parameter is accepted on Db.open, Db.inMemory, SqliteQueryProvider.file, and SqliteQueryProvider.inMemory (compile checks).
    • A password containing a single quote is escaped and does not produce a syntax error.
    • Opening without a password continues to work (back-compat, no behavior change).
    • An end-to-end round-trip (open → CREATE TABLEINSERT → close → reopen → read) and a wrong-password rejection are gated on a runtime probe for libsqlcipher; on hosts without the engine they are skipped with an explanatory message.
  • Cookbook recipe rewritten. The "Database encryption (SQLCipher)" entry in doc/12-cookbook.md previously documented an SqliteEncryption(...) API that does not exist in the package; the recipe has been rewritten to use the actual password: parameter on Db.open, and now links to the Security section of the FAQ for the engine setup and the PRAGMA rekey recipe.

  • Threat-model section added to the FAQ. doc/13-faq.md now has a "What does SQLCipher protect against — and what doesn't it?" entry that enumerates the realistic protection boundary (file at rest, backups, page HMAC, weak-password brute force) and the realistic non-protection boundary (root on a running device, the keychain itself, data in transit, the application process, side channels, a stolen unlocked device). The short mental model at the end ("SQLCipher makes a copied file unreadable without the key") is the line the docs want a security reviewer to walk away with.

1.0.4 — 2026-06-14 #

Patch release. Lifts the restriction that primary keys in @Table entities had to be int. UUID (and other non-int) primary keys are now supported, and auto-incrementing String PKs are filled with a UUID v4 at INSERT time.

  • Non-int primary keys (DDL). EntityMeta._columnDdl in lib/src/orm/entity_meta.dart previously emitted INTEGER PRIMARY KEY for every primary key, regardless of the field's Dart type. A @PrimaryKey(autoIncrement: false) String id field — the typical UUID pattern — therefore generated id INTEGER PRIMARY KEY in the CREATE TABLE DDL, which then failed at insert time when a UUID string was passed. The DDL now uses the field's actual SQLite type for non-auto-incrementing PKs, so a String PK produces id TEXT PRIMARY KEY, a DateTime PK produces started_at TEXT PRIMARY KEY, and so on. int PKs with autoIncrement: true still emit INTEGER PRIMARY KEY AUTOINCREMENT (SQLite's AUTOINCREMENT is restricted to INTEGER PRIMARY KEY, so that branch is unchanged). For isAutoIncrement: true on a non-int type, the column is <type> PRIMARY KEY (no AUTOINCREMENT keyword) and the runtime fills the value.

  • Auto-incrementing String PKs are filled with a UUID v4. When a @PrimaryKey() (default autoIncrement: true) is on a String field and the entity is inserted without a value set for that field, DbSet.insertOne in lib/src/orm/db_set.dart now generates a UUID v4 via the new generateUuidV4() helper (also exported from the package) and writes it back through the codegen-supplied meta.setId closure. The column DDL is id TEXT PRIMARY KEY (see above). The new helper is backed by Random.secure() and produces RFC 4122 v4 UUIDs (xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with y in 8/9/a/b).

  • @PrimaryKey docstring updated in lib/src/orm/primary_key.dart to document the supported field types and the autoIncrement semantics, including the "runtime fills the value for non-int PKs" behavior.

  • New unit tests in test/orm_runtime_test.dart:

    • String PK with autoIncrement: true emits id TEXT PRIMARY KEY and does not emit AUTOINCREMENT.
    • String PK with autoIncrement: false (the previous patch) still emits id TEXT PRIMARY KEY.
    • DateTime PK emits started_at TEXT PRIMARY KEY.
    • generateUuidV4() returns a valid RFC 4122 v4 UUID and consecutive calls do not collide.

No behavior change for existing int PKs. For non-int PKs the generated CREATE TABLE DDL is now correct instead of broken, and for autoIncrement: true String PKs the runtime generates a UUID v4 so callers no longer need to set a value before saveChanges.

1.0.3 — 2026-06-14 #

Patch release. Lifts the restriction that primary keys in @Table entities had to be int. UUID (and other non-int) primary keys are now supported.

  • Non-int primary keys. EntityMeta._columnDdl in lib/src/orm/entity_meta.dart previously emitted INTEGER PRIMARY KEY for every primary key, regardless of the field's Dart type. A @PrimaryKey(autoIncrement: false) String id field — the typical UUID pattern — therefore generated id INTEGER PRIMARY KEY in the CREATE TABLE DDL, which then failed at insert time when a UUID string was passed. The DDL now uses the field's actual SQLite type for non-auto-incrementing PKs, so a String PK produces id TEXT PRIMARY KEY, a DateTime PK produces created_at TEXT PRIMARY KEY, and so on. int PKs with autoIncrement: true still emit INTEGER PRIMARY KEY AUTOINCREMENT (SQLite's AUTOINCREMENT is restricted to INTEGER PRIMARY KEY, so this branch is unchanged).

  • @PrimaryKey docstring updated in lib/src/orm/primary_key.dart to document the supported field types and the autoIncrement semantics.

  • New unit test in test/orm_runtime_test.dart covering String and DateTime primary keys. Existing int PK tests are unchanged and still pass.

No runtime behavior changes for int PKs. For non-int PKs the generated CREATE TABLE DDL is now correct instead of broken.

[1.0.3] — 2026-06-14 #

Patch release. Two changes:

  • CREATE TABLE now uses IF NOT EXISTS. EntityMeta.createTableDdl() in lib/src/orm/entity_meta.dart previously emitted CREATE TABLE $tableName ( unconditionally. On a fresh database this is fine, but any re-run of the migration on an existing database (e.g. a development reset that left tables behind, or a hot-reload in Flutter) threw SqliteException(1): table X already exists. The 3 unit tests that asserted the old CREATE TABLE X prefix were updated to assert the new CREATE TABLE IF NOT EXISTS X prefix. The behavior change is purely additive — the fresh-install path is unchanged (SQLite creates the table), and the re-run path is now a no-op (SQLite sees the table and skips).
  • README doc links dropped the packages/d_rocket/ prefix. The 14 doc links in the README's "Docs" section (and the 3 inline cross-references) pointed to packages/d_rocket/doc/ in the monorepo. They now point to doc/ at the repo root (e.g. https://github.com/torogoz-tech/d_rocket/blob/main/doc/01-overview.md). This is the URL shape the project README ships with on pub.dev.

No API or behavior changes.

1.0.2 — 2026-06-13 #

Patch release. Fixes the README's doc-link index on the published package (the pub.dev tarball includes the README, so the 1.0.1 fix only landed on GitHub). The 1.0.2 tarball carries the same README fix as GitHub commit c716699.

  • 14 doc links in README pointed to blob/main/doc/0X-... at the repo root, but the docs actually live under packages/d_rocket/doc/0X-... (the repo is a monorepo with the d_rocket package under packages/). All 14 now have the correct packages/d_rocket/ prefix.
  • 2 stale names in the "Docs" section survived the v1.0.0 rename: @RocketTable@Table in the Layer 4 bullet, and the CLI tools d_rocket:rocket_migration / d_rocket:rocket_closured_rocket:migration / d_rocket:closure.

The 17 remaining @Rocket* / d_rocket:rocket_* references in the README are the rename-mapping table (lines 79-86) and the CHANGELOG entries — intentional historical record.

1.0.1 — 2026-06-13 #

Patch release. No API changes. Fixes the pub.dev scoring report and the doc link index.

  • Moved lib/example/bookstore.dart and lib/example/quickstart.dart to example/. Both files require the codegen output to compile (they import d_rocket_registry.g.dart and bookstore.g.dart), which made pana fail the static-analysis check on the published tarball (5 errors, all URI_HAS_NOT_BEEN_GENERATED / UNDEFINED_FUNCTION). Files in example/ are not analyzed by pana, so the score recovers. The codegen-emitted central registry (lib/d_rocket_registry.g.dart) was patched to import the example from its new location. The two test files that imported the example via package:d_rocket/example/bookstore.dart were updated to use a relative import.
  • README doc-index fixes. The 14 links in the doc index pointed to /docs/ (with 's'); the actual folder is /doc/. The 3 inline doc references pointed to short file names (serialization.md, rest.md, linq.md) that no longer exist (renamed to 04-layer-1-serialization.md, 05-layer-2-rest.md, 06-layer-3-linq.md in the v0.4 doc reorganization). Also two stale identifiers in the overview table and the top code sample (RocketDbContextDbContext, RocketDb.openDb.open).
  • CHANGELOG header cleanup. Removed the "— First stable release" subtitle from the 1.0.0 header to match the version-only convention used elsewhere.

1.0.0 — 2026-06-12 #

The first stable, production-ready release of d_rocket. The public API is now frozen within the 1.x series: minor versions may add features, patch versions fix bugs, and breaking changes will trigger a 2.0 bump.

This release consolidates four prior pre-releases (0.1.0-dev, 0.3.0-dev, 0.4.0-dev, 0.5.0-dev) into a single, cohesive 1.0. The SQLite storage engine is now bundled directly in this package; the d_rocket_provider_sqlite companion package is kept as a thin compatibility shim for projects that have not yet migrated.

Breaking changes #

  • The Rocket prefix is gone from the public API. Every public type and every CLI command has been renamed. The old RocketTable is now Table; RocketDbContext is DbContext; the CLI command d_rocket:rocket_migration is now d_rocket:migration; etc. The full mapping is in the README's "Breaking changes in v1.0 — the rename" section. The annotation @RocketMigration is now @Migration; the abstract base class that the codegen-emitted migration subclass extends is MigrationBase (the two names are deliberately distinct to avoid a same-library collision with the annotation). Codegen output is regenerated from scratch on every build, so users that ran build_runner on the pre-release will need a one-time dart run build_runner build --delete-conflicting-outputs after upgrading.

Highlights #

  • One package, one mental model, one generator. Annotation- driven serialization, REST, LINQ, and ORM share the same design vocabulary, error model, and initializeD() wiring.
  • Async-first throughout. Every terminal query operator has an *Async_ sibling that returns a Future. No callback chains.
  • SQLite-bundled. RocketDb.open(path: ...) returns a fully-wired database. package:sqlite3 is the only engine shipped out of the box.
  • Offline-first sync & realtime. SyncProvider for push/pull pipelines, WebSocketClient and ServerSentEventsClient for typed realtime streams.
  • 989 tests across the runtime, the codegen, and the SQLite engine — all passing.

Added (since 0.4.0-dev) #

Layer 1 — Serialization

  • @Serializable with fromJson / toJson codegen.
  • JsonNaming policy: none, snakeCase, camelCase, kebabCase, pascalCase.
  • UnknownKeyPolicy: ignore (default, drop unknown keys), strict (throw), capture (route extras to an extra: Map<String, Object?> field — the class must declare one).
  • @JsonKey(name: ..., ignore: ..., requiredKey: ..., defaultValue: ..., converter: ..., useEnumIndex: ..., unknownEnumValue: ...) for per-field overrides.
  • Format (class, not enum): Format.trim(), Format.uppercase(), Format.lowercase(), Format.date('yyyy-MM-dd' | 'iso8601'), Format.custom(name), Format.customWith(type).
  • @SerializableUnion for sealed sum types with discriminator dispatch.

Layer 2 — REST

  • @RestClient with @HttpGet / @HttpPost / @HttpPut / @HttpPatch / @HttpDelete / @HttpHead.
  • Parameter binding: @Path, @Query, @Header, @Body, @Field, @Part, @RawBody.
  • RestConfig for one-place resilience configuration.
  • RetryPolicy with Backoff.exponential / Backoff.fixed / Backoff.linear.
  • RateLimit(requestsPerSecond: ...) token-bucket throttle.
  • CircuitBreaker state machine (closedopenhalfOpenclosed) with dRest.circuitState<T>().
  • RestInterceptor interface and dRest.use(...) chain (auth, logging, tracing, metrics).
  • Typed exception hierarchy: RestHttpException, NetworkException, RestConfigException.
  • CancelToken for cancellable requests.
  • Stream<T> return types for streaming endpoints.

Layer 3 — LINQ

  • IQueryable<T> with deferred execution.
  • Operators: where_, ofType_, select_, take_, skip_, takeWhile_, skipWhile_, orderBy_, orderByDescending_, thenBy_, thenByDescending_, distinct_, concat_, union_, intersect_, except_, any_, all_, contains_, count_, longCount_, sum_, average_, min_, max_, aggregate_, first_, firstOrDefault_, single_, singleOrDefault_, elementAt_, elementAtOrDefault_, toList_, toSet_, toMap_, asEnumerable_, cast_, join_, groupJoin_, groupBy_.
  • Async terminal operators: toListAsync_, toSetAsync_, firstAsync_, firstOrDefaultAsync_, countAsync_, sumAsync_, averageAsync_, minAsync_, maxAsync_, anyAsync_, allAsync_.
  • Expr DSL for expression-tree portability (the same where_(...) predicate is evaluated in-memory by the LINQ provider or pushed down to SQL by the ORM).
  • Closure-sugar extensions for prototyping over Iterable<T>.
  • Reactive watch() returning a Stream for live data.

Layer 4 — ORM (SQLite-bundled)

  • @RocketTable('table_name') for entity declaration.
  • @PrimaryKey(autoIncrement: true), @Column(name: ..., nullable: true, unique: true).
  • Type mapping: int, double, String, bool, DateTime (ISO-8601), Uint8List (BLOB).
  • @BelongsTo and @HasMany for navigation properties.
  • RocketDb.open(path: ..., strategy: ...) / RocketDb.inMemory().
  • Change-tracked DbSet<T>: add / addAsync, updateWhere / updateWhereAsync, removeWhere / removeWhereAsync.
  • saveChanges() / saveChangesAsync() flushes the change set in a single transaction.
  • include_<T>() codegen for eager-loading related entities in one round-trip.
  • asLinqQueryable() bridge to the SQL LINQ provider.
  • Bulk operations: addAll, updateAll, removeAll.
  • Reactive queries: watch() returns a Stream.

Migrations

  • Migration base class with id, version, name, up(exec), down(exec).
  • MigrationRunner for direct execution.
  • MigrationStrategy with declarative migrations list AND imperative onCreate / onUpgrade / onDowngrade callbacks.
  • Automatic upgrade / downgrade detection based on currentVersion() vs. targetVersion.
  • _d_rocket_migrations table for persisted migration history.
  • dart run d_rocket:rocket_migration add <name> CLI scaffolder.
  • dart run d_rocket:rocket_migration doctor validator.

Sync (offline-first)

  • SyncProvider interface for push / pull pipelines.
  • SyncOp queue with persistence to SQLite.
  • Background flush on table-change watch streams.
  • Conflict-resolution policies: lastWriterWins (default)
    • serverWins + clientWins + custom callback.
  • Identity persistence for re-attach after process restart.
  • Exponential-backoff retry on NetworkException.

Realtime

  • @WebSocketRoute for typed WebSocket methods returning Stream<T>.
  • @SseRoute for typed Server-Sent Events methods returning Stream<T>.
  • Reconnect with exponential backoff.
  • Heartbeat / ping support.

Codegen (d_rocket_builder)

  • d_rocket:rocket_serializer builder — emits per-class fromJson / toJson and central register<X>Serializer.
  • d_rocket:rocket_rest_client builder — emits per-interface RestClient implementations with interceptors, retry, and serialization wired in.
  • d_rocket:rocket_table builder — emits per-class fromRow and setId closures for the ORM.
  • Single generated d_rocket_registry.g.dart with initializeD() that registers every annotated class in the project.

Migration from d_serializer / d_rest 0.x #

  • d_serializer 1.3.0 was absorbed into d_rocket 1.0. Replace package:d_serializer/d_serializer.dart with package:d_rocket/d_rocket.dart. The annotation API is unchanged; the only API renames are Serializer.fromJsonSerializer.fromJson<T> and Format import path.
  • d_rest 0.1.0 was absorbed into d_rocket 1.0. Replace package:d_rest/d_rest.dart with package:d_rocket/d_rocket.dart. The @RestClient API is unchanged; resilience config moved from RestClientBuilder to RestConfig and the circuitState<T>() extension moved to dRest.circuitState<T>().

Acknowledgements #

The API design draws on patterns from several well-known frameworks: Entity Framework Core (DbContext, change tracking, Migrations), .NET LINQ (deferred execution, operator matrix), Retrofit (annotated interfaces), Moshi and kotlinx.serialization (annotation-driven serialization), and sqflite (SQLite migration runner).

License #

© Torogoz Tech. Released under the MIT License.

2
likes
0
points
537
downloads

Documentation

Documentation

Publisher

verified publishertorogoz.tech

Weekly Downloads

Dart/Flutter data-layer framework: @Serializable codegen, @RestClient with retry, deferred LINQ, and a code-first SQLite ORM with migrations. See README for platform support.

Repository (GitHub)
View/report issues

Topics

#linq #orm #sqlite #codegen #framework

License

unknown (license)

Dependencies

flutter, http, meta, sqlite3

More

Packages that depend on d_rocket