All files / kernel-platforms/src factory.ts

95.23% Statements 20/21
83.33% Branches 5/6
100% Functions 6/6
95% Lines 19/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94                            3x       14x 19x 2x 4x       12x                 3x         9x                     9x       14x   14x     15x 15x       15x             15x             15x             14x     14x     9x    
import { assert } from '@metamask/superstruct';
import type { Infer } from '@metamask/superstruct';
 
import { platformConfigStruct } from './capabilities/index.ts';
import type {
  PlatformFactory,
  PlatformConfig,
  Platform,
  Capability,
  CapabilityName,
  CapabilityFactories,
  PlatformOptions,
} from './types.ts';
 
const validatePlatformConfig = (
  config: Infer<typeof platformConfigStruct>,
  known: CapabilityName[],
): void => {
  const configured = Object.keys(config) as CapabilityName[];
  if (configured.some((name) => !known.includes(name))) {
    throw new Error(
      `Config provided entry for unregistered capability: ${configured.find((name) => !known.includes(name))}`,
      { cause: { configured, known } },
    );
  }
  assert(config, platformConfigStruct);
};
 
/**
 * Creates a platform factory from capability factories
 *
 * @param capabilityFactories - The capability factories to use
 * @returns A platform factory function
 */
export const makePlatformFactory = <
  Factories extends Partial<CapabilityFactories>,
>(
  capabilityFactories: Factories,
): PlatformFactory<CapabilityName, Factories> => {
  const knownCapabilities = Object.keys(
    capabilityFactories,
  ) as CapabilityName[];
 
  /**
   * Creates a platform with the specified capabilities
   *
   * @param config - The configuration for the platform
   * @param options - The options for the platform
   * @returns An object with the specified capabilities
   */
  const createPlatform = async (
    config: Partial<PlatformConfig>,
    options?: Partial<PlatformOptions<Factories>>,
  ): Promise<Platform<keyof typeof config>> => {
    validatePlatformConfig(config, knownCapabilities);
 
    const capabilityEntries = Object.entries(config).map(
      ([name, capabilityConfig]) => {
        const factory =
          capabilityFactories[name as (typeof knownCapabilities)[number]];
        Iif (!factory) {
          throw new Error(`No factory found for capability: ${name}`);
        }
 
        const capabilityOptions = options?.[name as keyof typeof config] ?? {};
 
        // The `any` type assertion is necessary here because TypeScript cannot infer that:
        // 1. The factory for 'name' is specifically typed for that capability
        // 2. The config for 'name' matches the factory's expected config type
        // 3. The generic constraints align between the factory and config
        // This is a limitation of TypeScript's type system with dynamic property access
        const capability = factory(
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          capabilityConfig as any,
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          capabilityOptions as any,
        );
 
        return [name, harden(capability)] as [
          keyof typeof config,
          Capability<keyof typeof config>,
        ];
      },
    );
 
    const platform = Object.fromEntries(capabilityEntries) as Platform<
      keyof typeof config
    >;
    return harden(platform);
  };
 
  return harden(createPlatform);
};