mirror of https://github.com/openclaw/openclaw.git
perf(test): trim secrets runtime coverage
This commit is contained in:
parent
a1eb677241
commit
b432dc5af9
|
|
@ -0,0 +1 @@
|
|||
export { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export { resolveSecretRefValues } from "./resolve.js";
|
||||
export { collectAuthStoreAssignments } from "./runtime-auth-collectors.js";
|
||||
export { collectConfigAssignments } from "./runtime-config-collectors.js";
|
||||
export { applyResolvedAssignments, createResolverContext } from "./runtime-shared.js";
|
||||
export { resolveRuntimeWebTools } from "./runtime-web-tools.js";
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { PluginWebSearchProviderEntry } from "../plugins/types.js";
|
||||
|
||||
const { resolvePluginWebSearchProvidersMock } = vi.hoisted(() => ({
|
||||
resolvePluginWebSearchProvidersMock: vi.fn<() => PluginWebSearchProviderEntry[]>(() => [
|
||||
{
|
||||
pluginId: "google",
|
||||
id: "gemini",
|
||||
label: "gemini",
|
||||
hint: "gemini test provider",
|
||||
envVars: ["GEMINI_API_KEY"],
|
||||
placeholder: "gemini-...",
|
||||
signupUrl: "https://example.com/gemini",
|
||||
autoDetectOrder: 20,
|
||||
credentialPath: "plugins.entries.google.config.webSearch.apiKey",
|
||||
inactiveSecretPaths: ["plugins.entries.google.config.webSearch.apiKey"],
|
||||
getCredentialValue: (searchConfig) => searchConfig?.apiKey,
|
||||
setCredentialValue: (searchConfigTarget, value) => {
|
||||
searchConfigTarget.apiKey = value;
|
||||
},
|
||||
getConfiguredCredentialValue: (config) =>
|
||||
(config?.plugins?.entries?.google?.config as { webSearch?: { apiKey?: unknown } })
|
||||
?.webSearch?.apiKey,
|
||||
setConfiguredCredentialValue: (configTarget, value) => {
|
||||
const plugins = (configTarget.plugins ??= {}) as { entries?: Record<string, unknown> };
|
||||
const entries = (plugins.entries ??= {});
|
||||
const entry = (entries.google ??= {}) as { config?: Record<string, unknown> };
|
||||
const config = (entry.config ??= {});
|
||||
const webSearch = (config.webSearch ??= {}) as { apiKey?: unknown };
|
||||
webSearch.apiKey = value;
|
||||
},
|
||||
createTool: () => null,
|
||||
},
|
||||
]),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/web-search-providers.runtime.js", () => ({
|
||||
resolvePluginWebSearchProviders: resolvePluginWebSearchProvidersMock,
|
||||
}));
|
||||
|
||||
function asConfig(value: unknown): OpenClawConfig {
|
||||
return value as OpenClawConfig;
|
||||
}
|
||||
|
||||
let clearConfigCache: typeof import("../config/config.js").clearConfigCache;
|
||||
let clearRuntimeConfigSnapshot: typeof import("../config/config.js").clearRuntimeConfigSnapshot;
|
||||
let activateSecretsRuntimeSnapshot: typeof import("./runtime.js").activateSecretsRuntimeSnapshot;
|
||||
let clearSecretsRuntimeSnapshot: typeof import("./runtime.js").clearSecretsRuntimeSnapshot;
|
||||
let getActiveRuntimeWebToolsMetadata: typeof import("./runtime.js").getActiveRuntimeWebToolsMetadata;
|
||||
let prepareSecretsRuntimeSnapshot: typeof import("./runtime.js").prepareSecretsRuntimeSnapshot;
|
||||
|
||||
describe("runtime web tools state", () => {
|
||||
beforeAll(async () => {
|
||||
({ clearConfigCache, clearRuntimeConfigSnapshot } = await import("../config/config.js"));
|
||||
({
|
||||
activateSecretsRuntimeSnapshot,
|
||||
clearSecretsRuntimeSnapshot,
|
||||
getActiveRuntimeWebToolsMetadata,
|
||||
prepareSecretsRuntimeSnapshot,
|
||||
} = await import("./runtime.js"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearSecretsRuntimeSnapshot();
|
||||
clearRuntimeConfigSnapshot();
|
||||
clearConfigCache();
|
||||
});
|
||||
|
||||
it("exposes active runtime web tool metadata as a defensive clone", async () => {
|
||||
const snapshot = await prepareSecretsRuntimeSnapshot({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
provider: "gemini",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "WEB_SEARCH_GEMINI_API_KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: {
|
||||
WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref",
|
||||
},
|
||||
agentDirs: ["/tmp/openclaw-agent-main"],
|
||||
loadAuthStore: () => ({ version: 1, profiles: {} }),
|
||||
});
|
||||
|
||||
activateSecretsRuntimeSnapshot(snapshot);
|
||||
|
||||
const first = getActiveRuntimeWebToolsMetadata();
|
||||
expect(first?.search.providerConfigured).toBe("gemini");
|
||||
expect(first?.search.selectedProvider).toBe("gemini");
|
||||
expect(first?.search.selectedProviderKeySource).toBe("secretRef");
|
||||
if (!first) {
|
||||
throw new Error("missing runtime web tools metadata");
|
||||
}
|
||||
first.search.providerConfigured = "brave";
|
||||
first.search.selectedProvider = "brave";
|
||||
|
||||
const second = getActiveRuntimeWebToolsMetadata();
|
||||
expect(second?.search.providerConfigured).toBe("gemini");
|
||||
expect(second?.search.selectedProvider).toBe("gemini");
|
||||
});
|
||||
});
|
||||
|
|
@ -370,6 +370,48 @@ describe("runtime web tools resolution", () => {
|
|||
},
|
||||
);
|
||||
|
||||
it("resolves selected provider SecretRef even when provider config is disabled", async () => {
|
||||
const { metadata, resolvedConfig, context } = await runRuntimeWebTools({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
enabled: true,
|
||||
provider: "gemini",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
enabled: false,
|
||||
apiKey: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "WEB_SEARCH_GEMINI_API_KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: {
|
||||
WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref",
|
||||
},
|
||||
});
|
||||
|
||||
expect(metadata.search.providerConfigured).toBe("gemini");
|
||||
expect(metadata.search.selectedProvider).toBe("gemini");
|
||||
expect(readProviderKey(resolvedConfig, "gemini")).toBe("web-search-gemini-ref");
|
||||
expect(context.warnings.map((warning) => warning.path)).not.toContain(
|
||||
"plugins.entries.google.config.webSearch.apiKey",
|
||||
);
|
||||
});
|
||||
|
||||
it("auto-detects provider precedence across all configured providers", async () => {
|
||||
const { metadata, resolvedConfig, context } = await runRuntimeWebTools({
|
||||
config: asConfig({
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ let clearConfigCache: typeof import("../config/config.js").clearConfigCache;
|
|||
let clearRuntimeConfigSnapshot: typeof import("../config/config.js").clearRuntimeConfigSnapshot;
|
||||
let activateSecretsRuntimeSnapshot: typeof import("./runtime.js").activateSecretsRuntimeSnapshot;
|
||||
let clearSecretsRuntimeSnapshot: typeof import("./runtime.js").clearSecretsRuntimeSnapshot;
|
||||
let getActiveRuntimeWebToolsMetadata: typeof import("./runtime.js").getActiveRuntimeWebToolsMetadata;
|
||||
let prepareSecretsRuntimeSnapshot: typeof import("./runtime.js").prepareSecretsRuntimeSnapshot;
|
||||
|
||||
function createOpenAiFileModelsConfig(): NonNullable<OpenClawConfig["models"]> {
|
||||
|
|
@ -120,7 +119,6 @@ describe("secrets runtime snapshot", () => {
|
|||
({
|
||||
activateSecretsRuntimeSnapshot,
|
||||
clearSecretsRuntimeSnapshot,
|
||||
getActiveRuntimeWebToolsMetadata,
|
||||
prepareSecretsRuntimeSnapshot,
|
||||
} = await import("./runtime.js"));
|
||||
});
|
||||
|
|
@ -739,239 +737,6 @@ describe("secrets runtime snapshot", () => {
|
|||
expect(profile.key).toBe("primary-key-value");
|
||||
});
|
||||
|
||||
it("treats non-selected web search provider refs as inactive", async () => {
|
||||
const snapshot = await prepareSecretsRuntimeSnapshot({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
enabled: true,
|
||||
provider: "brave",
|
||||
apiKey: { source: "env", provider: "default", id: "WEB_SEARCH_API_KEY" },
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
xai: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: "MISSING_GROK_API_KEY" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: {
|
||||
WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret
|
||||
},
|
||||
agentDirs: ["/tmp/openclaw-agent-main"],
|
||||
loadAuthStore: () => ({ version: 1, profiles: {} }),
|
||||
});
|
||||
|
||||
expect(snapshot.config.tools?.web?.search?.apiKey).toBe("web-search-ref");
|
||||
const xaiWebSearchConfig = snapshot.config.plugins?.entries?.xai?.config as
|
||||
| { webSearch?: { apiKey?: unknown } }
|
||||
| undefined;
|
||||
expect(xaiWebSearchConfig?.webSearch?.apiKey).toEqual({
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "MISSING_GROK_API_KEY",
|
||||
});
|
||||
expect(snapshot.warnings).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "SECRETS_REF_IGNORED_INACTIVE_SURFACE",
|
||||
path: "plugins.entries.xai.config.webSearch.apiKey",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps non-selected provider refs inactive in web search auto mode", async () => {
|
||||
const snapshot = await prepareSecretsRuntimeSnapshot({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
enabled: true,
|
||||
apiKey: { source: "env", provider: "default", id: "WEB_SEARCH_API_KEY" },
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "WEB_SEARCH_GEMINI_API_KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: {
|
||||
WEB_SEARCH_API_KEY: "web-search-ref", // pragma: allowlist secret
|
||||
WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref", // pragma: allowlist secret
|
||||
},
|
||||
agentDirs: ["/tmp/openclaw-agent-main"],
|
||||
loadAuthStore: () => ({ version: 1, profiles: {} }),
|
||||
});
|
||||
|
||||
expect(snapshot.config.tools?.web?.search?.apiKey).toBe("web-search-ref");
|
||||
const googleWebSearchConfig = snapshot.config.plugins?.entries?.google?.config as
|
||||
| { webSearch?: { apiKey?: unknown } }
|
||||
| undefined;
|
||||
expect(googleWebSearchConfig?.webSearch?.apiKey).toEqual({
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "WEB_SEARCH_GEMINI_API_KEY",
|
||||
});
|
||||
expect(snapshot.webTools.search.selectedProvider).toBe("brave");
|
||||
expect(snapshot.warnings).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "SECRETS_REF_IGNORED_INACTIVE_SURFACE",
|
||||
path: "plugins.entries.google.config.webSearch.apiKey",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves selected web search provider ref even when provider config is disabled", async () => {
|
||||
const snapshot = await prepareSecretsRuntimeSnapshot({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
enabled: true,
|
||||
provider: "gemini",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
config: {
|
||||
webSearch: {
|
||||
enabled: false,
|
||||
apiKey: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "WEB_SEARCH_GEMINI_API_KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: {
|
||||
WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref", // pragma: allowlist secret
|
||||
},
|
||||
agentDirs: ["/tmp/openclaw-agent-main"],
|
||||
loadAuthStore: () => ({ version: 1, profiles: {} }),
|
||||
});
|
||||
const resolvedGoogleWebSearchConfig = snapshot.config.plugins?.entries?.google?.config as
|
||||
| { webSearch?: { apiKey?: unknown } }
|
||||
| undefined;
|
||||
expect(resolvedGoogleWebSearchConfig?.webSearch?.apiKey).toBe("web-search-gemini-ref");
|
||||
expect(snapshot.warnings.map((warning) => warning.path)).not.toContain(
|
||||
"plugins.entries.google.config.webSearch.apiKey",
|
||||
);
|
||||
});
|
||||
|
||||
it("fails fast at startup when selected web search provider ref is unresolved", async () => {
|
||||
await expect(
|
||||
prepareSecretsRuntimeSnapshot({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
enabled: true,
|
||||
provider: "gemini",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "MISSING_WEB_SEARCH_GEMINI_API_KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: {},
|
||||
agentDirs: ["/tmp/openclaw-agent-main"],
|
||||
loadAuthStore: () => ({ version: 1, profiles: {} }),
|
||||
}),
|
||||
).rejects.toThrow("[WEB_SEARCH_KEY_UNRESOLVED_NO_FALLBACK]");
|
||||
});
|
||||
|
||||
it("exposes active runtime web tool metadata as a defensive clone", async () => {
|
||||
const snapshot = await prepareSecretsRuntimeSnapshot({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
provider: "gemini",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "WEB_SEARCH_GEMINI_API_KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
env: {
|
||||
WEB_SEARCH_GEMINI_API_KEY: "web-search-gemini-ref", // pragma: allowlist secret
|
||||
},
|
||||
agentDirs: ["/tmp/openclaw-agent-main"],
|
||||
loadAuthStore: () => ({ version: 1, profiles: {} }),
|
||||
});
|
||||
|
||||
activateSecretsRuntimeSnapshot(snapshot);
|
||||
|
||||
const first = getActiveRuntimeWebToolsMetadata();
|
||||
expect(first?.search.providerConfigured).toBe("gemini");
|
||||
expect(first?.search.selectedProvider).toBe("gemini");
|
||||
expect(first?.search.selectedProviderKeySource).toBe("secretRef");
|
||||
if (!first) {
|
||||
throw new Error("missing runtime web tools metadata");
|
||||
}
|
||||
first.search.providerConfigured = "brave";
|
||||
first.search.selectedProvider = "brave";
|
||||
|
||||
const second = getActiveRuntimeWebToolsMetadata();
|
||||
expect(second?.search.providerConfigured).toBe("gemini");
|
||||
expect(second?.search.selectedProvider).toBe("gemini");
|
||||
});
|
||||
|
||||
it("resolves model provider request secret refs for headers, auth, and tls material", async () => {
|
||||
const config = asConfig({
|
||||
models: {
|
||||
|
|
|
|||
|
|
@ -17,27 +17,19 @@ import {
|
|||
setRuntimeConfigSnapshot,
|
||||
type OpenClawConfig,
|
||||
} from "../config/config.js";
|
||||
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import type { PluginOrigin } from "../plugins/types.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import {
|
||||
collectCommandSecretAssignmentsFromSnapshot,
|
||||
type CommandSecretAssignment,
|
||||
} from "./command-config.js";
|
||||
import { resolveSecretRefValues } from "./resolve.js";
|
||||
import { collectAuthStoreAssignments } from "./runtime-auth-collectors.js";
|
||||
import { collectConfigAssignments } from "./runtime-config-collectors.js";
|
||||
import {
|
||||
applyResolvedAssignments,
|
||||
createResolverContext,
|
||||
type SecretResolverWarning,
|
||||
} from "./runtime-shared.js";
|
||||
import { type SecretResolverWarning } from "./runtime-shared.js";
|
||||
import {
|
||||
clearActiveRuntimeWebToolsMetadata,
|
||||
getActiveRuntimeWebToolsMetadata as getActiveRuntimeWebToolsMetadataFromState,
|
||||
setActiveRuntimeWebToolsMetadata,
|
||||
} from "./runtime-web-tools-state.js";
|
||||
import { resolveRuntimeWebTools, type RuntimeWebToolsMetadata } from "./runtime-web-tools.js";
|
||||
import type { RuntimeWebToolsMetadata } from "./runtime-web-tools.js";
|
||||
|
||||
export type { SecretResolverWarning } from "./runtime-shared.js";
|
||||
|
||||
|
|
@ -75,6 +67,18 @@ const preparedSnapshotRefreshContext = new WeakMap<
|
|||
PreparedSecretsRuntimeSnapshot,
|
||||
SecretsRuntimeRefreshContext
|
||||
>();
|
||||
let runtimeManifestPromise: Promise<typeof import("./runtime-manifest.runtime.js")> | null = null;
|
||||
let runtimePreparePromise: Promise<typeof import("./runtime-prepare.runtime.js")> | null = null;
|
||||
|
||||
function loadRuntimeManifestHelpers() {
|
||||
runtimeManifestPromise ??= import("./runtime-manifest.runtime.js");
|
||||
return runtimeManifestPromise;
|
||||
}
|
||||
|
||||
function loadRuntimePrepareHelpers() {
|
||||
runtimePreparePromise ??= import("./runtime-prepare.runtime.js");
|
||||
return runtimePreparePromise;
|
||||
}
|
||||
|
||||
function cloneSnapshot(snapshot: PreparedSecretsRuntimeSnapshot): PreparedSecretsRuntimeSnapshot {
|
||||
return {
|
||||
|
|
@ -130,14 +134,15 @@ function resolveRefreshAgentDirs(
|
|||
return [...new Set([...context.explicitAgentDirs, ...configDerived])];
|
||||
}
|
||||
|
||||
function resolveLoadablePluginOrigins(params: {
|
||||
async function resolveLoadablePluginOrigins(params: {
|
||||
config: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): ReadonlyMap<string, PluginOrigin> {
|
||||
}): Promise<ReadonlyMap<string, PluginOrigin>> {
|
||||
const workspaceDir = resolveAgentWorkspaceDir(
|
||||
params.config,
|
||||
resolveDefaultAgentId(params.config),
|
||||
);
|
||||
const { loadPluginManifestRegistry } = await loadRuntimeManifestHelpers();
|
||||
const manifestRegistry = loadPluginManifestRegistry({
|
||||
config: params.config,
|
||||
workspaceDir,
|
||||
|
|
@ -172,12 +177,20 @@ export async function prepareSecretsRuntimeSnapshot(params: {
|
|||
/** Test override for discovered loadable plugins and their origins. */
|
||||
loadablePluginOrigins?: ReadonlyMap<string, PluginOrigin>;
|
||||
}): Promise<PreparedSecretsRuntimeSnapshot> {
|
||||
const {
|
||||
applyResolvedAssignments,
|
||||
collectAuthStoreAssignments,
|
||||
collectConfigAssignments,
|
||||
createResolverContext,
|
||||
resolveRuntimeWebTools,
|
||||
resolveSecretRefValues,
|
||||
} = await loadRuntimePrepareHelpers();
|
||||
const runtimeEnv = mergeSecretsRuntimeEnv(params.env);
|
||||
const sourceConfig = structuredClone(params.config);
|
||||
const resolvedConfig = structuredClone(params.config);
|
||||
const loadablePluginOrigins =
|
||||
params.loadablePluginOrigins ??
|
||||
resolveLoadablePluginOrigins({ config: sourceConfig, env: runtimeEnv });
|
||||
(await resolveLoadablePluginOrigins({ config: sourceConfig, env: runtimeEnv }));
|
||||
const context = createResolverContext({
|
||||
sourceConfig,
|
||||
env: runtimeEnv,
|
||||
|
|
@ -249,10 +262,7 @@ export function activateSecretsRuntimeSnapshot(snapshot: PreparedSecretsRuntimeS
|
|||
env: { ...process.env } as Record<string, string | undefined>,
|
||||
explicitAgentDirs: null,
|
||||
loadAuthStore: loadAuthProfileStoreForSecretsRuntime,
|
||||
loadablePluginOrigins: resolveLoadablePluginOrigins({
|
||||
config: next.sourceConfig,
|
||||
env: process.env,
|
||||
}),
|
||||
loadablePluginOrigins: new Map<string, PluginOrigin>(),
|
||||
} satisfies SecretsRuntimeRefreshContext);
|
||||
setRuntimeConfigSnapshot(next.config, next.sourceConfig);
|
||||
replaceRuntimeAuthProfileStoreSnapshots(next.authStores);
|
||||
|
|
|
|||
Loading…
Reference in New Issue