diff --git a/eslint.config.mjs b/eslint.config.mjs index a43fee03..0586b65a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -69,6 +69,25 @@ export default defineConfig([ 'no-unused-vars': 'warn', 'no-case-declarations': 'off', 'no-control-regex': 'warn', + // Store boundary rule: + // 1) Disallow `xxxStore.xxx = ...` + // 2) Disallow `xxxStore.xxx++ / --` + // Reason: prevent direct cross-store mutation and enforce owner-store actions. + 'no-restricted-syntax': [ + 'error', + { + selector: + "AssignmentExpression[left.type='MemberExpression'][left.object.type='Identifier'][left.object.name=/Store$/]", + message: + 'Do not mutate store state directly via *Store.* assignment. Use owner-store actions.' + }, + { + selector: + "UpdateExpression[argument.type='MemberExpression'][argument.object.type='Identifier'][argument.object.name=/Store$/]", + message: + 'Do not mutate store state directly via *Store.* update operators. Use owner-store actions.' + } + ], 'vue/no-mutating-props': 'warn', 'vue/multi-word-component-names': 'off', diff --git a/src/stores/avatar.js b/src/stores/avatar.js index 494bae16..789dbb87 100644 --- a/src/stores/avatar.js +++ b/src/stores/avatar.js @@ -117,28 +117,7 @@ export const useAvatarStore = defineStore('Avatar', () => { favoriteStore.applyFavorite('avatar', ref.id); if (favoriteStore.localAvatarFavoritesList.includes(ref.id)) { const avatarRef = ref; - for ( - let i = 0; - i < favoriteStore.localAvatarFavoriteGroups.length; - ++i - ) { - const groupName = favoriteStore.localAvatarFavoriteGroups[i]; - if (!favoriteStore.localAvatarFavorites[groupName]) { - continue; - } - for ( - let j = 0; - j < favoriteStore.localAvatarFavorites[groupName].length; - ++j - ) { - const favoriteRef = - favoriteStore.localAvatarFavorites[groupName][j]; - if (favoriteRef.id === avatarRef.id) { - favoriteStore.localAvatarFavorites[groupName][j] = - avatarRef; - } - } - } + favoriteStore.syncLocalAvatarFavoriteRef(avatarRef); // update db cache database.addAvatarToCache(avatarRef); diff --git a/src/stores/favorite.js b/src/stores/favorite.js index f35b0d4f..c7acd405 100644 --- a/src/stores/favorite.js +++ b/src/stores/favorite.js @@ -970,6 +970,27 @@ export const useFavoriteStore = defineStore('Favorite', () => { friendImportDialogInput.value = value; } + /** + * @param {object} avatarRef + */ + function syncLocalAvatarFavoriteRef(avatarRef) { + if (!avatarRef?.id) { + return; + } + for (let i = 0; i < localAvatarFavoriteGroups.value.length; ++i) { + const groupName = localAvatarFavoriteGroups.value[i]; + const group = localAvatarFavorites[groupName]; + if (!group) { + continue; + } + for (let j = 0; j < group.length; ++j) { + if (group[j]?.id === avatarRef.id) { + group[j] = avatarRef; + } + } + } + } + /** * * @param {string} worldId @@ -1892,6 +1913,7 @@ export const useFavoriteStore = defineStore('Favorite', () => { setAvatarImportDialogInput, setWorldImportDialogInput, setFriendImportDialogInput, + syncLocalAvatarFavoriteRef, addLocalWorldFavorite, hasLocalWorldFavorite, hasLocalAvatarFavorite,