Assignee: @copilot
Summary
The Vuex store modules for three cloud providers (linode, digitalocean, and pnap) contain an identical 29-line cache infrastructure block — the same state, mutations.setCache, getters.fromCache, and cachedCommand action are copy-pasted verbatim across all three files. Any bug fix or enhancement to the caching logic must be applied to three separate places.
Duplication Details
Pattern: Shared credential-keyed cache state + mutations + getters + cachedCommand action
- Severity: High
- Occurrences: 3 files
- Locations:
shell/store/linode.js (lines 6–96)
shell/store/digitalocean.js (lines 24–152)
shell/store/pnap.js (lines 6–75)
Identical state + mutations + getters block (22 lines — exact copy in all 3 files):
export const state = () => {
return { cache: {} };
};
export const mutations = {
setCache(state, { credentialId, key, value }) {
let cache = state.cache[credentialId];
if ( !cache ) {
cache = {};
state.cache[credentialId] = cache;
}
cache[key] = value;
},
};
export const getters = {
fromCache: (state) => ({ credentialId, key }) => {
return state.cache[credentialId]?.[key];
},
};
Nearly-identical cachedCommand action (12 lines in each file):
async cachedCommand({ getters, commit, dispatch }, { credentialId, command }) {
let out = getters['fromCache']({ credentialId, key: command });
if ( !out ) {
out = await dispatch('request', { credentialId, command });
commit('setCache', {
credentialId, key: command, value: out
});
}
return out;
},
Impact Analysis
- Maintainability: Any change to caching logic (e.g., cache invalidation, TTL support, error handling) must be duplicated across all three files.
- Bug Risk: A bug fix in one store could easily be missed in the others, leading to inconsistent behaviour.
- Code Bloat: ~34 lines repeated three times adds ~68 lines of avoidable code.
Refactoring Recommendations
-
Extract shared cache factory — Create shell/store/cloud-provider-cache.js that exports the shared state, mutations, getters, and cachedCommand action:
- Estimated effort: 1–2 hours
- Benefits: Single source of truth, easier testing, consistent behaviour
// shell/store/cloud-provider-cache.js
export const createCacheState = () => ({ cache: {} });
export const cacheMutations = {
setCache(state, { credentialId, key, value }) {
let cache = state.cache[credentialId];
if (!cache) {
cache = {};
state.cache[credentialId] = cache;
}
cache[key] = value;
},
};
export const cacheGetters = {
fromCache: (state) => ({ credentialId, key }) => state.cache[credentialId]?.[key],
};
export const cachedCommandAction = {
async cachedCommand({ getters, commit, dispatch }, { credentialId, command }) {
let out = getters['fromCache']({ credentialId, key: command });
if (!out) {
out = await dispatch('request', { credentialId, command });
commit('setCache', { credentialId, key: command, value: out });
}
return out;
},
};
-
Update each store to spread the shared pieces:
import { createCacheState, cacheMutations, cacheGetters, cachedCommandAction } from './cloud-provider-cache';
export const state = createCacheState;
export const mutations = { ...cacheMutations };
export const getters = { ...cacheGetters };
export const actions = {
...cachedCommandAction,
// provider-specific actions...
};
Implementation Checklist
Analysis Metadata
- Analyzed Files: 3 (
shell/store/linode.js, shell/store/digitalocean.js, shell/store/pnap.js)
- Detection Method: Semantic code analysis + line-by-line diff
- Commit: 3331497
- Analysis Date: 2026-07-04T21:20:52Z
Generated by Duplicate Code Detector · ● 2.2M · ◷
Assignee:
@copilotSummary
The Vuex store modules for three cloud providers (
linode,digitalocean, andpnap) contain an identical 29-line cache infrastructure block — the samestate,mutations.setCache,getters.fromCache, andcachedCommandaction are copy-pasted verbatim across all three files. Any bug fix or enhancement to the caching logic must be applied to three separate places.Duplication Details
Pattern: Shared credential-keyed cache state + mutations + getters + cachedCommand action
shell/store/linode.js(lines 6–96)shell/store/digitalocean.js(lines 24–152)shell/store/pnap.js(lines 6–75)Identical
state+mutations+gettersblock (22 lines — exact copy in all 3 files):Nearly-identical
cachedCommandaction (12 lines in each file):Impact Analysis
Refactoring Recommendations
Extract shared cache factory — Create
shell/store/cloud-provider-cache.jsthat exports the sharedstate,mutations,getters, andcachedCommandaction:Update each store to spread the shared pieces:
Implementation Checklist
shell/store/cloud-provider-cache.jsshared moduleshell/store/linode.jsto use shared moduleshell/store/digitalocean.jsto use shared moduleshell/store/pnap.jsto use shared moduleAnalysis Metadata
shell/store/linode.js,shell/store/digitalocean.js,shell/store/pnap.js)