Files
ss-tools/frontend/src/lib/stores/environmentContext.ts
busya 03557d38cc feat(ts-migration): Phase 2.4 — all stores JS→TS with contracts
Converted 10 files:
  - stores.ts [C:3] — plugins, tasks, selection stores + fetchPlugins/fetchTasks
  - stores/health.ts [C:3] — health summary with polling dedup + derived failingCount
  - stores/activity.ts [C:2] — derived active task count for navbar badge
  - stores/sidebar.ts [C:3] — sidebar expansion, navigation, mobile overlay + localStorage persist
  - stores/taskDrawer.ts [C:3] — drawer open/close, resource-to-task mapping, auto-open pref
  - stores/environmentContext.ts [C:3] — environment selector, localStorage persist, derived env
  - stores/assistantChat.ts [C:3] — panel toggle, conversation binding, seed messages, focus target
  - stores/datasetReviewSession.ts [C:4] — session CRUD, dirty flag, UiPhase derivation
  - stores/translationRun.ts [C:4] — WebSocket + polling, uxState FSM, derived active/finished
  - stores/maintenance.svelte.ts [C:4] — Svelte 5 runes (), WS reconnect, CRUD

Build: npm run build passes
2026-06-01 15:24:10 +03:00

158 lines
4.5 KiB
TypeScript

// #region EnvironmentContextStore [C:3] [TYPE Store] [SEMANTICS environment, context, store, selector, filter]
// @BRIEF Global selected environment context for navigation, safety cues, and environment-based filtering.
// @LAYER UI
// @RELATION DEPENDS_ON -> [ApiModule]
// @RELATION BINDS_TO -> [SidebarStore]
// @UX_STATE Loading -> Environments being fetched
// @UX_STATE Ready -> Environment selector active with environment list
// @UX_STATE Error -> Failed to load environments
import { derived, get, writable, type Writable } from 'svelte/store';
import { browser } from '$app/environment';
import { api } from '$lib/api';
interface Environment {
id: string;
name: string;
stage?: string;
is_production?: boolean;
[k: string]: unknown;
}
interface EnvironmentContextState {
environments: Environment[];
selectedEnvId: string;
isLoading: boolean;
isLoaded: boolean;
error: string | null;
}
const INITIAL_STATE: EnvironmentContextState = {
environments: [],
selectedEnvId: '',
isLoading: false,
isLoaded: false,
error: null,
};
const SELECTED_ENV_KEY = 'selected_env_id';
const contextStore: Writable<EnvironmentContextState> = writable(INITIAL_STATE);
function getStoredSelectedEnvId(): string {
if (!browser) return '';
return localStorage.getItem(SELECTED_ENV_KEY) || '';
}
function persistSelectedEnvId(envId: string): void {
if (!browser) return;
if (!envId) {
localStorage.removeItem(SELECTED_ENV_KEY);
return;
}
localStorage.setItem(SELECTED_ENV_KEY, envId);
}
function hasAuthToken(): boolean {
if (!browser) return false;
return Boolean(localStorage.getItem('auth_token'));
}
function resolveSelectedEnvId(environments: Environment[], preferredEnvId: string): string {
if (!Array.isArray(environments) || environments.length === 0) return '';
if (preferredEnvId && environments.some((env) => env.id === preferredEnvId)) {
return preferredEnvId;
}
const stored = getStoredSelectedEnvId();
if (stored && environments.some((env) => env.id === stored)) {
return stored;
}
return environments[0].id;
}
function applySelectedEnvId(selectedEnvId: string): void {
contextStore.update((state) => {
const exists = state.environments.some((env) => env.id === selectedEnvId);
const nextSelectedEnvId = exists ? selectedEnvId : '';
persistSelectedEnvId(nextSelectedEnvId);
return { ...state, selectedEnvId: nextSelectedEnvId };
});
}
async function refreshEnvironmentContext(preferredEnvId: string = ''): Promise<void> {
if (!hasAuthToken()) {
contextStore.update((state) => ({
...state,
isLoading: false,
isLoaded: false,
error: null,
}));
return;
}
contextStore.update((state) => ({ ...state, isLoading: true, error: null }));
try {
const environments = await api.getEnvironmentsList<Environment[]>();
const current = get(contextStore).selectedEnvId;
const selectedEnvId = resolveSelectedEnvId(environments, preferredEnvId || current);
persistSelectedEnvId(selectedEnvId);
contextStore.update((state) => ({
...state,
environments,
selectedEnvId,
isLoading: false,
isLoaded: true,
error: null,
}));
} catch (error: unknown) {
const apiError = error as { status?: number; message?: string };
if (apiError?.status === 401) {
contextStore.update((state) => ({
...state,
isLoading: false,
isLoaded: false,
error: null,
}));
return;
}
console.error('[environmentContext] Failed to refresh environments', error);
contextStore.update((state) => ({
...state,
environments: [],
selectedEnvId: '',
isLoading: false,
isLoaded: true,
error: apiError?.message || 'Failed to load environments',
}));
}
}
async function initializeEnvironmentContext(): Promise<void> {
const state = get(contextStore);
if (state.isLoading || state.isLoaded) return;
await refreshEnvironmentContext();
}
export const environmentContextStore = {
subscribe: contextStore.subscribe,
};
export function setSelectedEnvironment(envId: string): void {
applySelectedEnvId(envId);
}
export { refreshEnvironmentContext, initializeEnvironmentContext };
export const selectedEnvironmentStore = derived(
environmentContextStore,
($context: EnvironmentContextState) =>
$context.environments.find((env) => env.id === $context.selectedEnvId) || null,
);
export const isProductionContextStore = derived(
selectedEnvironmentStore,
($selectedEnvironment: Environment | null) =>
String($selectedEnvironment?.stage || '').toUpperCase() === 'PROD' ||
Boolean($selectedEnvironment?.is_production),
);
// #endregion EnvironmentContextStore