initInternal static method

Future<SankofaDeploy> initInternal({
  1. required String apiKey,
  2. required String endpoint,
  3. required SankofaDeployOptions options,
  4. String? appVersion,
  5. String? flavor,
  6. String distinctIdResolver()?,
  7. void onLifecycleEvent(
    1. String eventType, {
    2. String? label,
    3. String? releaseId,
    })?,
})

Internal hook used by Sankofa.init — DO NOT call directly. The per-product enableDeploy flag triggers this. Idempotent.

Native platform plugin init is best-effort. When the platform plugin's initialize() throws (e.g. on iOS Path C where the native half of the libapp.so binary-diff updater doesn't exist), _instance is still set + _ready is left false. Pure-Dart methods (the KBC patch apply pipeline — applyKbcPatchFromBytes, applyKbcPatchFromFile, fetchAndApplyKbcPatch) don't gate on _ready and remain usable. Methods that DO require the platform plugin (Android baseline OTA — checkForUpdate, notifyAppReady, reportCrash, disableCurrentPatch, getActiveVersion, getCurrentLibappPath) throw via _assertReady() with a clear message explaining the platform is unavailable.

Before η polish (2026-05-24): platform-init failure inside the unawaited() wrapper that calls this function would silently leave _instance == null and Sankofa.deploy returned null — hello_sankofa hit this on iOS, fell back to the standalone applyKbcEnvelopeFromFile helper.

Implementation

static Future<SankofaDeploy> initInternal({
  required String apiKey,
  required String endpoint,
  required SankofaDeployOptions options,
  String? appVersion,
  String? flavor,
  String Function()? distinctIdResolver,
  void Function(String eventType, {String? label, String? releaseId})?
      onLifecycleEvent,
}) async {
  if (_instance != null) return _instance!;
  final deploy = SankofaDeploy._(options: options);
  deploy._ingestEndpoint = endpoint;
  deploy._ingestApiKey = apiKey;
  deploy._defaultAppVersion = appVersion;
  deploy._flavor = flavor;
  deploy._distinctIdResolver = distinctIdResolver;
  deploy._onLifecycleEvent = onLifecycleEvent;
  // Register with the Traffic Cop BEFORE the platform init kicks off
  // so the first unified handshake (fired from Sankofa.init right
  // after this call returns) has a route for `modules.deploy`. The
  // server is the source of truth for whether this module operates;
  // host-side `enableDeploy: true` is just intent.
  SankofaModuleRegistry.instance.register(deploy);

  // Publish the instance EAGERLY so a slow / failing platform plugin
  // init never strands `Sankofa.deploy` at null. The Path C methods
  // are pure Dart and don't need the native plugin; the Android
  // methods gate on `_ready` and throw a clear message when it's
  // false. Either way `Sankofa.deploy` is the canonical entry point.
  _instance = deploy;

  try {
    await SankofaDeployPlatform.instance.initialize(
      apiKey: apiKey,
      endpoint: endpoint,
      options: options,
    );
    deploy._ready = true;
  } on MissingPluginException {
    // No native Deploy plugin registered in this build. This is the
    // EXPECTED, benign case under plain `flutter run` (the native
    // baseline ships via the Sankofa CLI build / fork engine) and on
    // iOS (Path C is pure-Dart). KBC / Path C OTA is fully unaffected.
    // Degrade silently — no error, no scary log, no "BROKEN" report.
    deploy._platformUnavailable = true;
  } catch (_) {
    // A genuinely unexpected platform failure (plugin present but
    // threw). Still non-fatal — Path C doesn't gate on _ready — and we
    // deliberately stay quiet so a dev run never surfaces a deploy
    // error. _ready stays false; native baseline methods will throw a
    // clear _assertReady message only if the host actually calls them.
  }

  if (options.autoCheckOnStartup && deploy._ready) {
    // Fire-and-forget. Errors flow into the platform's logger and
    // the next checkForUpdate() call will surface the state. Skip
    // on iOS where _ready stays false because the legacy libapp.so
    // path is Android-only.
    // ignore: deprecated_member_use_from_same_package
    unawaited(deploy.legacyAndroidCheckForUpdate());
  }
  return deploy;
}