tom_basics 3.3.0
tom_basics: ^3.3.0 copied to clipboard
Basic utilities for the TOM framework including exception handling with UUID tracking.
tom_basics #
Part of the Tom framework by al-the-bear. © 2024–2026 Peter Nicolai Alexis Kyaw — BSD-3-Clause, see LICENSE.
Basic utilities for the Tom framework including exception handling with UUID tracking.
tom_basics is the bottom of the dependency stack. It carries the few
primitives that almost every other Tom package imports — a traceable exception
base, a configurable logger, a platform-abstraction layer, and a small runtime
environment model — while keeping its own dependency list to just three
well-known packages (uuid, stack_trace, http). Nothing here pulls in
Flutter, a database, or a server framework, so it is safe to import from the
most foundational corners of the codebase.
Overview #
A foundational library has one job: give the layers above it a vocabulary they
can share without dragging in heavy dependencies. tom_basics provides four
such vocabularies, each independent of the others:
-
Errors that can be traced across process boundaries. A
TomBaseExceptionstamps every failure with a UUID at the moment it is created. When the same error is logged on a server, surfaced in a client, and filed in a bug tracker, the UUID is the thread that ties the three sightings together. The exception also captures a terse, core-folded stack trace (viastack_trace) so the noise of the SDK internals is stripped from the report. -
Logging you can dial up and down per class. The global
tomLoginstance offers nine severity methods (trace→fatalplusstatus) and a bitwise level model that lets you compose, subtract, and match levels. You can raise the verbosity of a single class or method without touching the global level, and you can redirect all output to a destination of your choosing by swapping one field. -
Platform detail behind a single seam. Code that needs to know "are we on the web?" or "give me an HTTP client" talks to
TomPlatformUtils.currentinstead of importingdart:ioordart:html. The concrete implementation is injected once at startup, so the same library code compiles and runs on console, server, mobile, and web. -
A runtime environment model for wiring.
TomEnvironment,TomPlatform, andTomRuntimedescribe which environment (development / test / production) and which platform (web / macos / android / …) are active, with a parent hierarchy. Higher-level Tom packages use this to select environment- and platform-specific implementations.
These four concerns share no code with each other; you can import the package and use only the logger, only the exception base, or only the platform seam.
Installation #
Add the dependency with its hosted version constraint:
dependencies:
tom_basics: ^1.0.0
or from the command line:
dart pub add tom_basics
SDK: Dart ^3.10.0. Transitive dependencies: uuid, stack_trace,
http — all pure-Dart and platform-neutral, so tom_basics itself adds no
native or Flutter requirement.
Features #
Exception handling #
| Capability | Type / member | Notes |
|---|---|---|
| UUID-stamped exception | TomBaseException |
Auto-generates a UUIDv4 unless one is supplied. |
| Request correlation | requestUuid |
Optional id tying an error to an inbound request. |
| Structured context | parameters |
Map<String, Object?> of diagnostic values. |
| Cause chaining | rootException |
The underlying error this one wraps. |
| Terse stack trace | stackTrace, printStackTrace() |
Core frames folded out via stack_trace. |
| Creation timestamp | timeStamp |
UTC DateTime set at construction. |
Logging #
| Capability | Type / member | Notes |
|---|---|---|
| Global logger | tomLog |
Ready-to-use TomLogger singleton. |
| Severity methods | trace debug traffic info warn status error severe fatal |
One method per level. |
| Bitwise levels | TomLogLevel + - matches |
Compose and subtract levels. |
| Compound levels | development extended production still silent off |
Named presets. |
| Per-name overrides | addNameLevel / setLogLevelExceptions |
Raise verbosity for one class or method. |
| Level stack | pushLogLevel / popLogLevel |
Temporary, scoped verbosity. |
| Pluggable output | logOutput, TomLogOutput |
Swap in file / remote / custom sinks. |
| Custom rendering | TomLoggable |
Control how your objects appear in logs. |
| Into the structured sink | TomDiagnosticsLogOutput |
TomLogger lines go to a TomDiagnostics sink, so a program keeps one log. |
Structured diagnostics #
| Capability | Type / member | Notes |
|---|---|---|
| The sink port | TomDiagnostics |
log(TomDiagnosticEvent). |
| An entry | TomDiagnosticEvent |
Operation name, level, UTC time, JSON-shaped field map — never a rendered sentence. |
| Shown and logged | TomDiagnosticNotice |
One producer emits the field map for the log and the sentence for a person. |
| Failures | TomStructuredFailure, tomFailureFields |
A failure is logged by its type and structural facts, never its text. |
| Sinks | TomNullDiagnostics, TomInMemoryDiagnostics, TomFileDiagnostics |
Discard; bounded ring; rolling JSON-lines file (diagnostics_io.dart). |
Utilities #
| Capability | Type / member | Notes |
|---|---|---|
| Cancellation | TomCancellationToken, TomCancellationSource, TomOperationCancelled |
The first reason wins; TomCancellationToken.none never cancels. |
| Hash and names | tomSha256Hex, tomSha256OfText, tomSlugify, tomShortId |
One SHA-256 for content addressing; a filename-safe slug with a fallback. |
| Text codec | tomDecodeTextBytes, tomEncodeText, TomTextEncoding |
Encoding and BOM detected and written back unchanged. |
| Glob and gitignore | TomGlob, TomGitignore, TomGitignoreSet |
Git's rules, including escapes and folder ancestry. |
| Canonical YAML | tomEmitYamlDocument, tomEmitLiteralBlock, tomEmitCanonicalFragment, tomOrderedMapping, tomSortedById |
Deterministic output for files a program owns. |
| Hand-edited YAML | tomYamlScalar, tomYamlEntry, tomYamlListItem, tomNewlineOf, tomYamlSchemaRefOf |
Key-level writes into files people also edit, and the schema such a file names. |
| Comment-preserving edits | TomYamlConfigWriter |
Set and remove values without losing comments (yaml_edit). |
| Protected clipboard | TomProtectedClipboard, TomClipboardPort, TomProtectedClipboardState |
A copied secret is cleared after a delay, only while the clipboard still holds it; the port is the application's (Flutter bindings in tom_reusable_ui). |
Platform abstraction #
| Capability | Type / member | Notes |
|---|---|---|
| Injectable singleton | TomPlatformUtils.current / setCurrentPlatform |
One seam for all platform detail. |
| Environment type | isDesktop isMobile isWeb |
Coarse-grained checks. |
| OS detection | isWindows isLinux isMacOs isFuchsia isAndroid isIos |
Fine-grained checks. |
| Console output | out / outError |
Platform-routed stdout / stderr. |
| HTTP client factory | httpClient() |
Returns a package:http Client. |
| Env vars | envVars / getTomEnvVars |
Platform-neutral configuration map. |
| Default fallback | TomFallbackPlatformUtils |
Console output works; detection throws until configured. |
Runtime environments and platforms #
| Capability | Type / member | Notes |
|---|---|---|
| Environment model | TomEnvironment |
Named, with optional parent + initializer. |
| Platform model | TomPlatform |
Named target with an initializer hook. |
| Runtime registry | TomRuntime |
Holds current env / platform + hierarchy resolution. |
| Platform constants | platformWeb platformMacos platformWindows platformLinux platformAndroid platformIos platformFuchsia |
Predefined targets. |
| Env constants | defaultTomEnvironment noTomEnvironment |
Defaults / sentinels. |
Quick start #
import 'package:tom_basics/tom_basics.dart';
void main() {
// The global logger is ready to use immediately.
tomLog.setLogLevel(TomLogLevel.development);
tomLog.info('Application starting...');
try {
throw TomBaseException('example.run.failed', 'Something went wrong');
} on TomBaseException catch (e) {
tomLog.error('Caught: ${e.key} - ${e.defaultUserMessage}');
print('Exception UUID: ${e.uuid}'); // e.g. aceec92a-90ab-4f7b-896e-...
}
tomLog.info('Application finished.');
}
Running it prints (timestamps and the UUID vary per run):
2026-06-16 21:27:22.128328 - INFO Application starting... [main]
2026-06-16 21:27:22.140356 - ERROR Caught: example.run.failed - Something went wrong [main]
Exception UUID: aceec92a-90ab-4f7b-896e-595d7f1a94ca
2026-06-16 21:27:22.140927 - INFO Application finished. [main]
This is exactly example/tom_basics_example.dart —
run it with dart run example/tom_basics_example.dart.
Example projects #
| Sample | Demonstrates |
|---|---|
example/tom_basics_example.dart |
The 12-line quick start above: logger + tracked exception. |
tom_basics_sample |
The full exception-handling-with-UUID-tracking model, end to end, with the logger and platform seam. (article-grade sample, seven runnable examples) |
Usage #
Exception handling with UUID tracking #
The constructor takes a key (a stable, machine-readable code) and a default user message (human-readable), with everything else optional.
A key is dotted lowercase, lower_snake_case within each segment, and reads
<area>.<operation>.<condition> — users.fetch.not_found,
orders.submit.no_positions. The first segment names the module or
subsystem, which is what keeps keys from unrelated code from colliding and
lets a handler match a whole area with a prefix test. tom_core_kernel
documents the convention in full; every key in the Tom framework follows it,
and this package's base class is where it starts.
import 'package:tom_basics/tom_basics.dart';
void main() {
final ex = TomBaseException(
'users.validate.invalid_email',
'The email address is not valid',
parameters: {'field': 'email', 'value': 'not-an-email'},
);
print(ex.key); // users.validate.invalid_email
print(ex.defaultUserMessage); // The email address is not valid
print(ex.parameters?['field']); // email
print(ex.uuid.length); // 36 (a UUIDv4 string)
}
Why a key and a message? The key is what your code branches on and what
you grep logs for; it never changes when you reword the prose. The
defaultUserMessage is the fallback text shown when no localized message is
available. Keeping them separate means translators and programmers never fight
over the same string.
Correlating an error with a request. Pass requestUuid so a failure deep
in a handler can be matched back to the inbound call that triggered it:
TomBaseException(
'orders.query.timeout',
'The database did not respond in time',
requestUuid: incomingRequestId,
parameters: {'table': 'orders', 'timeoutMs': 5000},
);
Wrapping a lower-level error. When you catch an exception and rethrow a
domain-level one, keep the original in rootException and the original trace in
stack so nothing is lost:
try {
await db.query(sql);
} catch (e, s) {
throw TomBaseException(
'orders.load.failed',
'Could not load the order',
rootException: e,
stack: s,
);
}
Inspecting the trace. Each exception folds out core/SDK frames at
construction time and stores the result in stackTrace. Print it directly, or
use printStackTrace():
try {
throw TomBaseException('example.demo.failed', 'demo');
} on TomBaseException catch (e) {
e.printStackTrace(); // writes "<uuid>-<requestUuid> exception stacktrace:\n..."
// or inspect the stored string:
print(e.stackTrace.split('\n').first); // the first non-core frame
}
Note.
tom_basicsdeliberately stops at this minimal base class. The full-featured exception type with integrated logging lives intom_core_kernelasTomException; reach for that when you are above the foundation layer.
Logging #
The global tomLog instance is usable without
any setup. Each severity has its own method:
import 'package:tom_basics/tom_basics.dart';
void main() {
tomLog.info('Server started on port 8080');
tomLog.warn('Cache miss for key user:42');
tomLog.error('Failed to connect to database');
tomLog.debug('Request payload: {"id": 42}');
tomLog.trace('Entering computeChecksum()');
}
Output lines carry the timestamp, isolate name, level, message, and the
detected origin (the class.method that called the logger), e.g.:
2026-06-16 21:27:22.140 - INFO Server started on port 8080 [main]
Log levels are bit patterns
A TomLogLevel is a bit mask. Individual
levels can be combined with + and removed with -, and a logger only
emits a message when its level matches the message level:
// Build a custom level: info plus errors, nothing else.
var quiet = TomLogLevel.info + TomLogLevel.errors;
tomLog.setLogLevel(quiet);
tomLog.info('shown'); // matches -> printed
tomLog.debug('hidden'); // no overlap -> filtered out
tomLog.error('shown'); // matches -> printed
// Subtract a level from a preset.
var prodNoInfo = TomLogLevel.production - TomLogLevel.info;
print(prodNoInfo.matches(TomLogLevel.info)); // false
print(prodNoInfo.matches(TomLogLevel.warn)); // true
Named presets cover the common cases:
| Preset | Includes |
|---|---|
development |
everything, including trace |
extended |
production + debug + traffic |
production |
info + warn + errors + status |
still |
warn + errors + status |
silent |
errors + status only |
off |
nothing |
You can also resolve a level by name (useful for reading a level from config or an environment variable):
tomLog.setLogLevelByName('DEVELOPMENT'); // case-insensitive
final lvl = TomLogLevel.byName('SILENT'); // or null if unknown
Temporary verbosity with a level stack
To turn the volume up around one tricky section and restore it afterwards, push and pop:
tomLog.pushLogLevel(TomLogLevel.trace);
// ... noisy operations are fully traced here ...
tomLog.popLogLevel(); // back to whatever was active before
Per-class and per-method overrides
When stack-trace analysis is enabled (the default,
TomLogger.globalSettingDetermineCaller == true), the logger detects which
class.method emitted each message. That lets you raise verbosity for one
location only:
// Trace everything in DatabaseService, debug one method of ApiClient.
tomLog.addNameLevel('DatabaseService', TomLogLevel.trace);
tomLog.addNameLevel('ApiClient.sendRequest', TomLogLevel.debug);
// Or configure several at once from a pattern string (e.g. from config):
tomLog.setLogLevelExceptions('DatabaseService=TRACE,ApiClient=DEBUG');
Redirecting output
The logger writes through logOutput, a TomLogOutput.
The default is TomConsoleLogOutput (errors and
status to stderr, everything else to stdout). Implement TomLogOutput.output
to send logs anywhere:
class CollectingLogOutput extends TomLogOutput {
final List<String> lines = [];
@override
void output(
TomLogLevel loggerLevel,
TomLogLevel logLevel,
String level,
Object message,
String isolateName,
DateTime timeStamp,
String? origin,
) {
if (logLevel.matches(loggerLevel)) {
lines.add('$level ${convertToString(message)}');
}
}
}
void main() {
final sink = CollectingLogOutput();
tomLog.logOutput = sink;
tomLog.info('captured');
print(sink.lines); // [INFO captured]
}
convertToString (provided by the base class) handles Strings, lazy
Function messages, and TomLoggable objects automatically.
Custom log representation for your types
Implement TomLoggable so an object renders cleanly in logs without exposing
internals:
class User implements TomLoggable {
User(this.id, this.name);
final String id;
final String name;
@override
String get logRepresentation => 'User($id, $name)';
}
void main() {
tomLog.info(User('42', 'Ada')); // logs: User(42, Ada)
}
Structured diagnostics #
A rendered log line has already interpolated whatever its caller had, and no sink downstream can tell a locator from a password. The structured port keeps the payload as data until the sink renders it, which is what lets a rule like "secrets never reach the log" hold by construction:
import 'package:tom_basics/diagnostics.dart';
final TomDiagnostics log = TomInMemoryDiagnostics();
log.log(TomDiagnosticEvent(
operation: 'sync.commit',
level: TomDiagnosticLevel.warn,
fields: {'locator': 'notes/a.md', 'status': 409},
));
Three entry points, by what they pull in:
package:tom_basics/diagnostics.dart— the port, the events, notices and failure fields, the null and in-memory sinks. Imports nothing, so a web-linkable or dependency-light package can take it.package:tom_basics/diagnostics_io.dart— addsTomFileDiagnostics, a rolling JSON-lines file: one object per line, a byte ceiling checked before each append, a fixed number of generations, a settable path and level, and a value that is not JSON-shaped written as<non-JSON TypeName>rather than throughtoString(). A refused write stops the sink and is kept infailure, so a full disk is not a stall on every line.package:tom_basics/tom_basics.dart— all of the port, plusTomDiagnosticsLogOutput, which sendsTomLoggerlines into a sink:
final file = TomFileDiagnostics(path: 'logs/app.log');
tomLog.logOutput = TomDiagnosticsLogOutput(file);
tomLog.warn('disk almost full'); // one JSON line, operation "log"
A TomLogger line's text is already rendered, so the bridge cannot redact it;
what it keeps is the level, the time, the isolate and the sink's bounds — and a
program that logs both ways has one log.
Utilities #
import 'package:tom_basics/utilities.dart';
final source = TomCancellationSource();
source.token.whenCancelled.then((reason) => print('stopped: $reason'));
source.cancel('deadline');
final ignore = TomGitignoreSet()
..add(TomGitignore.parse('', 'build/\n*.log\n!keep.log\n'));
ignore.isIgnored('build/out/app.js', isDirectory: false); // true
tomSlugify('Chapter 2: Design'); // chapter-2-design
tomEmitYamlDocument({'title': 'Fix: the parser'}); // title: "Fix: the parser"
utilities.dart imports no dart:io and no HTTP stack. The two YAML styles
are deliberate: the canonical emitter is for files a program owns and always
writes the same bytes; the hand-edited style changes only the keys it is asked
to change in a file a person also edits.
tomYamlToPlain reads either kind back as plain maps, lists and scalars.
Platform abstraction #
Library code that must not import dart:io or dart:html directly talks to
TomPlatformUtils.current. The
concrete implementation is injected once at startup. Until you do that, the
default TomFallbackPlatformUtils
supports console output but throws UnimplementedError for detection — a loud,
deliberate signal that the platform was never configured:
import 'package:tom_basics/tom_basics.dart';
// A Linux console/server implementation. Extending TomFallbackPlatformUtils
// gives working out/outError; we override every detection method so that
// TomRuntime.initializePlatform() (which probes them all) also works.
class ConsolePlatformUtils extends TomFallbackPlatformUtils {
@override
bool isDesktop() => true;
@override
bool isMobile() => false;
@override
bool isWeb() => false;
@override
bool isWindows() => false;
@override
bool isLinux() => true;
@override
bool isMacOs() => false;
@override
bool isFuchsia() => false;
@override
bool isAndroid() => false;
@override
bool isIos() => false;
}
void main() {
TomPlatformUtils.setCurrentPlatform(ConsolePlatformUtils());
final p = TomPlatformUtils.current;
print(p.isDesktop()); // true
print(p.isLinux()); // true
p.out('hello from the platform seam'); // prints to stdout
}
A stub that overrides only isDesktop/isMobile/isWeb is enough for those
three checks, but TomRuntime.initializePlatform() (below) probes the full set
of OS methods — so a complete implementation like the one above is what makes
detection work end to end. In real apps you don't write this yourself:
tom_basics_console ships the desktop/server implementation and
tom_core_flutter ships the Flutter one. The seam is what lets the same
upstream code run on both.
The same singleton also hands out an HTTP client and a configuration map:
// Configuration that travels with the platform, not the call site.
TomPlatformUtils.envVars['API_BASE'] = 'https://api.example.com';
print(TomPlatformUtils.current.getTomEnvVars()['API_BASE']);
// https://api.example.com
// A platform-appropriate http client (once a real platform is configured):
// final client = TomPlatformUtils.current.httpClient();
// final res = await client.get(Uri.parse('https://api.example.com/health'));
Runtime environments and platforms #
The runtime model answers "which environment and platform are active, and what is the chain of fallbacks?" Register environments, pick the current one, and walk the hierarchy:
import 'package:tom_basics/tom_basics.dart';
void main() {
final prod = TomRuntime.addEnvironment(const TomEnvironment('production'));
TomRuntime.addEnvironment(
TomEnvironment('staging', parent: prod),
);
TomRuntime.setCurrentEnvironment('staging');
print(TomRuntime.getCurrentEnvironment().env); // staging
// Root-to-current chain, used by higher layers to resolve fallbacks.
final chain = TomRuntime.getEnvironmentHierarchy().map((e) => e.env).toList();
print(chain); // [production, staging]
}
An environment may carry an initializer that runs when it is activated:
final dev = TomEnvironment(
'development',
isDevelopment: true,
initializer: (env) => tomLog.info('Activated ${env.env}'),
);
dev.initialize(); // logs: Activated development
Platform detection ties the platform seam to the runtime registry. Once a real
TomPlatformUtils is configured, initializePlatform() detects and records the
current platform:
TomPlatformUtils.setCurrentPlatform(ConsolePlatformUtils());
TomRuntime.setCurrentEnvironment('production');
TomRuntime.initializePlatform();
print(TomRuntime.printReport());
// TomRuntime: Platform TomPlatform: linux Root Environment ... Current Environment ...
Architecture #
tom_basics is a set of small, independent modules behind a single barrel export
(lib/tom_basics.dart):
package:tom_basics/tom_basics.dart
│ (barrel export)
┌──────────────┬─────────────┼──────────────────┐
│ │ │ │
┌──────┴──────┐ ┌─────┴──────┐ ┌────┴─────────┐ ┌──────┴────────────┐
│ exceptions/ │ │ logging/ │ │ runtime/ │ │ runtime/ │
│ exception_ │ │ logging. │ │ platform_ │ │ platform_ │
│ base.dart │ │ dart │ │ neutral.dart │ │ environment_ │
│ │ │ │ │ │ │ runtime.dart │
│ TomBase │ │ TomLogger │ │ TomPlatform │ │ TomEnvironment │
│ Exception │ │ TomLogLevel│ │ Utils │ │ TomPlatform │
│ │ │ TomLogOut │ │ (+ fallback) │ │ TomRuntime │
└─────────────┘ └─────┬──────┘ └──────┬───────┘ └───────────────────┘
uuid, │ uses │ uses
stack_trace └───────────────┘
(logging routes its console
output through the platform seam)
The only internal coupling is that logging writes through the platform seam
(TomConsoleLogOutput calls TomPlatformUtils.current.out/outError) and reads
the isolate name from it. Exceptions and the environment model stand alone.
Two further modules have entry points of their own and depend on nothing
else in the package: diagnostics/ (diagnostics.dart, with the file sink in
diagnostics_io.dart) and utilities/ (utilities.dart). The barrel
re-exports both.
Key types #
| Type | Responsibility |
|---|---|
TomBaseException |
Minimal exception base with UUID, timestamp, params, cause, and folded stack trace. |
TomLogger |
The logger: severity methods, level + per-name configuration, output dispatch. |
tomLog |
The global TomLogger instance. |
TomLogLevel |
Bitwise log-level value with +, -, matches, and named presets. |
TomLogOutput |
Abstract log sink; TomConsoleLogOutput is the default stdout/stderr impl. |
TomLoggable |
Interface for objects that supply their own logRepresentation. |
TomDiagnostics |
Structured sink port; TomNullDiagnostics, TomInMemoryDiagnostics, TomFileDiagnostics. |
TomDiagnosticEvent / TomDiagnosticNotice |
A structured entry; a finding that is both logged and shown. |
TomDiagnosticsLogOutput |
TomLogOutput that writes TomLogger lines into a TomDiagnostics sink. |
TomCancellationToken / TomCancellationSource |
Cooperative cancellation with a reason. |
TomGitignoreSet |
Gitignore matching across several files. |
TomOutputTranscript / TomOutputRecorder |
A bounded, interleaved stdout/stderr transcript that states how many lines it dropped. |
TomYamlConfigWriter |
Comment-preserving YAML edits. |
TomProtectedClipboard |
Copies a secret and clears it again through an application-bound TomClipboardPort. |
TomPlatformUtils |
Injectable platform seam: detection, console output, HTTP client, env vars. |
TomFallbackPlatformUtils |
Default impl: console output works, detection throws until configured. |
TomEnvironment |
A named runtime environment with optional parent + initializer. |
TomPlatform |
A named target platform with an initializer hook. |
TomRuntime |
Registry of current environment/platform and hierarchy resolution. |
Ecosystem #
tom_basics sits at the root of the basics layer; the rest of the framework
imports it, never the other way around.
┌───────────────────────────┐
│ tom_core_kernel / server │ higher-level Tom packages
│ tom_core_flutter, d4rt … │ (TomException, DI, …)
└─────────────┬──────────────┘
│ depends on
┌────────────────┬──────────┼───────────────┐
│ │ │ │
┌──────┴──────┐ ┌──────┴──────┐ │ ┌───────┴────────┐
│ tom_basics_ │ │ tom_basics_ │ │ │ tom_crypto │
│ console │ │ network │ │ │ (uses logging) │
│ (platform │ │ │ │ └────────────────┘
│ impl) │ │ │ │
└──────┬──────┘ └──────┬──────┘ │
└────────────────┴──────────┘
│ all depend on
┌──────┴───────┐
│ tom_basics │ ← you are here
└──────────────┘
(uuid · stack_trace · http only)
See the basics repository map for the full package catalogue and the samples learning path.
Further documentation #
doc/runtime/runtime.md— the runtime guide:TomEnvironment,TomPlatform,TomRuntimeand theTomPlatformUtilsseam, with the initialization sequence and the bean-context integration.- Basics repository README — the map of all ten basics packages.
tom_basics_console— supplies the desktop/serverTomPlatformUtilsimplementation that backs the platform seam.tom_crypto— a downstream consumer that logs throughtomLog.tom_core_kernel(separate repo) — providesTomException, the full-featured exception type that builds on this base.
Status #
- Version: 1.0.0
- SDK: Dart
^3.10.0 - Tests: 6 passing (
TomBaseExceptiongroup — construction, parameters, custom UUID, cause capture,toString, stack-trace capture). Run withdart testortestkit :test. - Analyzer: clean (
dart analyze→ no issues).