diff --git a/src/core/config/config.ts b/src/core/config/config.ts index c8d2c02..ff215a6 100644 --- a/src/core/config/config.ts +++ b/src/core/config/config.ts @@ -11,6 +11,7 @@ import { DEFAULT_SUBMITTER, } from '../types/config.js'; import { LaunchPaths, type LaunchPathsService } from '../services/paths.js'; +import type { MutableDeep } from '../types/mutable.js'; /** * Absolute path to THIS package's own public entry (`defineConfig` + the config types), resolved * relative to the loader so it points at whichever copy is actually running - the globally-installed @@ -205,7 +206,7 @@ const appDescriptorFromConfig = ( appHandle = expoConfig['name']; } if (appHandle === undefined) return null; - const descriptor: AppDescriptor = { + const descriptor: MutableDeep = { name: appHandle.toLowerCase(), dir: appDirectory, configPath, diff --git a/src/core/config/env.ts b/src/core/config/env.ts index 2f2accb..a00614d 100644 --- a/src/core/config/env.ts +++ b/src/core/config/env.ts @@ -43,7 +43,7 @@ export const loadDotenvFile = (filePath: string) => * snag a publishable `EXPO_PUBLIC_..._KEY`. The single matching rule shared by {@link resolveEnv} (drops * matches before injection) and {@link missingKeys} (exempts matches from the gate), so the two agree. */ -export const isEnvExcluded = (name: string, patterns: string[]): boolean => { +export const isEnvExcluded = (name: string, patterns: readonly string[]): boolean => { for (const pattern of patterns) { if (pattern.endsWith('*')) { if (name.startsWith(pattern.slice(0, -1))) return true; @@ -64,7 +64,7 @@ export const isEnvExcluded = (name: string, patterns: string[]): boolean => { export const missingKeys = ( appDirectory: string, environment: Record, - excludedPatterns: string[] = [], + excludedPatterns: readonly string[] = [], ) => Effect.gen(function* () { const pathService = yield* Path.Path; @@ -138,7 +138,7 @@ export type ResolveEnvInput = { secrets?: Record | undefined; cliEnv?: Record | undefined; includeLocal?: boolean | undefined; - envExclude?: string[] | undefined; + envExclude?: readonly string[] | undefined; }; /** * Resolve env through the single precedence ladder (lowest -> highest, later overrides earlier): @@ -185,7 +185,7 @@ export const resolveEnv = (input: ResolveEnvInput) => // Hard denylist: an excluded name is skipped in EVERY layer, so it can never land in the result no // matter which layer (incl. the final `--env`) set it - exclusion wins over precedence by design. Names // some layer actually tried to set are recorded, so the build log reports real drops, not the raw list. - let excludedPatterns: string[] = []; + let excludedPatterns: readonly string[] = []; if (input.envExclude !== undefined) excludedPatterns = input.envExclude; const excludedSeen = new Set(); const values: Record = {}; diff --git a/src/core/config/toolchain.ts b/src/core/config/toolchain.ts index 193356f..c5f0260 100644 --- a/src/core/config/toolchain.ts +++ b/src/core/config/toolchain.ts @@ -197,7 +197,7 @@ export const remoteToolchainPreflight = (mode: 'install' | 'assert'): string => */ export type ToolchainIo = { exists(command: string): Effect.Effect; - run(command: string, args: string[]): Effect.Effect; + run(command: string, args: readonly string[]): Effect.Effect; confirm(message: string): Effect.Effect; confirmText(message: string, expected: string): Effect.Effect; log(message: string): Effect.Effect; @@ -226,7 +226,7 @@ export type EnsureToolchainOptions = { /** Return the tools from `tools` whose command isn't currently on `PATH`. */ const detectMissing = ( io: Pick, - tools: Tool[], + tools: readonly Tool[], ): Effect.Effect => Effect.filter(tools, (tool) => io.exists(tool.command).pipe(Effect.map((exists) => !exists)), { concurrency: 1, @@ -343,7 +343,7 @@ export const ensureCcacheInstalled = (options: { */ const installBrewTools = ( io: ToolchainIo, - brewTools: Tool[], + brewTools: readonly Tool[], assumeYes: boolean, ): Effect.Effect => Effect.gen(function* () {