fromUrl static method

Future<StemApp> fromUrl(
  1. String url, {
  2. StemModule? module,
  3. Iterable<StemModule> modules = const [],
  4. Iterable<TaskHandler<Object?>> tasks = const [],
  5. TaskRegistry? registry,
  6. Iterable<StemStoreAdapter> adapters = const [],
  7. StemStoreOverrides overrides = const StemStoreOverrides(),
  8. StemWorkerConfig workerConfig = const StemWorkerConfig(),
  9. RevokeStore? revokeStore,
  10. UniqueTaskCoordinator? uniqueTaskCoordinator,
  11. bool uniqueTasks = false,
  12. Duration uniqueTaskDefaultTtl = const Duration(minutes: 5),
  13. String uniqueTaskNamespace = 'stem:unique',
  14. bool requireRevokeStore = false,
  15. RetryStrategy? retryStrategy,
  16. Iterable<Middleware> middleware = const [],
  17. PayloadSigner? signer,
  18. RoutingRegistry? routing,
  19. TaskPayloadEncoderRegistry? encoderRegistry,
  20. TaskPayloadEncoder resultEncoder = const JsonTaskPayloadEncoder(),
  21. TaskPayloadEncoder argsEncoder = const JsonTaskPayloadEncoder(),
  22. Iterable<TaskPayloadEncoder> additionalEncoders = const [],
  23. StemStack? stack,
})

Creates an app from a single backend URL plus adapter wiring.

This helper resolves broker/backend factories via StemStack.fromUrl and can optionally auto-wire revoke and unique-task coordination stores.

Implementation

static Future<StemApp> fromUrl(
  String url, {
  StemModule? module,
  Iterable<StemModule> modules = const [],
  Iterable<TaskHandler<Object?>> tasks = const [],
  TaskRegistry? registry,
  Iterable<StemStoreAdapter> adapters = const [],
  StemStoreOverrides overrides = const StemStoreOverrides(),
  StemWorkerConfig workerConfig = const StemWorkerConfig(),
  RevokeStore? revokeStore,
  UniqueTaskCoordinator? uniqueTaskCoordinator,
  bool uniqueTasks = false,
  Duration uniqueTaskDefaultTtl = const Duration(minutes: 5),
  String uniqueTaskNamespace = 'stem:unique',
  bool requireRevokeStore = false,
  RetryStrategy? retryStrategy,
  Iterable<Middleware> middleware = const [],
  PayloadSigner? signer,
  RoutingRegistry? routing,
  TaskPayloadEncoderRegistry? encoderRegistry,
  TaskPayloadEncoder resultEncoder = const JsonTaskPayloadEncoder(),
  TaskPayloadEncoder argsEncoder = const JsonTaskPayloadEncoder(),
  Iterable<TaskPayloadEncoder> additionalEncoders = const [],
  StemStack? stack,
}) async {
  final needsUniqueLockStore =
      uniqueTasks &&
      uniqueTaskCoordinator == null &&
      workerConfig.uniqueTaskCoordinator == null;
  final needsRevokeStore =
      requireRevokeStore &&
      revokeStore == null &&
      workerConfig.revokeStore == null;

  final resolvedStack =
      stack ??
      StemStack.fromUrl(
        url,
        adapters: adapters,
        overrides: overrides,
        uniqueTasks: needsUniqueLockStore,
        requireRevokeStore: needsRevokeStore,
      );

  final autoDisposers = <Future<void> Function()>[];

  var resolvedUniqueTaskCoordinator =
      uniqueTaskCoordinator ?? workerConfig.uniqueTaskCoordinator;
  if (needsUniqueLockStore) {
    final lockFactory = resolvedStack.lockStore;
    if (lockFactory == null) {
      throw StateError(
        'Unique task coordination requested but lock store factory missing.',
      );
    }
    final lockStore = await lockFactory.create();
    resolvedUniqueTaskCoordinator = UniqueTaskCoordinator(
      lockStore: lockStore,
      defaultTtl: uniqueTaskDefaultTtl,
      namespace: uniqueTaskNamespace,
    );
    autoDisposers.add(() async => lockFactory.dispose(lockStore));
  }

  var resolvedRevokeStore = revokeStore ?? workerConfig.revokeStore;
  if (needsRevokeStore) {
    final revokeFactory = resolvedStack.revokeStore;
    if (revokeFactory == null) {
      throw StateError('Revoke store required but no revoke factory found.');
    }
    final createdRevokeStore = await revokeFactory.create();
    resolvedRevokeStore = createdRevokeStore;
    autoDisposers.add(() async => revokeFactory.dispose(createdRevokeStore));
  }

  try {
    final app = await create(
      module: module,
      modules: modules,
      tasks: tasks,
      registry: registry,
      broker: resolvedStack.broker,
      backend: resolvedStack.backend,
      workerConfig: workerConfig,
      revokeStore: resolvedRevokeStore,
      uniqueTaskCoordinator: resolvedUniqueTaskCoordinator,
      retryStrategy: retryStrategy,
      middleware: middleware,
      signer: signer,
      routing: routing,
      encoderRegistry: encoderRegistry,
      resultEncoder: resultEncoder,
      argsEncoder: argsEncoder,
      additionalEncoders: additionalEncoders,
    );

    // Dispose auto-provisioned lock/revoke stores after worker shutdown and
    // before backend/broker factories are disposed.
    app._insertAutoDisposers(autoDisposers);

    return app;
  } on Object catch (error, stackTrace) {
    // If app creation fails, release any auto-provisioned stores now to avoid
    // leaking startup resources.
    for (final disposer in autoDisposers.reversed) {
      try {
        await disposer();
      } on Object {
        // Keep the original startup error as the primary failure.
      }
    }
    Error.throwWithStackTrace(error, stackTrace);
  }
}