import { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import pkg from '../package.json' with { type: 'json' }; declare global { namespace globalThis { // oxlint-disable-next-line no-var var env: Readonly; // oxlint-disable-next-line no-var var readEnv: (key: string, defaultValue: T, availableValues?: T[]) => T; } } export enum Flavor { AllInOne = 'allinone', Graphql = 'graphql', Sync = 'sync', Renderer = 'renderer', Doc = 'doc', Script = 'script', } export enum Namespace { Dev = 'dev', Beta = 'beta', Production = 'production', } export enum NodeEnv { Development = 'development', Test = 'test', Production = 'production', } export enum DeploymentType { Affine = 'affine', Selfhosted = 'selfhosted', } export enum Platform { GCP = 'gcp', Unknown = 'unknown', } export type AppEnv = { NODE_ENV: NodeEnv; NAMESPACE: Namespace; DEPLOYMENT_TYPE: DeploymentType; version: string; }; globalThis.readEnv = function readEnv( env: string, defaultValue: T, availableValues?: T[] ) { const value = process.env[env]; if (value === undefined) { return defaultValue; } if (availableValues && !availableValues.includes(value as any)) { throw new Error( `Invalid value "${value}" for environment variable ${env}, expected one of ${JSON.stringify( availableValues )}` ); } return value as T; }; export class Env implements AppEnv { NODE_ENV = readEnv('NODE_ENV', NodeEnv.Production, Object.values(NodeEnv)); NAMESPACE = readEnv( 'AFFINE_ENV', Namespace.Production, Object.values(Namespace) ); DEPLOYMENT_TYPE = readEnv( 'DEPLOYMENT_TYPE', this.dev ? DeploymentType.Affine : DeploymentType.Selfhosted, Object.values(DeploymentType) ); FLAVOR = readEnv('SERVER_FLAVOR', Flavor.AllInOne, Object.values(Flavor)); platform = readEnv('DEPLOYMENT_PLATFORM', Platform.Unknown); version = pkg.version; projectRoot = resolve(fileURLToPath(import.meta.url), '../../'); get selfhosted() { return this.DEPLOYMENT_TYPE === DeploymentType.Selfhosted; } isFlavor(flavor: Flavor) { return this.FLAVOR === flavor || this.FLAVOR === Flavor.AllInOne; } get flavors() { return { graphql: this.isFlavor(Flavor.Graphql), sync: this.isFlavor(Flavor.Sync), renderer: this.isFlavor(Flavor.Renderer), doc: this.isFlavor(Flavor.Doc), // Script in a special flavor, return true only when it is set explicitly script: this.FLAVOR === Flavor.Script, }; } get namespaces() { return { canary: this.NAMESPACE === Namespace.Dev, beta: this.NAMESPACE === Namespace.Beta, production: this.NAMESPACE === Namespace.Production, }; } get testing() { return this.NODE_ENV === NodeEnv.Test; } get dev() { return this.NODE_ENV === NodeEnv.Development; } get prod() { return this.NODE_ENV === NodeEnv.Production; } get gcp() { return this.platform === Platform.GCP; } } globalThis.env = new Env();