Skip to content

Commit 412a5ba

Browse files
committed
chore: changelog + df_log bump + formatter pass for 0.15.0
Expanded CHANGELOG to capture all 0.15.0 work (fieldPath dual-shape, analyzer-driven enum sentinel, Equatable-by-inheritance, dialect prefixes, DBML export, orNull contract fixes). Bumped df_log constraint to ^0.5.1 to match the published latest. Regenerated all integration test fixtures and let the formatter canonicalise unrelated files.
1 parent f58b318 commit 412a5ba

41 files changed

Lines changed: 3377 additions & 2927 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
## [0.15.0]
44

5-
- Released @ 5/2026 (UTC)
6-
- Fix: AI prompt no longer collapses field path segments via `join('')` — uses `.` separator so the AI receives the structured field name (e.g. `user.name` instead of `username`)
7-
- Fix: AI generator now extracts field properties via the safe `FieldUtils` accessors instead of dynamic property access
8-
- Fix: `___FROM_JSON_OR_NULL_PARAMS___` template no longer crashes on fields with null `fieldPath` or null `fieldName`; comparator tolerates nulls
9-
- Fix: AI generator's top-level error path now logs the actual exception, not just a generic message
10-
- Fix: removed obsolete `df_generate_dart_models_gemeni` executable entry that referenced a non-existent script (superseded by `df_generate_dart_models_ai`)
11-
- Pulls in df_gen_core 0.8.0 with mapper robustness fixes
5+
- Released @ 6/2026 (UTC)
6+
- New: `fieldPath` dual-shape — dot-separated `String` or `Iterable<String>`; multi-segment paths emit deep null-aware accessors (`json?['profile']?['id']`)
7+
- New: enum detection without `Type`/`Enum` suffix — analyzer-driven via `@enum` sentinel; legacy suffix-based detection still works
8+
- New: Equatable opt-in by inheritance — `extends BaseModel` opts out (const-set-safe), everything else gets `EquatableMixin`
9+
- New: dialect support — `PG_*`, `SQLITE_*`, `FS_*`, `STRICT-` prefix vocabulary with composable mappers
10+
- New: DBML schema export via `df_generate_dbml` CLI — PKs, FKs, sized varchar/numeric, enums, JSONB columns
11+
- Fix: `fromOrNull` / `fromJsonStringOrNull` / `fromUriOrNull` templates honor the null-return contract (no more silent throws on the null/empty/wrong-path input)
12+
- Fix: AI prompt no longer collapses field path segments via `join('')` — uses `.` separator
13+
- Fix: AI generator uses safe `FieldUtils` accessors instead of dynamic property access
14+
- Fix: top-level catch logs the actual exception, not just a generic message
15+
- Removed: obsolete `df_generate_dart_models_gemeni` executable
16+
- Pulls in df_gen_core 0.8.0 and df_generate_dart_models_core 0.10.0
1217

1318
## [0.14.3]
1419

example/lib/models/model_all_types/model_all_types.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ class PaymentModel {
111111
// Special string-case types — base.
112112
Field(fieldPath: ['trimmed'], fieldType: 'Trimmed-String', nullable: true),
113113
Field(
114-
fieldPath: ['noSpaces'], fieldType: 'NoSpaces-String', nullable: true,),
114+
fieldPath: ['noSpaces'],
115+
fieldType: 'NoSpaces-String',
116+
nullable: true,
117+
),
115118
Field(
116119
fieldPath: ['searchable'],
117120
fieldType: 'Searchable-String',
@@ -143,7 +146,10 @@ class PaymentModel {
143146
),
144147
Field(fieldPath: ['camel'], fieldType: 'CamelCase-String', nullable: true),
145148
Field(
146-
fieldPath: ['pascal'], fieldType: 'PascalCase-String', nullable: true,),
149+
fieldPath: ['pascal'],
150+
fieldType: 'PascalCase-String',
151+
nullable: true,
152+
),
147153

148154
// Trimmed case conversions.
149155
Field(
@@ -192,7 +198,10 @@ class PaymentModel {
192198
Field(fieldPath: ['flags'], fieldType: Set<bool>, nullable: true),
193199
Field(fieldPath: ['scores'], fieldType: Iterable<int>, nullable: true),
194200
Field(
195-
fieldPath: ['lookup'], fieldType: Map<String, double>, nullable: true,),
201+
fieldPath: ['lookup'],
202+
fieldType: Map<String, double>,
203+
nullable: true,
204+
),
196205

197206
// Queue (from-side was missing — verifies the asymmetry fix).
198207
Field(fieldPath: ['todo'], fieldType: Queue<String>, nullable: true),

lib/src/dart_firestore_type_mappers.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ class DartFirestoreTypeMappers extends TypeMappers {
5252
//
5353

5454
@override
55-
TTypeMappers<CollectionMapperEvent> get collectionToMappers =>
56-
newTypeMap({});
55+
TTypeMappers<CollectionMapperEvent> get collectionToMappers => newTypeMap({});
5756

5857
//
5958
//

lib/src/dart_postgres_type_mappers.dart

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,22 @@ class DartPostgresTypeMappers extends TypeMappers {
130130
_pgTail +
131131
r'-(?:(Type-?\w+|\w+-?Type|Enum-?\w+|\w+-?Enum)|(\w+)@enum)\??$':
132132
(e) {
133-
final typeName = (e.matchGroups?.elementAt(1)) ??
134-
(e.matchGroups?.elementAt(2));
133+
final typeName =
134+
(e.matchGroups?.elementAt(1)) ?? (e.matchGroups?.elementAt(2));
135135
return '$typeName.values.valueOf(${e.name}?.toString())';
136136
},
137137

138138
// ─── DDL-only ───────────────────────────────────────────────────────
139139
// All of these forward to the same code [DartCoreTypeMappers] uses
140140
// for the bare Dart type. The prefix only exists for schema export.
141-
r'^PG_(?:text|varchar|char|citext|name|xml|uuid|inet|cidr|macaddr|macaddr8|tsvector|tsquery|money|pg_lsn|bit|oid)'
142-
+ _pgTail +
143-
r'-(String)\??$': (e) {
141+
r'^PG_(?:text|varchar|char|citext|name|xml|uuid|inet|cidr|macaddr|macaddr8|tsvector|tsquery|money|pg_lsn|bit|oid)' +
142+
_pgTail +
143+
r'-(String)\??$': (e) {
144144
return '${e.name}?.toString().trim().nullIfEmpty';
145145
},
146-
r'^PG_(?:smallint|integer|bigint|int2|int4|int8|smallserial|serial|bigserial)'
147-
+ _pgTail +
148-
r'-(int)\??$': (e) {
146+
r'^PG_(?:smallint|integer|bigint|int2|int4|int8|smallserial|serial|bigserial)' +
147+
_pgTail +
148+
r'-(int)\??$': (e) {
149149
return 'letIntOrNull(${e.name})';
150150
},
151151
r'^PG_(?:real|float4|float8|double)' + _pgTail + r'-(double)\??$': (e) {
@@ -162,9 +162,8 @@ class DartPostgresTypeMappers extends TypeMappers {
162162
r'^PG_boolean' + _pgTail + r'-(bool)\??$': (e) {
163163
return 'letBoolOrNull(${e.name})';
164164
},
165-
r'^PG_(?:timestamp|timestamptz|date)' +
166-
_pgTail +
167-
r'-(DateTime)\??$': (e) {
165+
r'^PG_(?:timestamp|timestamptz|date)' + _pgTail + r'-(DateTime)\??$':
166+
(e) {
168167
return '(){ final a = ${e.name}?.toString().trim().nullIfEmpty; return a != null ? DateTime.tryParse(a)?.toUtc(): null; }()';
169168
},
170169
r'^PG_interval' + _pgTail + r'-(Duration)\??$': (e) {
@@ -198,22 +197,21 @@ class DartPostgresTypeMappers extends TypeMappers {
198197
},
199198

200199
// ─── DDL-only — same code Core emits for the bare type ───────────
201-
r'^PG_(?:text|varchar|char|citext|name|xml|uuid|inet|cidr|macaddr|macaddr8|tsvector|tsquery|money|pg_lsn|bit|oid)'
202-
+ _pgTail +
203-
r'-(String)\??$': (e) {
200+
r'^PG_(?:text|varchar|char|citext|name|xml|uuid|inet|cidr|macaddr|macaddr8|tsvector|tsquery|money|pg_lsn|bit|oid)' +
201+
_pgTail +
202+
r'-(String)\??$': (e) {
204203
return '${e.name}?.trim().nullIfEmpty';
205204
},
206-
r'^PG_(?:smallint|integer|bigint|int2|int4|int8|smallserial|serial|bigserial|real|float4|float8|double|boolean)'
207-
+ _pgTail +
208-
r'-(int|double|bool)\??$': (e) {
205+
r'^PG_(?:smallint|integer|bigint|int2|int4|int8|smallserial|serial|bigserial|real|float4|float8|double|boolean)' +
206+
_pgTail +
207+
r'-(int|double|bool)\??$': (e) {
209208
return '${e.name}';
210209
},
211210
r'^PG_numeric' + _pgTail + r'-(String|double)\??$': (e) {
212211
return '${e.name}';
213212
},
214-
r'^PG_(?:timestamp|timestamptz|date)' +
215-
_pgTail +
216-
r'-(DateTime)\??$': (e) {
213+
r'^PG_(?:timestamp|timestamptz|date)' + _pgTail + r'-(DateTime)\??$':
214+
(e) {
217215
return '${e.name}?.toUtc().toIso8601String()';
218216
},
219217
r'^PG_interval' + _pgTail + r'-(Duration)\??$': (e) {

lib/src/dart_sqlite_type_mappers.dart

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ class DartSqliteTypeMappers extends TypeMappers {
8181
return '(){ final a = ${e.name}; return a != null ? jsonEncode(a.map((${e.args}) => MapEntry(${e.hashes},)).nonNulls.nullIfEmpty) : null; }()';
8282
},
8383
r'^SQLITE_(?:json|jsonb)' +
84-
_sqliteTail +
85-
r'-(List|Set|Iterable|Queue)\??$': (e) {
84+
_sqliteTail +
85+
r'-(List|Set|Iterable|Queue)\??$': (e) {
8686
return '(){ final a = ${e.name}; return a != null ? jsonEncode(a.map((${e.args}) => ${e.hashes},).nonNulls.nullIfEmpty?.toList()) : null; }()';
8787
},
8888
});
@@ -111,16 +111,16 @@ class DartSqliteTypeMappers extends TypeMappers {
111111
return '(){ final a = letDoubleOrNull(${e.name}); return a != null ? DateTime.fromMillisecondsSinceEpoch(((a - 2440587.5) * 86400000).round(), isUtc: true) : null; }()';
112112
},
113113
// JSON column with a nested model — same shape as PG_jsonb.
114-
r'^SQLITE_(?:json|jsonb)' + _sqliteTail + r'-(Model-?\w+|\w+-?Model)\??$':
115-
(e) {
114+
r'^SQLITE_(?:json|jsonb)' +
115+
_sqliteTail +
116+
r'-(Model-?\w+|\w+-?Model)\??$': (e) {
116117
final typeName = e.matchGroups?.elementAt(1);
117118
return '() { final a = letMapOrNull<String, dynamic>(${e.name}); return a != null ? $typeName.fromJson(a): null; }()';
118119
},
119120

120121
// ─── DDL-only — forward to Core's behaviour for the bare type ────
121-
r'^SQLITE_(?:text|varchar|char|clob)' +
122-
_sqliteTail +
123-
r'-(String)\??$': (e) {
122+
r'^SQLITE_(?:text|varchar|char|clob)' + _sqliteTail + r'-(String)\??$':
123+
(e) {
124124
return '${e.name}?.toString().trim().nullIfEmpty';
125125
},
126126
r'^SQLITE_integer' + _sqliteTail + r'-(int)\??$': (e) {
@@ -163,15 +163,15 @@ class DartSqliteTypeMappers extends TypeMappers {
163163
r'^SQLITE_julianday' + _sqliteTail + r'-(DateTime)\??$': (e) {
164164
return '${e.name} != null ? (${e.name}!.toUtc().millisecondsSinceEpoch / 86400000) + 2440587.5 : null';
165165
},
166-
r'^SQLITE_(?:json|jsonb)' + _sqliteTail + r'-(Model-?\w+|\w+-?Model)\??$':
167-
(e) {
166+
r'^SQLITE_(?:json|jsonb)' +
167+
_sqliteTail +
168+
r'-(Model-?\w+|\w+-?Model)\??$': (e) {
168169
return '${e.name} != null ? jsonEncode(${e.name}!.toJson()) : null';
169170
},
170171

171172
// ─── DDL-only ────────────────────────────────────────────────────
172-
r'^SQLITE_(?:text|varchar|char|clob)' +
173-
_sqliteTail +
174-
r'-(String)\??$': (e) {
173+
r'^SQLITE_(?:text|varchar|char|clob)' + _sqliteTail + r'-(String)\??$':
174+
(e) {
175175
return '${e.name}?.trim().nullIfEmpty';
176176
},
177177
r'^SQLITE_integer' + _sqliteTail + r'-(int)\??$': (e) {

lib/src/extract_insights_from_file.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ Future<List<ClassInsight<GenerateDartModel>>> extractInsightsFromFile(
5151
final fullPathName = params.fullFilePath;
5252
final fileName = p.basename(fullPathName);
5353
final dirPath = p.dirname(fullPathName);
54-
final supertypeName = _extractSupertypeName(fullPathName, params.className);
54+
final supertypeName =
55+
_extractSupertypeName(fullPathName, params.className);
5556
final insight = ClassInsight<GenerateDartModel>(
5657
className: params.className,
5758
annotation: temp,
@@ -257,9 +258,9 @@ String? _extractSupertypeName(String fullPathName, String className) {
257258
// The `extends` clause is optional (a class without it has no specific
258259
// supertype to inherit from); we only return a name when it's present.
259260
final pattern = RegExp(
260-
r'(?:^|\n)\s*(?:abstract\s+)?(?:base\s+|final\s+|sealed\s+|interface\s+|mixin\s+)?class\s+'
261-
+ RegExp.escape(className) +
262-
r'\b(?:<[^>]*>)?\s+extends\s+([A-Za-z_][\w]*)',
261+
r'(?:^|\n)\s*(?:abstract\s+)?(?:base\s+|final\s+|sealed\s+|interface\s+|mixin\s+)?class\s+' +
262+
RegExp.escape(className) +
263+
r'\b(?:<[^>]*>)?\s+extends\s+([A-Za-z_][\w]*)',
263264
);
264265
final match = pattern.firstMatch(src);
265266
return match?.group(1);

lib/src/generate_dbml.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,12 @@ Future<void> generateDbml(
3939

4040
final parser = CliParser(
4141
title: 'dev-cetera.com',
42-
description:
43-
'Generates a DBML schema file from classes annotated with '
42+
description: 'Generates a DBML schema file from classes annotated with '
4443
'@GenerateDartModel. The PG_*-/SQLITE_*- prefixes on fieldType drive '
4544
'column types, `references:` drives FK Ref lines, and `primaryKey:` '
4645
'drives the `[pk]` constraint.',
4746
example: 'generate-dbml -i lib/src/db_models -o schema.dbml',
48-
additional:
49-
'For contributions, error reports and information, visit: '
47+
additional: 'For contributions, error reports and information, visit: '
5048
'https://github.com/dev-cetera.',
5149
params: [
5250
DefaultFlags.HELP.flag,

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dependencies:
4343
df_config: ^0.8.0
4444
df_gen_core: ^0.8.0
4545
df_generate_dart_models_core: ^0.10.0
46-
df_log: ^0.5.0
46+
df_log: ^0.5.1
4747
df_string: ^0.3.0
4848
df_type: ^0.16.0
4949

test/dart_composite_type_mappers_test.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ class _Sentinel extends TypeMappers {
3636
newTypeMap({});
3737

3838
@override
39-
TTypeMappers<CollectionMapperEvent> get collectionToMappers =>
40-
newTypeMap({});
39+
TTypeMappers<CollectionMapperEvent> get collectionToMappers => newTypeMap({});
4140

4241
@override
4342
TTypeMappers<MapperEvent> get objectFromMappers => newTypeMap({

test/dart_core_type_mappers_test.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,7 @@ void main() {
227227
expect(r, contains('toUtc()'));
228228
});
229229

230-
test('PG_jsonb-ModelXxx decodes via letMapOrNull then nested fromJson',
231-
() {
230+
test('PG_jsonb-ModelXxx decodes via letMapOrNull then nested fromJson', () {
232231
final r = mapFrom('PG_jsonb-ModelBulletinPage', name: 'v');
233232
expect(r, contains('letMapOrNull'));
234233
expect(r, contains('ModelBulletinPage.fromJson(a)'));
@@ -347,7 +346,9 @@ void main() {
347346
group('Tier 2 Flutter geometry types', () {
348347
test('Offset uses dx/dy map', () {
349348
expect(
350-
mapFrom('Offset'), contains("Offset(letAsOrNull<double>(a['dx'])"),);
349+
mapFrom('Offset'),
350+
contains("Offset(letAsOrNull<double>(a['dx'])"),
351+
);
351352
expect(mapTo('Offset'), "x != null ? {'dx': x!.dx, 'dy': x!.dy} : null");
352353
});
353354

@@ -501,7 +502,8 @@ void main() {
501502
expect(r, contains('v!.toUtc()'));
502503
});
503504

504-
test('FS_server_timestamp-DateTime emits FieldValue.serverTimestamp() on '
505+
test(
506+
'FS_server_timestamp-DateTime emits FieldValue.serverTimestamp() on '
505507
'write', () {
506508
expect(
507509
mapTo('FS_server_timestamp-DateTime', name: 'v'),

0 commit comments

Comments
 (0)