NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #4331 most downloaded on pub.dev
Bones_API - A powerful API backend framework for Dart. It comes with a built-in HTTP Server, route handler, entity handler, SQL translator, and DB adapters.
Last release 4 days ago
04 Oct 2026
Ships fairly regularly
a new release about every 3 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
5 years old
355 releases · first in 2021
benchmark/db_users : a Users API benchmark sub-project — an APIRoot with reflection-wired modules and Role / Address / User entities, measured end to
benchmark/db_users: a Users API benchmark sub-project — an APIRoot with
reflection-wired modules and Role/Address/User entities, measured end
to end against memory, SQLite or PostgreSQL (--docker or an existing
server), with latency percentiles and a results history (HISTORY.md).
Performance, found and measured with benchmark/db_users:
BEGIN/COMMIT — a select of an entity with references wasBEGIN, its queries and COMMIT. Its first write opens the transaction.DBSQLAdapter.readsOutsideTransaction, on by default for PostgreSQLREAD COMMITTED), opt-out with readsOutsideTransaction: false.DBSQLMemoryAdapter: relationship selects and inserts go through theFixes:
LoggerHandler.root threw a LateInitializationError when it was theDBSQLMemoryAdapter: after a rollback, a relationship restored in itsSetMapHistory can't roll back), so queries throughDependency updates:
reflection_factory: ^2.10.1 (faster constructor lookup when decodingmap_history: ^1.0.7 (consolidate visits only the changed keys: theOne column per quarter.
APITestConfigDockerDB (and the PostgreSQL and MySQL configs):
APITestConfigDockerDB (and the PostgreSQL and MySQL configs):
dockerChosenPort: Docker chooses a free host port for the databaseport of the DB<prefix>_<session>_<n>, so a container left by an interrupted runrunOptions: further docker run options for the containerDockerRunOptions: tmpfs, labels, resources, health check...).APITestConfigDockerPostgreSQL: settings (each -c key=value),
initdbArgs, extraEnvironment, and ephemeral (a throwaway database:
durability off, data directory in memory).
APITestConfigDockerMySQL: settings, extraEnvironment and ephemeral.
Dependency updates:
docker_commander: ^3.1.0A route parameter typed as an enum no longer fails when the value arrives as a qualified name (Currency.brl), the form an enum.toString() produces and
A route parameter typed as an enum no longer fails when the value arrives as
a qualified name (Currency.brl), the form an enum.toString() produces and
the one external callers tend to send:
type 'String' is not a subtype of type 'Currency?' of 'chargedCurrency'
EnumReflection.from only read a bare value name, so the qualified form
parsed to null, APIRouteBuilder.resolveValueByType fell back to the value
it was given, and the raw String reached Function.apply. The bare name
(brl), in any case, already resolved.
deps: reflection_factory: ^2.10.0, which resolves the qualified enum name.
Added the cross_origin configuration entry, grouping every cross-origin response header and splitting it by the kind of response it applies to, the sa
Added the cross_origin configuration entry, grouping every cross-origin
response header and splitting it by the kind of response it applies to, the
same shape the cache entry already uses:
cross_origin:
api:
allow_origin: ''
allow_methods: 'GET,HEAD,PUT,POST,PATCH,DELETE,OPTIONS'
allow_headers: 'Content-Type, Access-Control-Allow-Headers, Authorization'
allow_credentials: true
expose_headers: 'Content-Length, Content-Type, Last-Modified, X-Access-Token, X-Access-Token-Expiration'
max_age: 0
vary_origin: true
static_files:
opener_policy: 'same-origin-allow-popups'
embedder_policy: ''
resource_policy: ''The split is not cosmetic: CORS is set on API responses only, while the
cross-origin isolation policies are set on static file responses only, and
the two never meet on the same response.
Every entry is also a command-line option
(--cross-origin-api-max-age, --cross-origin-static-files-opener-policy,
...), and an APICrossOriginConfig can be passed straight to APIServer.
See APICORSConfig and APICrossOriginPolicies.
Until now all of these values were hardcoded in APIServer.setCORS, with no
way to change them short of rewriting the response.
Behavior change: static text/html responses are now served with
Cross-Origin-Opener-Policy: same-origin-allow-popups.
It severs the opener relationship with a cross-origin document that opened
the page — the isolation of same-origin — while still allowing the popups
the page itself opens to keep a handle back to it. That is what popup-based
sign-in flows need (Sign in with Google, OAuth popups): the popup reports
its result by calling back into its opener.
A document served by this server that is itself opened as a cross-origin
popup and calls window.opener (an OAuth callback landing page) must now opt
out with opener_policy: none.
COOP and COEP are only sent for text/html, since they are document
headers, while CORP applies to every static file. COEP and CORP are
disabled by default.
Behavior change: API responses are now served with Vary: Origin.
Access-Control-Allow-Origin reflects the request Origin, and without
Vary a shared cache is free to serve one origin's
Access-Control-Allow-Origin to another. Disable with vary_origin: false.
allow_origin accepts an allowlist. When set, the request Origin is
reflected only if it matches, otherwise no Access-Control-Allow-Origin is
sent and the browser blocks the cross-origin read.
Worth noting for anyone leaving it empty: reflecting any origin together with
Access-Control-Allow-Credentials: true — the behavior before this version,
and still the default — lets any site make credentialed calls to the API and
read the responses. The browser only rejects that pairing for a literal *.
Access-Control-Max-Age is now available through max_age, sent only on
OPTIONS responses, the only ones a browser caches. Defaults to 0, which
omits the header.
Fixed: a cookieless server did not apply the cookieless guarantee to static
file responses.
Set-Cookie was dropped and X-Cookieless-Server: Blocking all cookies was
added by the API response builder, but a static file response is built by the
shelf handler and returned before reaching it. So the header was missing
from every static file, and nothing enforced the absence of Set-Cookie
there. Both now happen for static files as well, including the non-2xx
responses.
The placeholder pruning added in 1.15.1 no longer runs on every encoded condition.
The placeholder pruning added in 1.15.1 no longer runs on every encoded
condition.
Rewriting field == ? bound to null into field IS NULL leaves that
parameter unreferenced, and 1.15.1 found it by materializing the encoded
output and scanning it for every placeholder. That cost was paid by every
query, including the overwhelmingly common one that compares nothing against
null: ~0.30us per encoded condition (measured on a 3-placeholder
condition), against the ~0.90us of a whole logged route call after the 1.15.0
dispatch work.
Only a placeholder actually rewritten to IS NULL/IS NOT NULL can become
unreferenced, so those keys are now recorded as they are written, and a
condition that compares nothing against null returns immediately — without
materializing the output or scanning it. The output is materialized lazily
even then, since a recorded key may still be referenced by another operator.
No behaviour change: same statements, same bound parameters.
Fixed: a condition comparing a field to null passed as a parameter was encoded as field = ? bound to null . = NULL is never true in SQL, so the query
Fixed: a condition comparing a field to null passed as a parameter was
encoded as field = ? bound to null. = NULL is never true in SQL, so the
query returned no rows instead of the rows whose column is null.
ConditionSQLEncoder did turn =/IN against null into IS NULL (and
!=/NOT IN into IS NOT NULL), but only when the null was written
straight into the statement. A null arriving as a parameter is encoded as a
placeholder, whose text never equals 'null', so the conversion was skipped.
Entity queries take the parameter form, which is why it surfaced there:
// Returned [] with matching rows present; now returns the rows whose
// `state` is null.
repository.selectByQuery(' state == ? && active == ? ',
parameters: {'state': null, 'active': true});The encoder is shared, so this affected every SQL adapter — SQLite,
PostgreSQL and MySQL alike — and any condition compared against a null
parameter, including compound ones whose other terms matched.
Rewriting the comparison also leaves the parameter unmentioned by the
statement, so it is now dropped once the condition is encoded: PostgreSQL
rejects a statement carrying variables it does not use. A placeholder still
referenced by another operator (field > ? bound to null) keeps its binding.
Covered now by the shared adapter test suite, so all three adapters exercise
it.
reflection_factory: ^2.8.1 → ^2.9.0.
dart_style to the formatter bundled with Dart 3.12, so on*.reflection.g.dart no longer matched thisdart format, making dart format --set-exit-if-changed andtest/ensure_build_test.dart mutually exclusive. 2.9.0 tracks theFaster request dispatch. A logged route call is ~2.9x faster (measured in-process, APIRoot.call on a trivial route: 2.76us -> 0.90us).
Faster request dispatch. A logged route call is ~2.9x faster
(measured in-process, APIRoot.call on a trivial route: 2.76us -> 0.90us).
LoggerHandler no longer builds the formatted log message when nothingZone lookup for the current APIRequest id — and then discarded when nologAllTo/logErrorTo/logDbTo/console) was configured,CALL> and RESPONSE>).APIRouteHandler caches its CALL> message and RESPONSE> prefix. Bothparameters Map).APIRoot._callImpl no longer copies the path parts list just to read theAPIServer.toAPIRequest no longer copies the query-parameters Map aThe routes builder now accepts config: on any/get/post/put/
delete/patch/head, matching APIModule.addRoute. Previously an
APIRouteConfig could only be set through addRoute, so per-route logging
could not be turned off through the usual API:
routes.get('ping', handler, config: const APIRouteConfig(log: false));Route logging is on by default and costs roughly 4x the rest of a trivial
dispatch, so this is worth setting on hot routes.
New benchmark/ suites, with layered breakdowns so a regression can be
attributed rather than just observed. See benchmark/README.md.
dart run benchmark/bones_api_benchmark.dart # request path
dart run benchmark/json_benchmark.dart # JSON request/response
dart run benchmark/db_benchmark.dart # DB entity path
They record that query parsing is well cached (~300x cheaper than parsing)
and SQL generation is under a microsecond. JSON encoding already runs close
to a bare dart:convert encode, and request bodies use dart:convert
directly, so no JSON optimization came out of that suite.
DBSQLMemoryAdapter now answers a select by ID with a direct lookup in the
table Map, which is already keyed by ID, instead of scanning it. A miss
still falls through to the scan, so results are unchanged.
selectByID was O(rows) and is now flat: 7.8us -> 7.1us at 10 rows,
9.7us -> 7.2us at 50, and 25.3us -> 7.2us at 400. This mostly speeds up the
test suite and development, since the memory adapter is where those run.
New DBSQLiteAdapter : an embedded SQLite DB adapter, backed by the sqlite3 package.
New DBSQLiteAdapter: an embedded SQLite DB adapter, backed by the
sqlite3 package.
import 'package:bones_api/bones_api_db_sqlite.dart';
var adapter = DBSQLiteAdapter('/var/lib/myapp/db.sqlite',
generateTables: true);
// Or an in-memory database:
var memoryAdapter = DBSQLiteAdapter(':memory:', generateTables: true);Registered as sqlite, sqlite3, sql.sqlite and sql.sqlite3, so a
config block db: { sqlite: {...} } resolves it.
fromConfig accepts path/file/database/db for the database file,
and memory: true (or the path :memory:) for an in-memory database, plus
the usual generateTables/checkTables/populate/log.sql keys.
Irrelevant keys (host, port, username, password) are accepted and
ignored, so a config can be pointed at SQLite without being rewritten.
No server and no native library to install: the sqlite3 package
bundles SQLite (3.53.4) through Dart's build hooks.
Runs the same entity test-suite as the PostgreSQL and MySQL adapters, and
needs no Docker container to do it. New APITestConfigSQLite, exported by
package:bones_api/bones_api_test_sqlite.dart.
Notes on the SQLite dialect:
INTEGER PRIMARY KEY AUTOINCREMENT:SERIAL/AUTO_INCREMENT, only a column declared exactlyINTEGER PRIMARY KEY aliases the rowid, and without AUTOINCREMENTENUM is emulated with a VARCHAR CHECK (col IN (...)) constraint.sqlite3 is a synchronous driver, and SQLite allows a singleSAVEPOINT.New SQLDialect.returningAcceptsTableWildcard (default true, so the
PostgreSQL/MySQL/memory dialects are unchanged). SQLite rejects the
table-qualified wildcard that DELETE ... RETURNING emits
("RETURNING may not use TABLE.* wildcards") and needs a bare
RETURNING *.
Fixed DBObjectDirectoryAdapter losing objects written just before a read:
_saveObject was async and its Future was dropped by doInsert/
doUpdate, while every reader in the adapter inspects the filesystem
synchronously. A store could therefore return before its object was on
disk, and selectAll would silently omit it (a not-yet-written file reads
back as null and was discarded). The write is now synchronous.
Breaking: the minimum Dart SDK is now 3.10.0 (was 3.7.0), required by
sqlite3 and its build hooks.
Dependencies:
sqlite3: ^3.5.1New EntityPagination.onEvent : an optional hook notified of what is being fetched, for progress reporting and logging.
New EntityPagination.onEvent: an optional hook notified of what is being
fetched, for progress reporting and logging.
var p = userRepository.paginateByQuery(' state == ? ',
parameters: ['NY'], limit: 20, onEvent: (event) {
switch (event) {
case EntityPaginationPageLoading(:var page):
print('fetching page $page...');
case EntityPaginationPageLoaded(:var page, :var entriesLength):
print('page $page: $entriesLength entries');
case EntityPaginationPageError(:var page, :var error):
print('page $page failed: $error');
case EntityPaginationPageSkipped(:var page, :var reason):
print('page $page not fetched: ${reason.name}');
case EntityPaginationEnd(:var totalLength):
print('done: $totalLength entries');
case EntityPaginationReset(:var discardedPages):
print('discarded ${discardedPages.length} pages');
}
});EntityPageLoader (a Stream wouldonEvent: myEventStream.add.onEvent is not final, so it can also be attached to an already builtEntityPagination. Only events emitted afterwards are seen.Zone andonEvent is null.New EntityPaginationEvent<O>, a sealed hierarchy so a switch over it is
exhaustive, with EntityPaginationListener<O> as the callback type:
EntityPaginationPageLoading: a fetch is about to start. Emitted once perEntityPaginationPageLoaded: a fetch finished, with the entries, theentriesLength, the elapsedTime of the pageLoader and isFinalPage.EntityPaginationPageError: a fetch failed, with the error, thestackTrace and the elapsedTime. The error is rethrown to the callerEntityPaginationPageSkipped: a page was served without a fetch, with anEntityPaginationSkipReason: alreadyLoaded, inFlight (a concurrentknownEmpty (past the resolved end). Not anEntityPaginationEnd: the end was resolved, with the finalPage and thetotalLength. Emitted once, immediately after theEntityPaginationPageLoaded that resolved it — which is not necessarilyEntityPaginationReset: reset() or refresh() discarded the loadeddiscardedPages, the discardedEntitiesLength andisRefresh — true while it is the reset of a refresh(), whichEvery event but EntityPaginationReset is about a page, and is an
EntityPaginationPageEvent (also sealed) carrying the page.
Note that concurrent page loads interleave: getRange and refresh start
every page at once, so all the fetches are announced before any completes.
paginateByQuery, paginate and paginateAll gained the optional onEvent
parameter, on EntitySource, EntityRepository and APIRepository, so the
hook is reachable without building an EntityPagination by hand.
Tests: 15 new cases in bones_api_entity_pagination_test.dart (the event
sequence of a full read, of an exact multiple of the page size, of an empty
result and of a failure; the 3 skip reasons; the synchronous delivery; a
listener attached after construction; a throwing listener; and the
reset/refresh events).
New EntityPagination<O> : a lazily loaded, paginated view over a select, for reading a result page by page without knowing its total length upfront.
New EntityPagination<O>: a lazily loaded, paginated view over a select,
for reading a result page by page without knowing its total length upfront.
var p = userRepository.paginateByQuery(' state == ? ',
parameters: ['NY'], limit: 20);
await p.loadNextPage(); // page 1
p[0]; // sync, already loaded
await p.getAt(45); // loads page 3 on demand, leaving page 2 a gap
await p.loadAll(); // fills the gaps and resolves the totalpage parameter of the select*List). SeeindexOfPage / pageOfIndex.getAt(45) with a limitoperator [], loadedEntities) never fetches;FutureOr methods (getAt, getPage, getRange,loadNextPage, loadPage, loadAll, stream) do. operator [] returnsnull for a gap, an unloaded page or an out-of-range index alike; useisPageLoaded / isIndexKnownOutOfRange to tell them apart.List or an Iterable: both require alength, which is exactly what a paginated select can't answer until itloadAll when a complete list is really needed.What it knows: loadedPages, loadedPagesLength, loadedEntities,
loadedEntitiesLength, maxLoadedPage, maxLoadedIndex, maxKnownPage,
isFinalPageResolved, finalPage, totalLength, isKnownEmpty,
and information().
Since every page except the last holds exactly limit entries, identifying
the final page yields the total even with gaps:
totalLength == (finalPage - 1) * limit + entries(finalPage).
The end resolves when a page comes back short, when an empty page has a
loaded and full predecessor, or when page 1 comes back empty. An empty page
without a loaded predecessor does not resolve it — jumping to page 50
of a 3-page result only proves the end is somewhere before page 50 — but it
is still recorded, to avoid re-fetching that page or any page after it.
Concurrent requests for the same page share a single fetch, and a failed
load is evicted so a retry actually retries.
New paginateByQuery, paginate and paginateAll on EntitySource,
EntityRepository (with resolutionRules) and APIRepository. They return
immediately without loading anything. orderByID defaults to true there,
rather than following the offset != null rule of the select* methods:
a paginated read is only meaningful over a stable order.
Note: each page is an independent select, without a shared Transaction.
Entries inserted or deleted between two page loads shift the offsets, so a
page loaded later can repeat or skip entries. This is inherent to
offset-based pagination; ordering by ID makes it as stable as it can be.
selectByQuery and its siblings gained 4 optional parameters, for pagination and ordering:
selectByQuery and its siblings gained 4 optional parameters, for pagination
and ordering:
offset: the return offset.page: the 1-based page to return, an ergonomic alternative to offset(page - 1) * limit.orderByID: orders the result by the table's ID column, resolvedTableScheme.idFieldNameEncodingContext.tableFieldID, or EntityHandler.idFieldName).orderDirection: the new OrderDirection enum, ascending (default) ordescending.Semantics:
orderByID ?? (offset != null): a non-nulloffset turns the ordering on by default, since an offset-basedorderByID: false toOFFSET.orderDirection is ignored while the ordering is not active.page is a public convenience resolved to an offset at the repositoryresolveSelectOffset); the adapter contract keeps taking onlyoffset. It throws an ArgumentError when combined with an offset (twolimit to use as the< 1. page: 1 resolves to offset: 0, whichAdded to EntitySource/EntityRepository (selectByQuery,
selectFirstByQuery, select, selectIDsByQuery, selectIDsBy,
selectAll), APIRepository, IterableEntityRepository
(matches/all included), DBEntityRepository, DBRelationalAdapter/
DBRelationalRepositoryAdapter/DBRelationalEntityRepository,
DBAdapter.doSelectAll/doSelectByIDs, DBSQLAdapter.doSelect/
doSelectIDsBy/generateSelectSQL/generateSelectIDsSQL and
DBSQLRepositoryAdapter.generateSelectSQL.
New OrderDirection enum (bones_api_types.dart), with sqlKeyword,
parse and the resolvers resolve and resolveOrderByID that state the
semantics above exactly once.
New compareEntityIDs and applySelectOrderAndPagination
(bones_api_entity.dart): the shared Dart-side "order by ID → skip → take"
used by every adapter that can't delegate the ordering to a DB engine.
New resolveSelectOffset (bones_api_entity.dart): resolves page to an
offset, and states the page/offset/limit validation rules once.
SQLDialect:
orderBySQL and limitOffsetSQL clause builders, so all theSELECT tail syntax lives in one place.offsetRequiresLimit and offsetMaxLimitValue capabilities. MySQL setsoffsetRequiresLimit: true since it can't parse an OFFSET that is notLIMIT; an offset-only select there emitsLIMIT 18446744073709551615 OFFSET n. PostgreSQL and the genericOFFSET n.SQL: new offset, orderByID and orderDirection fields (carried by
copy()), read by DBSQLMemoryAdapter to apply the same semantics in Dart.
APIDBModule.select (/db/select/<table>): new LIMIT=<n>, OFFSET=<n>,
PAGE=<n> and ORDER=asc|desc query directives
(see APIDBModule.selectQueryDirectives),
parsed from the query String alongside the pre-existing EAGER=true and
stripped before the remainder is parsed as the entity condition query. The
endpoint no longer selects the whole table and sorts it in Dart — the ordering
is now resolved by the DB. Its output order is unchanged. An invalid PAGE
becomes an error response rather than an uncaught ArgumentError.
Behavior change: limit is now honored on the paths that previously
accepted and silently ignored it — DBEntityRepository.select's
ConditionID/ConditionIdIN/ConditionANY/KeyConditionEQ fast paths,
DBAdapter.doSelectAll/doSelectByIDs, and the DBObjectMemoryAdapter,
DBObjectDirectoryAdapter and DBObjectGCSAdapter adapters. For example,
selectAll(limit: 2) on an object adapter returned every row before this
release; it now returns 2.
Source-breaking for external subclasses: new named parameters were added
to abstract members (EntitySource.select/selectIDsBy/selectAll,
DBAdapter.doSelectAll/doSelectByIDs,
DBRelationalAdapter.doSelect/doSelectIDsBy). Dart requires an override to
accept every named parameter of the supertype, so third-party
EntityRepository/DBAdapter implementations must widen their overrides.
Known limitation: a query over a to-many relationship generates a JOIN
without a DISTINCT, so it can return the same entity more than once
(pre-existing). Paginating such a query is therefore best-effort.
Tests:
bones_api_entity_select_order_test.dart (OrderDirection,compareEntityIDs, applySelectOrderAndPagination, SQLDialect clausebones_api_entity_db_sql_select_test.dart (exact generated SQLbones_api_db_module_test.dart (first coverage of APIDBModule).bones_api_entity_db_tests_base.dart: 3 new tests in the shared adapterdocker_commander : ^2.1.8 → ^3.0.0 .
docker_commander: ^2.1.8 → ^3.0.0.
wasm_run and flutter_rust_bridge 1.x from the dependency graphdocker_commander → apollovm, and were only ever neededshelf_web_socket ^1.0.2 andweb_socket_channel ^2.2.0, so every bones_api application was lockeddocker_commander's own API is unchanged, so this is a minor release: theDockerHost types exposed by the test utils keep the same shape.petitparser: ^6.1.0 → ^7.0.2 (required by apollovm 2.0.0).
JsonGrammarLexer.token: flatten() takes its message as a namedConditionSQLEncoder.valueToParameterValue : fixed encoding of a List of values containing ConditionParameter s; each element is now resolved individua
Bug fixes:
ConditionSQLEncoder.valueToParameterValue: fixed encoding of a List of values containing ConditionParameters; each element is now resolved individually instead of passing the whole list to every element.ConditionEncoder.resolveValueToType: fixed resolution of a single-element Iterable to a primitive type (was a no-op comparison instead of an assignment, leaving the value as a List).MapGetterExtension.matchKeyIgnoreCase: fixed case-insensitive key matching that always returned null (empty loop body); now returns the matching key. Also fixes setMultiValue(..., ignoreCase: true).Time: millisecond/microsecond range validation now correctly rejects 1000 (valid range is 0..999).Time._bytesInStringFormat: fixed the second-byte digit check that was effectively disabled (length < 2 instead of length >= 2).APISession.isExpired: now honors the provided now argument instead of always using DateTime.now().APIServerResponseCache cached entry: replaceFileStat no longer compares a variable to itself (identical(myFileStat, myFileStat)), so the file stat is correctly replaced.WeakList.set: now increments the internal modification counter, consistent with the other mutating methods.Tests:
Time range/string parsing, matchKeyIgnoreCase/getIgnoreCase/setMultiValue, APISession.isExpired, and ConditionSQLEncoder/ConditionEncoder value resolution).Added logging of severe errors when apiRoot.ensureInitialized() returns a failure with an error.
v1.9.30
APIRootStarter:
start:
apiRoot.ensureInitialized() returns a failure with an error.Project template:
update_project_template.sh:
project_template prepare command to exclude IDE module files matching ^\w+\.iml$ from the template archive.Dependencies:
build_runner to ^2.15.0.test to ^1.31.1.vm_service to ^15.2.0.Added method resolveIDValue to resolve the ID value from parameters or ConditionParameter.
ConditionID:
resolveIDValue to resolve the ID value from parameters or ConditionParameter.DBEntityRepository:
selectIDsBy and _selectByID to use ConditionID.resolveIDValue for ID resolution.select:
KeyConditionEQ matcher with a single key matching the entity ID field._selectByID to fetch the entity by ID and returns a single-element list or empty list accordingly.DBObjectDirectoryAdapter:
_doCountImpl and _doDeleteImpl to use ConditionID.resolveIDValue for ID resolution.parameters ?? namedParameters) to internal implementations.DBObjectGCSAdapter:
_doCountImpl and _doDeleteImpl to use ConditionID.resolveIDValue for ID resolution.parameters ?? namedParameters) to internal implementations.DBObjectMemoryAdapter:
_doCountImpl and _doDeleteImpl to use ConditionID.resolveIDValue for ID resolution.parameters ?? namedParameters) to internal implementations.Dependency updates:
vm_service: ^15.0.2 → ^15.1.0Fixed referenceTable and referenceColumn assignment in unique constraint SQL entries to allow nullable references.
SQLGenerator:
referenceTable and referenceColumn assignment in unique constraint SQL entries to allow nullable references.generateAddUniqueConstraintAlterTableSQL and generateAddEnumConstraintAlterTableSQL to use normalized column names with double underscores for consistency.Dependency updates:
async_extension: ^1.2.22reflection_factory: ^2.7.5swiss_knife: ^3.3.14meta: ^1.18.2hotreloader: ^4.4.0googleapis_auth: ^2.3.0build_runner: ^2.13.1test: ^1.31.0…bones_api_utils_fast_checksum.dart instead of deprecated Adler32 and Crc32 classes.
Added bones_api_utils_fast_checksum.dart:
getAdler32Uint8List, getAdler32Hex, getCrc32Uint8List, and getCrc32Hex for Adler-32 and CRC-32 checksums as byte arrays and hex strings.getAdler32 and getCrc32 from archive package for checksum calculation.WeakEtag class (bones_api_base.dart):
WeakEtag.adler32 and WeakEtag.crc32 factories to use getAdler32Hex and getCrc32Hex from bones_api_utils_fast_checksum.dart instead of deprecated Adler32 and Crc32 classes.bones_api.dart:
bones_api_utils_fast_checksum.dart utility.Dependencies:
async_extension from ^1.2.20 to ^1.2.21.swiss_knife from ^3.3.3 to ^3.3.5.archive from ^4.0.7 to ^4.0.9.build_runner from ^2.10.5 to ^2.11.1.ensureInitialized: added onError handler to then call to route errors to _onInitializationError.
Initializable mixin:
ensureInitialized: added onError handler to then call to route errors to _onInitializationError.executeInitializedCallback:
onError handler to then call on async initialization result to throw InitializationError with stack trace._FutureExtension:
toCompleter: added onError handler to then to complete completer with error and stack trace if not completed.Added nullable field indexName to represent the name of the index if one exists.
TableFieldReference:
indexName to represent the name of the index if one exists.Added new class TableRelationshipReferenceEntityTyped extending TableRelationshipReference:
sourceFieldEntityType and targetFieldEntityType fields of type TypeInfo.copyWithEntityTypes method to create typed copies.TableRelationshipReference:
sourceRelationshipFieldIndex and targetRelationshipFieldIndex.copyWithEntityTypes method returning TableRelationshipReferenceEntityTyped.EntityHandler:
getFieldsListEntityTypes method to return a map of fields that are list entities or references with their TypeInfo.SQLDialect:
foreignKeyCreatesImplicitIndex boolean flag with default true.createIndexIfNotExists to indicate support for IF NOT EXISTS in CREATE INDEX (default true).CreateIndexSQL:
buildSQL method to conditionally include IF NOT EXISTS only if dialect supports it.DBPostgreSQLAdapter:
foreignKeyCreatesImplicitIndex flag to PostgreSQL dialect set to false._findAllTableFieldsReferences query to include foreign key index name (fk_index_name) by joining with pg_index and pg_class.indexName in TableFieldReference instances from query result.sourceRelationshipFieldIndex and targetRelationshipFieldIndex from indexName.DBMySQLAdapter:
createIndexIfNotExists to false in MySQL dialect capabilities.DBSQLAdapter:
parseConfigDBGenerateTablesAndCheckTables: changed return type from List<bool> to a record with named fields (generateTables, checkTables).extractTableSQLs: updated regex to also match CREATE INDEX statements in addition to CREATE and ALTER TABLE._populateTablesFromSQLsImpl: fixed error handling for CREATE INDEX statements when the SQL dialect does not support IF NOT EXISTS.
_checkDBTableSchemeReferenceField to return TableRelationshipReferenceEntityTyped with entity types._DBTableCheck class:
missingReferenceIndexes and missingRelationshipReferenceIndexes._DBRelationshipTableColumn subclass of _DBTableColumn to represent relationship table columns with relationship table name.generateAddColumnAlterTableSQL.Dependency updates:
async_extension: ^1.2.19 → ^1.2.20meta: ^1.18.0 → ^1.18.1Added override for addSlice to handle partial chunk addition and update _inputLength accordingly.
GZipSink:
addSlice to handle partial chunk addition and update _inputLength accordingly.addSlice to call _gzipSink.close() when isLast is true and full chunk is added.BytesSink:
addSlice to use new addPart method for partial chunk addition.BytesBuffer:
addPart method to add a slice of bytes from a given offset and length, resizing buffer if needed.add method to delegate to addPart.addPart.async_extension: ^1.2.18 -> ^1.2.19
mapDataTypeToDartType: added support for PostgreSQL types smallint and smallserial mapping to int.
DBPostgreSQLAdapter:
mapDataTypeToDartType: added support for PostgreSQL types smallint and smallserial mapping to int.Added calls to _forceLogFlushMessages() before throwing InitializationError in:
Initializable mixin:
_forceLogFlushMessages() before throwing InitializationError in:
_checkDependency_setInitializedDependenciesCompleters_onInitializationError_checkAllDependenciesOk_finalizeInitializationcheckInitializedexecuteInitializedLogging:
_forceLogFlushMessages() function to call logging.Logger.root.forceFlushMessages().Logger extension:
forceFlushMessages() method to invoke LoggerHandler.forceFlushMessages().LoggerHandler abstract class:
forceFlushMessages() method.LoggerHandlerGeneric implementation:
forceFlushMessages() returning false.LoggerHandlerIO implementation:
forceFlushMessages() to flush the print message queue immediately if not empty.Updated all Map.unmodifiable usages to explicitly specify type arguments, e.g. Map .unmodifiable.
EntityHandler:
Map.unmodifiable usages to explicitly specify type arguments, e.g. Map<String, TypeInfo>.unmodifiable.fieldsWithEntityReference, fieldsWithEntityReferenceList, fieldsEntityAnnotations, fieldsWithType, getFieldsTypes, getFieldsEnumTypes, getFieldsEntityTypes, and constructors to use typed unmodifiable maps.Dependency updates:
meta: ^1.18.0keyToSQL: added check to throw ConditionEncodingError if keys is empty.
ConditionSQLEncoder:
keyToSQL: added check to throw ConditionEncodingError if keys is empty.keyFieldReferenceToSQL to recursively resolve multi-level key references by walking keys and resolving intermediate tables and relationships._resolveReferenceField and _resolveFinalField to modularize reference resolution logic.DBSQLAdapter:
_JoinEntry typedef to represent SQL JOIN fragments with explicit alias dependencies (defs and refs).List<_JoinEntry> to perform dependency-aware sorting of JOINs ensuring referenced aliases are resolved before use._JoinEntry with defined and referenced aliases.Dependencies:
async_extension from ^1.2.17 to ^1.2.18.build_runner from ^2.10.4 to ^2.10.5.Updated reflection_factory dependency from ^2.7.2 to ^2.7.3.
reflection_factory dependency from ^2.7.2 to ^2.7.3.Improved error messages in instantiation methods to include a list of instantiator function keys.
DBAdapter:
Dependencies:
async_extension from ^1.2.15 to ^1.2.17.test from ^1.28.0 to ^1.29.0._resolvePayloadFromString: Improved JSON payload parsing:
APIServer:
_resolvePayloadFromString: Improved JSON payload parsing:
null for empty bodies.statistics: ^1.2.1
New FileLimited: expose Fili limit handling.
New FileLimited: expose Fili limit handling.
FileLimitExtension: use FileLimited.global.
APIServerResponseCache:
_fileLimited for file operations.File operations to prioritize async and limited operations.shelf_letsencrypt: ^2.0.3
build_runner: ^2.10.4
test: ^1.28.0
Added statLimited, deleteLimited.
FileLimitExtension:
statLimited, deleteLimited.DBObjectGCSAdapter:
deleteLimited() instead of delete()statLimited() instead of stat()Too many open files errors during cache cleanup and maintenance...Added readAsBytesLimited() and writeAsBytesLimited() methods to safely limit concurrent file I/O operations and prevent Too many open files errors.
FileLimitExtension:
readAsBytesLimited() and writeAsBytesLimited() methods to
safely limit concurrent file I/O operations and prevent Too many open files errors.DBObjectGCSAdapter:
FileLimitExtension.readAsBytesLimited() to control concurrent I/O and prevent
Too many open files errors during cache access.async_locks: ^4.0.2
build_runner: ^2.10.2
test: ^1.27.0
Fix calculation of needed deleting and extra 20%.
DBObjectGCSAdapter:
_checkCacheDirectoryLimit:
Added properties: cacheDevelopment, cacheFilesLimit, cacheCheckMaxSkips and cacheCheckTimeout.
DBObjectGCSAdapter:
cacheDevelopment, cacheFilesLimit, cacheCheckMaxSkips and cacheCheckTimeout.cacheDirectory on cacheDevelopment._checkCacheDirectoryLimit:
cacheFilesLimit.DBObjectDirectoryAdapter:
development.directory on development.reflection_factory: ^2.7.2
postgres: ^3.5.9
crypto: ^3.0.7
http: ^1.6.0
_discoveryapis_commons: ^1.0.7
build_runner: ^2.10.1
resolveFieldsValues: when resolving an EntityReference and the value can't be resolved, pass the ID to the EntityReference.
EntityHandler:
resolveFieldsValues: when resolving an EntityReference and the value can't be resolved, pass the ID to the EntityReference.resolveValueByType: optimize for null value.LoggerHandler:
_buildMsg: handle long debugName starting with test_suite:.ClassProxyListener:
onCall: On response error, throw an exception using response.stackTrace when available.- DBEntityRepository: - Optimize resolveEntities. - build_runner: ^2.7.1
DBEntityRepository:
resolveEntities.build_runner: ^2.7.1
Added notifyAPITokenInfoChange, disposeAuthenticationPermission, disposeAuthenticationDataAndPermission.
APISecurity:
notifyAPITokenInfoChange, disposeAuthenticationPermission, disposeAuthenticationDataAndPermission.APITokenStore:
removeTokenPermissions, removeTokenDataAndPermissions.APIRequest:
APIRequest and getPayloadParameterIgnoreCase.Comment: dependency_validator: ^4.1.3.
sdk: '>=3.7.0 <4.0.0'
reflection_factory: ^2.6.0
collection: ^1.19.1
mime: ^2.0.0
http: ^1.5.0
build_runner: ^2.7.0
Comment: dependency_validator: ^4.1.3.
EntityAccessRules: added totalRules.
EntityAccessRules: added totalRules.
APIModule:
addRouteHandler.APIRouteBuilder:
addRouteHandler.apiMethod: optimize the built routeHandler.Now APIRouteHandler is abstract:
function.APIRouteHandlerFunction.MethodReflectionExtension:
returnsAPIResponse: do not accept dynamic.APIServer:
reflection_factory: ^2.5.3
Fix serverTimingEntryName default value from obj->json->gzip to obj-json-gzip.
APIServer:
_defineGZipEncodedHeaders:
serverTimingEntryName default value from obj->json->gzip to obj-json-gzip.Log errors while encoding payload to JSON.
APIServer:
_resolveBodyImpl:
OutOfMemoryError and log.apiResponse.asError on errors._jsonEncodePayload:
AutoGZipSink and Json.encodeToSink to stream JSON encoding with automatic GZip compression based on output size.Added AutoGZipSink, GZipSink and BytesSink and BytesBuffer.
Json:
encodeToSink.reflection_factory: ^2.5.2
swiss_knife: ^3.3.3
test: ^1.26.3
Main updates (see v1.9.4-beta.* for more):
Main updates (see v1.9.4-beta.* for more):
APIServerConfig:
defaultStaticFilesCacheControl: removed must-revalidate (conflicts with stale-while-revalidate).longLivedStaticFilesCacheControl and longLivedStaticFilesCached
/, /index.html, styles.css, /pwa_sw.jsapiConfig:CacheControl:
mustRevalidate from the default directives (conflicts with staleWhileRevalidate).coverage: ^1.15.0
Fix normalizeHeaderValue resolution when using apiConfig.
APIServerConfig:
normalizeHeaderValue resolution when using apiConfig.defaultLongLivedStaticFilesCacheControl: changed max-age from 86400 (1 day) to 3600 (1 hour).
APIServerConfig:
defaultLongLivedStaticFilesCacheControl: changed max-age from 86400 (1 day) to 3600 (1 hour).apiConfig:
cookieless, useSessionID.maxPayloadLength, decompressPayload.apiCacheControl, staticFilesCacheControl.longLivedStaticFilesCacheControl, longLivedStaticFilesCached.Removed mustRevalidate from the default directives (conflicts with staleWhileRevalidate).
CacheControl:
mustRevalidate from the default directives (conflicts with staleWhileRevalidate).APIServerConfig:
defaultStaticFilesCacheControl: removed must-revalidate (conflicts with stale-while-revalidate).longLivedStaticFilesCacheControl and longLivedStaticFilesCached
/, /index.html, styles.css, /pwa_sw.jsAPIServerResponseCache:
Cache-Control and Server.Last-Modified on 304 responses.DBEntityRepositoryProvider: check for duplicated repositories.
DBPostgreSQLAdapter:
postgres API v3.DBEntityRepositoryProvider: check for duplicated repositories.
APIServerConfig, APIServerWorker, APIServer:
maxPayloadLength and decompressPayload options for request handling.APIServer:
_loadPayloadBytes:
_decodePayloadGzip to handled GZip decompression and check the decompressed size in header before decompression.Time.parse: accept format Time(hh:mm:ss.sss)
Fix SQL column generation type if min/max is defined for the field.
postgres: ^3.5.6
Added _checkDuplicatedRepositories: check for duplicated repositores, by Type and name.
DBEntityRepositoryProvider:
_checkDuplicatedRepositories: check for duplicated repositores, by Type and name.Initializable:
_doInitializationImpl: add extra timeout when new parents are added.DBMySQLAdapter, DBPostgreSQLAdapter:
DBMySQLAdapter, DBPostgreSQLAdapter:
typeToSQLType: fix for int/BigInt ID (isID: true).Fix for int: use entityFieldAnnotations min/max to define SQL type (TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT).
DBMySQLAdapter:
typeToSQLType:
int: use entityFieldAnnotations min/max to define SQL type (TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT).BigInt and DynamicInt: DECIMAL(65, 0)DBPostgreSQLAdapter:
typeToSQLType:
int: use entityFieldAnnotations min/max to define SQL type (SMALLINT,INT,BIGINT).BigInt and DynamicInt: NUMERICAPIServerConfig, APIServerWorker, APIServer:
APIServerConfig, APIServerWorker, APIServer:
maxPayloadLength and decompressPayload options for request handling.APIServer:
_loadPayloadBytes:
_decodePayloadGzip to handled GZip decompression and check the decompressed size in header before decompression.GenericEntityHandler, ClassReflectionEntityHandler:
GenericEntityHandler, ClassReflectionEntityHandler:
getFieldType: if the field doesn't have a setter do not use cached fields types.meta: ^1.17.0
gcloud: ^0.8.19
http: ^1.4.0
googleapis_auth: ^2.0.0
test: ^1.26.2
coverage: ^1.14.1
vm_service: ^15.0.2
TypeInfoEntityExtension, TypeReflectionEntityExtension:
TypeInfoEntityExtension, TypeReflectionEntityExtension:
entityType: also handle List<E>, returning the List generic type (E).TypeInfoEntityExtension:
toCastedList.DBSQLAdapter:
_checkDBTableScheme:
referenceFields and collectionReferenceFields._DBTableCheck: added field missingCollectionReferenceColumns.EntityHandler:
resolveFieldsValues: ensure that List<E> fields are casted to the list, using entityType.toCastedList(val).New InitializationError.
Initializable: better handling of errors of dependencies while initializing.
async_extension: ^1.2.15
args: ^2.7.0
postgres: ^3.5.6
archive: ^4.0.7
coverage: ^1.12.0
Added fields username, host, port, database, secure.
DBPostgreSQLAdapter:
PostgreSQLConnectionWrapper:
_endpoint.username, host, port, database, secure.connectionURL: appended query string with sslmode._connectSSLImpl, _connectNoSSLImpl: simplify error handling.
DBPostgreSQLAdapter:
_connectSSLImpl, _connectNoSSLImpl: simplify error handling.Remove filed onlySecureConnections.
New DBAdapterConnectivity.
DBAdapter:
connectivity.DBPostgreSQLAdapter:
onlySecureConnections.connectivity field.New DBAdapterCapabilityConnectivity.
New DBAdapterCapabilityConnectivity.
DBAdapterCapability:
connectivity.DBPostgreSQLAdapter:
onlySecureConnections.dependency_validator: ^4.1.3
Time.parse: accept format Time(hhss.sss)
DBPostgreSQLAdapter:
postgres API v3.Time.parse: accept format Time(hh:mm:ss.sss)
postgres: ^3.5.4
project_template: ^1.1.1
archive: ^4.0.4
resolveFiledName: improve field matching.
FieldsFromMap:
resolveFiledName: improve field matching.EntityHandler
getFieldType: added parameter resolveFiledName: false.GenericEntityHandler, ClassReflectionEntityHandler:
- async_events: ^1.3.0 - reflection_factory: ^2.5.1 - web: ^1.1.1
Change use of dart:html (deprecated) to package web.
APIPlatformBrowser:
dart:html (deprecated) to package web.Json:
defaultFieldValueResolver: optimize primitives parsing (String, bool, int, double, num) .dumpRuntimeTypes.sdk: '>=3.6.0 <4.0.0'
reflection_factory: ^2.5.0
statistics: ^1.2.0
swiss_knife: ^3.3.0
yaml_writer: ^2.1.0
mercury_client: ^2.3.0
resource_portable: ^3.1.2
collection: ^1.19.0
web: ^1.1.0
- reflection_factory: ^2.4.10 - petitparser: ^6.1.0 - hotreloader: ^4.3.0 - stream_channel: ^2.1.4 - http: ^1.3.0 - lints: ^5.1.1 - build_runner: ^2.4
reflection_factory: ^2.4.10
petitparser: ^6.1.0
hotreloader: ^4.3.0
stream_channel: ^2.1.4
http: ^1.3.0
lints: ^5.1.1
build_runner: ^2.4.15
test: ^1.25.15
✨♻️ Improve cast method in APIResponse
Your coding agent can read these notes before it upgrades. Set up the MCP server →