feat: fai_dart_sdk scaffold

Dart package skeleton for the F∆I gRPC client. Used by F∆I
Studio (Tier-2) and Tier-3 domain apps (DigiScout / J∆I /
Scout). Public surface — HubClient, HubEndpoint — is committed
as a typed stub so downstream apps can compile against it
immediately. Generated proto bindings land in a follow-up
commit once the codegen step (planned: tools/generate.sh
wrapping protoc-gen-dart against ../fai_platform/proto) is
wired in.

Future Forgejo path: fai/dart-sdk.

dart analyze: clean. dart test: 4/4.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-05 14:19:39 +02:00
commit d405a6a9bd
9 changed files with 216 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

3
CHANGELOG.md Normal file
View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

52
README.md Normal file
View file

@ -0,0 +1,52 @@
# fai_dart_sdk
gRPC client SDK for the F∆I Platform hub. Used by F∆I Studio
(Tier-2 generic GUI) and any Tier-3 Dart/Flutter domain app
(DigiScout, J∆I, Scout, …) that wants to talk to a running
`fai serve`.
> **Status (2026-05-05):** scaffold. Public surface (`HubClient`,
> `HubEndpoint`) is committed; generated proto bindings land in
> a follow-up commit once the codegen step is wired in. Until
> then, downstream apps can compile against the stub and Studio
> uses mock data.
## Why a separate SDK package
The platform hub speaks gRPC. Each Dart consumer (Studio plus
each Tier-3 domain app) would otherwise re-generate proto
bindings independently and write near-identical client
boilerplate. This package centralises that work:
- One source of generated proto bindings.
- One typed `HubClient` wrapper with helpers.
- One place to bump when the wire protocol evolves.
## Layout
```
fai_dart_sdk/
├── lib/
│ ├── fai_dart_sdk.dart # Public API
│ └── src/
│ ├── hub_client.dart # Typed client wrapper
│ └── generated/ # protoc output (committed; pinned to platform tag)
├── tools/
│ └── generate.sh # Wraps `protoc --dart_out=...` against ../fai_platform/proto
├── example/
└── pubspec.yaml
```
## Repo placement
Will be published as `fai/dart-sdk` on Forgejo
(`git.flemming.ws`). The local directory `fai_dart_sdk/`
follows the established `fai_platform/` layout convention.
## Versioning
Tracks the platform's wire-protocol version, not the platform
binary version. As long as `fai:platform@1.x` is current, this
SDK stays at `0.x` (will move to `1.0` once API stabilises);
when `fai:platform@2.0` lands, a `2.x` SDK ships in parallel
during the transition.

30
analysis_options.yaml Normal file
View file

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,20 @@
// Example: connect to a locally-running FI hub.
//
// Run with `dart run example/fai_dart_sdk_example.dart`.
// Currently the SDK is a stub; once codegen lands, this example
// will exercise the actual Hub RPCs.
import 'package:fai_dart_sdk/fai_dart_sdk.dart';
Future<void> main() async {
final client = HubClient(
endpoint: const HubEndpoint(host: '127.0.0.1', port: 50051),
);
// ignore: avoid_print
print('would connect to ${client.endpoint}');
// ignore: avoid_print
print('(SDK is a stub today; live RPC arrives with codegen)');
await client.close();
}

14
lib/fai_dart_sdk.dart Normal file
View file

@ -0,0 +1,14 @@
/// gRPC client for the FI Platform hub.
///
/// Provides a typed Dart wrapper around the hub's gRPC services
/// (Hub, HubAdmin) defined in `fai/platform`'s `proto/fai/v1/`
/// directory. Used by FI Studio and any Tier-3 Dart/Flutter
/// app (DigiScout, JI, Scout, ) that wants to talk to a hub.
///
/// Status (2026-05-05): scaffold only. Generated proto bindings
/// land in `lib/src/generated/` once the codegen step is wired
/// in (`tools/generate.sh`). Until then, [HubClient] is a stub
/// type that documents the intended surface.
library;
export 'src/hub_client.dart';

43
lib/src/hub_client.dart Normal file
View file

@ -0,0 +1,43 @@
// HubClient typed wrapper around the FI Hub gRPC service.
//
// Status (2026-05-05): scaffold. The generated proto bindings
// from `fai/platform`'s proto/fai/v1/ directory will populate
// the actual RPC implementations under `lib/src/generated/`
// once the codegen tooling is wired (planned: a small
// `tools/generate.sh` script that runs `protoc` with
// `protoc-gen-dart` against the platform repo's protos).
/// Connection coordinates for a hub endpoint.
class HubEndpoint {
/// Hostname or IP. Defaults to localhost for the canonical
/// `fai serve` setup.
final String host;
/// gRPC port. Defaults to 50051.
final int port;
/// When true, use TLS. Phase 1+ will add credential support.
final bool secure;
const HubEndpoint({
this.host = '127.0.0.1',
this.port = 50051,
this.secure = false,
});
@override
String toString() => '${secure ? "https" : "http"}://$host:$port';
}
/// Stub client interface. The real implementation will arrive
/// when proto codegen is wired in. Studio uses this type as the
/// future-stable surface; the mock data layer in
/// `fai_studio/lib/data/mock.dart` is the placeholder until then.
class HubClient {
final HubEndpoint endpoint;
HubClient({this.endpoint = const HubEndpoint()});
/// Closes the underlying channel. No-op while stubbed.
Future<void> close() async {}
}

16
pubspec.yaml Normal file
View file

@ -0,0 +1,16 @@
name: fai_dart_sdk
description: gRPC client SDK for the F∆I Platform hub. Used by F∆I Studio and Tier-3 domain apps.
version: 0.1.0
publish_to: 'none'
repository: https://git.flemming.ws/fai/dart-sdk
environment:
sdk: ^3.11.0-200.1.beta
dependencies:
grpc: ^4.0.0
protobuf: ^4.0.0
dev_dependencies:
lints: ^6.0.0
test: ^1.25.6

View file

@ -0,0 +1,31 @@
import 'package:fai_dart_sdk/fai_dart_sdk.dart';
import 'package:test/test.dart';
void main() {
group('HubEndpoint', () {
test('default endpoint targets local fai serve', () {
const e = HubEndpoint();
expect(e.host, '127.0.0.1');
expect(e.port, 50051);
expect(e.secure, false);
expect(e.toString(), 'http://127.0.0.1:50051');
});
test('secure flag flips scheme', () {
const e = HubEndpoint(host: 'hub.example', port: 443, secure: true);
expect(e.toString(), 'https://hub.example:443');
});
});
group('HubClient', () {
test('constructs with default endpoint', () {
final c = HubClient();
expect(c.endpoint.host, '127.0.0.1');
});
test('close is a no-op on the stub', () async {
final c = HubClient();
await c.close();
});
});
}