start static method

Future<DVAppLaunchResult> start({
  1. required String appId,
  2. required List<String> arguments,
  3. required Future<void> open(
    1. String route
    ),
  4. String? lockPath,
  5. String filesRoute = '/open',
  6. Duration poll = const Duration(milliseconds: 500),
  7. DVAppLaunchAcquire acquire = DVSingleInstance.acquire,
})

Takes the lock for appId, or hands arguments to the process that has it. The primary opens its own arguments' routes and, every poll, whatever later launches forwarded, through open.

Implementation

static Future<DVAppLaunchResult> start({
  required String appId,
  required List<String> arguments,
  required Future<void> Function(String route) open,
  String? lockPath,
  String filesRoute = '/open',
  Duration poll = const Duration(milliseconds: 500),
  DVAppLaunchAcquire acquire = DVSingleInstance.acquire,
}) async {
  final List<String> routes = <String>[
    for (final String a in arguments)
      if (routeFor(a, filesRoute: filesRoute) case final String r) r,
  ];
  for (final String a in arguments) {
    if (!isLink(a) || routeFor(a, filesRoute: filesRoute) == null) continue;
    _initialLink ??= a.trim();
    const DVDeepLinks().dispatch(a.trim());
  }
  final String path = lockPath ?? lockPathFor(appId);
  final DVAppLaunchResult? already = _primaries[path];
  if (already != null) {
    for (final String r in routes) {
      already._lock!.send(r);
    }
    return already;
  }
  final DVInstanceLock lock = acquire(path);
  if (!lock.isPrimary) {
    for (final String r in routes) {
      lock.send(r);
    }
    lock.release();
    return DVAppLaunchResult._(isPrimary: false, forwarded: routes);
  }
  // Own arguments go through the same queue as everyone else's, so there
  // is one path, one trust rule and one order.
  for (final String r in routes) {
    lock.send(r);
  }
  final DVAppLaunchResult result = DVAppLaunchResult._(
    isPrimary: true,
    forwarded: const <String>[],
    lock: lock,
  );
  _primaries[path] = result;
  bool draining = false;
  Future<void> drain() async {
    if (draining) return;
    draining = true;
    try {
      for (final String route in lock.takePending()) {
        // Written by another process, so not trusted as a route outright.
        final String path = route.trim();
        if (!path.startsWith('/') || path.startsWith('//')) continue;
        await open(path);
      }
    } finally {
      draining = false;
    }
  }

  result._timer = Timer.periodic(poll, (_) => unawaited(drain()));
  unawaited(drain());
  return result;
}