fix(profile): project IPC inputs to consumed fields

This commit is contained in:
Tunglies
2026-10-08 01:57:25 +08:00
parent 2e439b5e1a
commit 66a12607ce
6 changed files with 238 additions and 26 deletions
+51 -3
View File
@@ -140,13 +140,13 @@ export const commands = {
__TAURI_INVOKE<NetworkInterfaceView[]>('get_network_interfaces_info'),
getProfiles: () => __TAURI_INVOKE<IProfiles_Serialize>('get_profiles'),
enhanceProfiles: () => __TAURI_INVOKE<ValidationOutcome>('enhance_profiles'),
patchProfilesConfig: (profiles: IProfiles_Deserialize) =>
patchProfilesConfig: (profiles: ProfileSelection) =>
__TAURI_INVOKE<ValidationOutcome>('patch_profiles_config', { profiles }),
viewProfile: (index: string) =>
__TAURI_INVOKE<null>('view_profile', { index }),
patchProfile: (index: string, profile: PrfItem_Deserialize) =>
patchProfile: (index: string, profile: ProfilePatch_Deserialize) =>
__TAURI_INVOKE<null>('patch_profile', { index, profile }),
createProfile: (item: PrfItem_Deserialize, fileData: string | null) =>
createProfile: (item: ProfileCreate_Deserialize, fileData: string | null) =>
__TAURI_INVOKE<null>('create_profile', { item, fileData }),
importProfile: (
url: string,
@@ -733,6 +733,24 @@ export type PrfSelected = {
now?: string | null
}
export type ProfileCreate = ProfileCreate_Serialize | ProfileCreate_Deserialize
export type ProfileCreate_Deserialize = {
type?: string | null
name?: string | null
desc?: string | null
url?: string | null
option?: PrfOption_Deserialize | null
}
export type ProfileCreate_Serialize = {
type: string | null
name: string | null
desc: string | null
url: string | null
option: PrfOption_Serialize | null
}
export type ProfileDnsSettings =
| ProfileDnsSettings_Serialize
| ProfileDnsSettings_Deserialize
@@ -747,6 +765,36 @@ export type ProfileDnsSettings_Serialize = {
confirmation?: string | null
}
export type ProfilePatch = ProfilePatch_Serialize | ProfilePatch_Deserialize
export type ProfilePatch_Deserialize = {
type?: string | null
name?: string | null
desc?: string | null
file?: string | null
url?: string | null
selected?: PrfSelected[] | null
extra?: PrfExtra | null
updated?: number | null
option?: PrfOption_Deserialize | null
}
export type ProfilePatch_Serialize = {
type: string | null
name: string | null
desc: string | null
file: string | null
url: string | null
selected: PrfSelected[] | null
extra: PrfExtra | null
updated: number | null
option: PrfOption_Serialize | null
}
export type ProfileSelection = {
current?: string | null
}
export type ProfileUpdatePayload = {
uid: string
}
+63
View File
@@ -0,0 +1,63 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { commands } from './bindings'
import { createProfile, patchProfile, patchProfilesConfig } from './cmds'
vi.mock('./bindings', () => ({
commands: {
createProfile: vi.fn(),
patchProfile: vi.fn(),
patchProfilesConfig: vi.fn(),
},
}))
const wirePayload = (value: unknown) => JSON.parse(JSON.stringify(value))
beforeEach(() => vi.clearAllMocks())
describe('profile IPC projections', () => {
it('sends only current for profile selection even with a full snapshot', async () => {
const snapshot = { current: 'profile', items: [{ uid: 'profile' }] }
await patchProfilesConfig(snapshot)
expect(commands.patchProfilesConfig).toHaveBeenCalledWith({
current: 'profile',
})
})
it('strips profile output fields while retaining explicit selections and options', async () => {
const selected = [{ name: 'group', now: 'node' }]
const option = {
script: 'script',
merge: 'merge',
rules: 'rules',
proxies: 'proxies',
groups: 'groups',
}
const profile = {
uid: 'profile',
home: 'https://example.com',
file_data: 'unused',
name: 'renamed',
selected,
option,
}
await patchProfile('profile', profile)
expect(
wirePayload(vi.mocked(commands.patchProfile).mock.calls[0][1]),
).toEqual({ name: 'renamed', selected, option })
})
it('omits ignored creation fields and a local profile URL', async () => {
const profile = {
type: 'local',
name: 'local',
url: '',
uid: 'old',
selected: [{ name: 'group', now: 'node' }],
}
await createProfile(profile, 'content')
const [payload, content] = vi.mocked(commands.createProfile).mock.calls[0]
expect(wirePayload(payload)).toEqual({ type: 'local', name: 'local' })
expect(content).toBe('content')
})
})
+30 -11
View File
@@ -12,8 +12,9 @@ import type {
ProxyPortSettings,
UnlockItem,
ProxyViewV1_Serialize as ProxyViewV1,
IProfiles_Deserialize,
PrfItem_Deserialize,
ProfileSelection,
ProfilePatch,
ProfileCreate,
} from './bindings'
export type {
@@ -41,15 +42,24 @@ export async function enhanceProfiles() {
return (await commands.enhanceProfiles()).status === 'valid'
}
export async function patchProfilesConfig(profiles: IProfiles_Deserialize) {
return commands.patchProfilesConfig(profiles)
export async function patchProfilesConfig(profiles: ProfileSelection) {
return commands.patchProfilesConfig({ current: profiles.current })
}
export async function createProfile(
item: PrfItem_Deserialize,
item: ProfileCreate,
fileData?: string | null,
) {
return commands.createProfile(item, fileData ?? null)
return commands.createProfile(
{
type: item.type,
name: item.name,
desc: item.desc,
...(item.type === 'remote' ? { url: item.url } : {}),
option: item.option,
},
fileData ?? null,
)
}
export async function viewProfile(index: string) {
@@ -80,11 +90,20 @@ export async function deleteProfile(index: string) {
return commands.deleteProfile(index)
}
export async function patchProfile(
index: string,
profile: PrfItem_Deserialize,
) {
return commands.patchProfile(index, profile)
export async function patchProfile(index: string, profile: ProfilePatch) {
const { type, name, desc, file, url, selected, extra, updated, option } =
profile
return commands.patchProfile(index, {
type,
name,
desc,
file,
url,
selected,
extra,
updated,
option,
})
}
export async function getClashInfo() {