All files / lib config.ts

100% Statements 6/6
100% Branches 4/4
100% Functions 1/1
100% Lines 6/6

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                  2x                       2x 6x   6x                           2x             2x    
import { Context, Layer, Schema } from 'effect';
 
/**
 * Every environment value the app reads, in one place, validated at startup.
 *
 * Vite inlines `import.meta.env.VITE_*` at build time: the values end up as plain text in
 * `dist/`, so never put a secret in one. Access them statically — `import.meta.env[name]` is
 * not replaced and is `undefined` in the build.
 */
const Env = Schema.Struct({
  VITE_API_URL: Schema.optional(Schema.String),
  VITE_API_MOCKING: Schema.optional(Schema.Literals(['true', 'false'])),
});
 
/** The variables as Vite exposes them: strings, or undefined when unset. */
interface RawEnv {
  readonly VITE_API_URL?: string | undefined;
  readonly VITE_API_MOCKING?: string | undefined;
}
 
/** Throws a SchemaError naming the bad variable, so a misconfigured app fails at startup. */
export const parseConfig = (raw: RawEnv) => {
  const { VITE_API_URL, VITE_API_MOCKING } = Schema.decodeUnknownSync(Env)(raw);
 
  return {
    // An empty value (`VITE_API_URL=` in .env.local, an unset CI variable) counts as unset: '' would
    // send every request to the page's own host, which answers with index.html.
    apiUrl: VITE_API_URL === undefined || VITE_API_URL === '' ? '/api' : VITE_API_URL,
    apiMocking: VITE_API_MOCKING === 'true',
  } as const;
};
 
type Config = ReturnType<typeof parseConfig>;
 
/**
 * The parsed values, for code outside Effect: `main.tsx` (start MSW or not) and MSW handlers
 * (their URLs). Effect code reads `AppConfig` instead, so a test can provide other values.
 */
export const config: Config = parseConfig({
  VITE_API_URL: import.meta.env.VITE_API_URL,
  VITE_API_MOCKING: import.meta.env.VITE_API_MOCKING,
});
 
/** The config as a service: `const { apiUrl } = yield* AppConfig`. */
export class AppConfig extends Context.Service<AppConfig, Config>()('app/lib/AppConfig') {
  static readonly layer = Layer.succeed(AppConfig, config);
}