run static method
Future<SankofaBootstrapResult>
run({
- SankofaBootstrapOptions options = const SankofaBootstrapOptions(),
Run the bootstrap. Idempotent — calling twice is harmless (the second call is a no-op because Sankofa.instance is already initialized).
Implementation
static Future<SankofaBootstrapResult> run({
SankofaBootstrapOptions options = const SankofaBootstrapOptions(),
}) async {
final config = await _readSankofaYaml(options.yamlAssetPath);
final apiKey = config['api_key']?.trim() ?? _kFallbackApiKey;
final appId = config['app_id']?.trim() ?? _kFallbackAppId;
final endpoint = config['base_url']?.trim() ?? options.fallbackEndpoint;
final engineVersion = config['engine_version']?.trim() ?? '';
// signing_pubkey is written by `sankofa keys generate` and never
// edited by hand — customer doesn't need to know it exists.
final signingPubkey = config['signing_pubkey']?.trim();
if (apiKey.isEmpty && kDebugMode) {
debugPrint(
'[Sankofa.bootstrap] WARNING: api_key missing in '
'${options.yamlAssetPath} — SDK will initialize with empty apiKey '
'and the server will refuse all calls.',
);
}
// Auto-enable Deploy when a loader was provided.
final enableDeploy = options.enableDeploy ?? (options.loader != null);
// If sankofa.yaml carried an engine_version + signing_pubkey, forward
// them as deploy defaults so customer code never has to repeat
// engine identity / signing keys on every call. Both fields are
// written by the Sankofa CLI (`sankofa init` / `sankofa keys
// generate`) — customers do not edit them by hand.
SankofaDeployOptions effectiveDeployOptions = options.deployOptions;
if (engineVersion.isNotEmpty &&
(effectiveDeployOptions.engineVersion == null ||
effectiveDeployOptions.engineVersion!.isEmpty)) {
effectiveDeployOptions =
effectiveDeployOptions.copyWith(engineVersion: engineVersion);
}
if (signingPubkey != null &&
signingPubkey.isNotEmpty &&
(effectiveDeployOptions.signingPubkeyB64 == null ||
effectiveDeployOptions.signingPubkeyB64!.isEmpty)) {
effectiveDeployOptions =
effectiveDeployOptions.copyWith(signingPubkeyB64: signingPubkey);
}
await Sankofa.instance.init(
apiKey: apiKey,
endpoint: endpoint,
debug: options.debug,
enableDeploy: enableDeploy,
deployOptions: effectiveDeployOptions,
// Respect overrides; otherwise leave init() defaults alone.
enableCatch: options.enableCatch ?? true,
enableAnalytics: options.enableAnalytics ?? true,
);
KbcPatchResult? staged;
if (enableDeploy && options.loader != null) {
try {
staged = await Sankofa.instance.deploy?.tryApplyStagedKbcPatch(
loader: options.loader!,
);
} catch (e, st) {
if (kDebugMode) {
debugPrint('[Sankofa.bootstrap] tryApplyStagedKbcPatch threw: $e\n$st');
}
}
if (options.scheduleNotifyOnFirstFrame && staged != null) {
// Schedule notify after the first frame paints. The deploy
// module's auto-confirm timer is a safety net; this is the
// explicit path that lets the host signal "rendering worked"
// without writing the addPostFrameCallback boilerplate.
SchedulerBinding.instance.addPostFrameCallback((_) {
// Schedule on the next microtask so we don't block frame work.
Future<void>.delayed(Duration.zero, () async {
try {
await Sankofa.instance.deploy?.notifyKbcPatchReady();
} catch (e) {
if (kDebugMode) {
debugPrint('[Sankofa.bootstrap] notifyKbcPatchReady failed: $e');
}
}
});
});
}
}
return SankofaBootstrapResult(
apiKey: apiKey,
endpoint: endpoint,
appId: appId,
engineVersion: engineVersion,
didApplyStagedPatch: staged != null,
stagedPatch: staged,
);
}