<repo_name>PedroHurtado/httpapi<file_sep>/src/services/userservice.js
import userprovider from '../providers/userprovider'
export class UserService{
    constructor(userprovider){
        this._userprovider = userprovider;
    }
    async refreshToken(refresh_token){
        //TODO:refresh token
        //await this._userprovider.add()
    }
}

export default new UserService(userprovider);<file_sep>/src/providers/cacheprovider.js
const key = "api"
//TODO:cachestrategies
export class CacheProviders {
    constructor(key,cacheProvider){
        this._key = key;
        this._cacheProvider = cacheProvider;
        this._cache = null;
    }
    get async cache(){
        if(!this._cache){
            this._cache = await this._cacheProvider.open(this._key);
        }
        return this._cache;
    }  
    async put(request,response){
        await this.cache.put(request.clone(),response.clone());
    }
    async match(request){
        return await this.cache.match(request);
    }
}
export default new CacheProviders(key,caches)<file_sep>/src/providers/cultureprovider.js
import * idb from 'idb-keyval'
import userprovider from './userprovider'
const suportcultures = ['es','en']
const defaultculture = 'en'
const key="culture"
export class CultureProvider{
    constructor(idb,userprovider,languages){
        this._idb = idb;
        this._userprovider=userprovider;
        this._languajes = languages;
        this._culture = null;
    }
    async add(culture){
        await this._idb.set(key,culture);
        this._culture = null;
    }
    async get culture(){
        if(!this._culture){
            this._culture  = await this.get();
        }
        return this._culture;
    }
    _getDefaultCulture(){
        for(let i=0;i<languages.length){
            let language=languages[i];
            if(suportcultures.includes(language)){
                return language;
            }
        }
        return defaultculture;
    }
    async get(){
        let culture = await this._idb(key);
        if(!culture){
            const user = await this._userprovider.get();
            if(user){
                culture = user.culture;
            }else{
                culture = this._getDefaultCulture();
            }
            await this.add(culture);
        }
        return culture;
    }
}

export default new CultureProvider(idb,userprovider,navigator.languages)<file_sep>/src/interceptors.js
import {
    NotFound,
    BadRequest,
    Unauthorized,
    ForBiden,
    NotAllowed,
    ServerError
} from './customerrors.js'

const OK = async (req, res) => await res.json();
const NOCONTENT = async (req,res)=>'';
const NOTFOUND = async (req, res) => {throw new NotFound(res.statusText);}
const BADREQUEST = async (req, res) => {
    const data = await res.json()
    throw new BadRequest(data,res.statusText)
};
const UNAUTHORIZED = async (req, res) => {throw new Unauthorized(res.statusText);}
const FORBIDEN = async (req, res) => {throw new ForBiden(res.statusText);}
const NOTALLOWED = async (req, res) => {throw new NotAllowed(res.statusText);}
const SERVERERROR = async (req, res) => {
    const data = await res.json()
    throw new ServerError(data,res.statusText);
};

const GLOBALERRORS = {
    "400": BADREQUEST,
    "401": UNAUTHORIZED,
    "403": FORBIDEN,
    "405": NOTALLOWED,
    "500": SERVERERROR
}
export const DEFAULTINTERCEPTORS = {
    "GET": {
        "200" :OK,
        "404": NOTFOUND,
        ...GLOBALERRORS
    },
    "POST": {
        "201": OK,
        ...GLOBALERRORS
    },
    "PUT": {
        "200": OK,
        "204":NOCONTENT,
        "404": NOTFOUND,
        ...GLOBALERRORS
    },
    "PATCH": {
        "200": OK,
        "204":NOCONTENT,
        "404": NOTFOUND,
        ...GLOBALERRORS
    },
    "DELETE": {
        "200": OK,
        "204":NOCONTENT,
        "404": NOTFOUND,
        ...GLOBALERRORS
    }
}<file_sep>/src/after.js
import {validate} from './validate.js'
export default after = async (request, response, interceptors) => {
    validate(request, 'request is required');
    validate(response, 'response is required');
    validate(interceptors, 'interceptors is required');

    const { method, status } = response;

    const methodinterceptors = interceptors[method];
    validate(methodinterceptors, `${method} not defined in interceptors`)
    
    const interceptor = interceptors[methodinterceptors];
    validate(interceptor, `status code:${status} not defined in interceptor ${method}`)
    return await interceptor(request, response)
}<file_sep>/src/customerrors.js
const Mixin = Base => class extends Base{
    constructor(name,code,...params){
        super(...params)
        if(Error.captureStackTrace){
            Error.captureStackTrace(this,NotFound)
        }
        this.name = name;
        this.code = code;
        this.date = Date.now();
    }
}

export class NotFound extends Mixin(Error){
    constructor(...params){
        super("NotFound", 404,...params);
    }
}

export class BadRequest extends Mixin(Error){
    constructor(data,...params){
        super("BadRequest",400,...params);
        this.data = data;
    }
}

export class Unauthorized extends Error{
    constructor(...params){
        super("Unauthorized",401,...params)
    }
}

export class ForBiden extends Error{
    constructor(...params){
        super("ForBiden",403,...params)
    }
}

export class NotAllowed extends Error{
    constructor(...params){
        super("NotAllowed",405,...params)
    }
}

export class ServerError extends Error{
    constructor(data,...params){
        super("ServerError",500,...params)
        this.data = data;
    }
}<file_sep>/src/providers/userprovider.js
import * as idb from 'idb-keyval'
const KEY = 'USER'
export class UserProvider{
    constructor(idb){
        this._idb = idb;
    }
    async add(user){
        await this._idb.set(KEY,user)
    }
    async get(){
        return await this._idb.get(KEY)
    }
    async remove(){
        await this._idb.del(KEY)
    }
}
export default new UserProvider(idb);<file_sep>/src/ioccontainer.js
//TODO estrategies singleton,value,factory,instance,etc...
import { validate } from './validate.js'

export const singleton = function (key, ...dependecies) {
    return () => { }
}
export const instance = function (key, ...dependecies) {
    return () => { }
}
export const value = function (key,value) {
    return () => { }
}
export const factory = function (key, ...dependecies) {
    return () => { }
}
export class IocContainer {
    constructor() {
        this._dependencies = new Map();
        this._caches = new Map();
    }
    set(key, strategy) {
        validate(key, 'key is required');
        validate(strategy, 'strategy is required');
        this._dependencies.set(key, strategy.bind(this))
    }
    get(key) {
        validate(key, 'key is required');
        const strategy = this._dependencies.get(key)
        if (!strategy) {
            validate(`${key} dependecies is not defined`)
        }
        return strategy();
    }
}