Repository: spotify/backstage
File: packages/backend-app-api/src/wiring/BackendInitializer.ts (TypeScript)

Code before:
    })
    .flat();
  return createServiceFactory({
    service: instanceMetadataServiceRef,
    deps: {
      httpRouter: coreServices.rootHttpRouter,
      logger: coreServices.rootLogger,
    },
    factory: async ({ logger, httpRouter }) => {
      const instanceMetadata = {
        getInstalledFeatures: () => installedFeatures,
      };

      logger.info(
        `Installed features on this instance: ${instanceMetadata
          .getInstalledFeatures()
          .join(', ')}`,
      );

      const router = Router();

      router.get('/features/installed', (_, res) => {
        res.json({ items: instanceMetadata.getInstalledFeatures() });
      });

      httpRouter.use('/.backstage/instanceMetadata/v1', router);

      return instanceMetadata;
    },
  });
}

export class BackendInitializer {
  #startPromise?: Promise<void>;
  #stopPromise?: Promise<void>;
  #registrations = new Array<InternalBackendRegistrations>();
  #extensionPoints = new Map<string, { impl: unknown; pluginId: string }>();
  #serviceRegistry: ServiceRegistry;
  #registeredFeatures = new Array<Promise<BackendFeature>>();
  #registeredFeatureLoaders = new Array<InternalBackendFeatureLoader>();

  constructor(defaultApiFactories: ServiceFactory[]) {
    this.#serviceRegistry = ServiceRegistry.create([...defaultApiFactories]);
  }

  async #getInitDeps(
    deps: { [name: string]: ServiceOrExtensionPoint },
    pluginId: string,
    moduleId?: string,
  ) {
    const result = new Map<string, unknown>();

Review comment (suggestion):
Had the thought that maybe this should be just `metadata`, since the fact that we're calling an API kind of implies that we're talking to an instance anyway. I don't dislike the current naming either, just wonder if there are any other similar thoughts.

Code after:
  });
}

export class BackendInitializer {
  #startPromise?: Promise<{ result: BackendStartupResult }>;
  #stopPromise?: Promise<void>;
  #registrations = new Array<InternalBackendRegistrations>();
  #extensionPoints = new Map<string, { impl: unknown; pluginId: string }>();
  #serviceRegistry: ServiceRegistry;
  #registeredFeatures = new Array<Promise<BackendFeature>>();
  #registeredFeatureLoaders = new Array<InternalBackendFeatureLoader>();

  constructor(defaultApiFactories: ServiceFactory[]) {
    this.#serviceRegistry = ServiceRegistry.create([...defaultApiFactories]);
  }

  async #getInitDeps(
    deps: { [name: string]: ServiceOrExtensionPoint },
    pluginId: string,
    moduleId?: string,
  ) {
    const result = new Map<string, unknown>();
    const missingRefs = new Set<ServiceOrExtensionPoint>();

    for (const [name, ref] of Object.entries(deps)) {
      const ep = this.#extensionPoints.get(ref.id);
      if (ep) {
        if (ep.pluginId !== pluginId) {
          throw new Error(
            `Illegal dependency: Module '${moduleId}' for plugin '${pluginId}' attempted to depend on extension point '${ref.id}' for plugin '${ep.pluginId}'. Extension points can only be used within their plugin's scope.`,
          );
        }
        result.set(name, ep.impl);
      } else {
        const impl = await this.#serviceRegistry.get(
          ref as ServiceRef<unknown>,
          pluginId,
        );
        if (impl) {
          result.set(name, impl);
        } else {
          missingRefs.add(ref);
        }
      }
    }

    if (missingRefs.size > 0) {
      const missing = Array.from(missingRefs).join(', ');
      const target = moduleId
        ? `module '${moduleId}' for plugin '${pluginId}'`
        : `plugin '${pluginId}'`;