@fctc/interface-logic 1.7.7 → 1.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/provider.js CHANGED
@@ -81,16 +81,10 @@ var breadcrums_slice_default = breadcrumbsSlice.reducer;
81
81
  var import_toolkit2 = require("@reduxjs/toolkit");
82
82
  var initialState2 = {
83
83
  baseUrl: "",
84
+ requests: null,
84
85
  companies: [],
85
86
  user: {},
86
- db: "",
87
- refreshTokenEndpoint: "",
88
- config: {
89
- grantType: "",
90
- clientId: "",
91
- clientSecret: "",
92
- redirectUri: ""
93
- },
87
+ config: null,
94
88
  envFile: null,
95
89
  defaultCompany: {
96
90
  id: null,
@@ -2807,19 +2801,303 @@ function matchDomain(record, domain) {
2807
2801
 
2808
2802
  // src/utils/function.ts
2809
2803
  var import_react = require("react");
2804
+ var updateTokenParamInOriginalRequest = (originalRequest, newAccessToken) => {
2805
+ if (!originalRequest.data) return originalRequest.data;
2806
+ if (typeof originalRequest.data === "string") {
2807
+ try {
2808
+ const parsedData = JSON.parse(originalRequest.data);
2809
+ if (parsedData.with_context && typeof parsedData.with_context === "object") {
2810
+ parsedData.with_context.token = newAccessToken;
2811
+ }
2812
+ return JSON.stringify(parsedData);
2813
+ } catch (e) {
2814
+ console.warn("Failed to parse originalRequest.data", e);
2815
+ return originalRequest.data;
2816
+ }
2817
+ }
2818
+ if (typeof originalRequest.data === "object" && originalRequest.data.with_context) {
2819
+ originalRequest.data.with_context.token = newAccessToken;
2820
+ }
2821
+ return originalRequest.data;
2822
+ };
2810
2823
 
2811
- // src/environment/EnvStore.ts
2812
- var requests = {
2813
- get: async (url, headers) => ({}),
2814
- post: async (url, body, headers) => ({}),
2815
- post_excel: async (url, body, headers) => ({}),
2816
- put: async (url, body, headers) => ({}),
2817
- patch: async (url, body) => ({}),
2818
- delete: async (url, body) => ({})
2824
+ // src/utils/storage/local-storage.ts
2825
+ var localStorageUtils = () => {
2826
+ const setToken = async (access_token) => {
2827
+ localStorage.setItem("accessToken", access_token);
2828
+ };
2829
+ const setRefreshToken = async (refresh_token) => {
2830
+ localStorage.setItem("refreshToken", refresh_token);
2831
+ };
2832
+ const getAccessToken = async () => {
2833
+ return localStorage.getItem("accessToken");
2834
+ };
2835
+ const getRefreshToken = async () => {
2836
+ return localStorage.getItem("refreshToken");
2837
+ };
2838
+ const clearToken = async () => {
2839
+ localStorage.removeItem("accessToken");
2840
+ localStorage.removeItem("refreshToken");
2841
+ };
2842
+ return {
2843
+ setToken,
2844
+ setRefreshToken,
2845
+ getAccessToken,
2846
+ getRefreshToken,
2847
+ clearToken
2848
+ };
2819
2849
  };
2850
+
2851
+ // src/utils/storage/session-storage.ts
2852
+ var sessionStorageUtils = () => {
2853
+ const getBrowserSession = async () => {
2854
+ return sessionStorage.getItem("browserSession");
2855
+ };
2856
+ return {
2857
+ getBrowserSession
2858
+ };
2859
+ };
2860
+
2861
+ // src/configs/axios-client.ts
2862
+ var axiosClient = {
2863
+ init(config) {
2864
+ const localStorage2 = config?.localStorageUtils ?? localStorageUtils();
2865
+ const sessionStorage2 = config?.sessionStorageUtils ?? sessionStorageUtils();
2866
+ const db = config?.db;
2867
+ let isRefreshing = false;
2868
+ let failedQueue = [];
2869
+ const processQueue = (error, token = null) => {
2870
+ failedQueue?.forEach((prom) => {
2871
+ if (error) {
2872
+ prom.reject(error);
2873
+ } else {
2874
+ prom.resolve(token);
2875
+ }
2876
+ });
2877
+ failedQueue = [];
2878
+ };
2879
+ const instance = import_axios.default.create({
2880
+ adapter: import_axios.default.defaults.adapter,
2881
+ baseURL: config.baseUrl,
2882
+ timeout: 5e4,
2883
+ paramsSerializer: (params) => new URLSearchParams(params).toString()
2884
+ });
2885
+ instance.interceptors.request.use(
2886
+ async (config2) => {
2887
+ const useRefreshToken = config2.useRefreshToken;
2888
+ const token = useRefreshToken ? await localStorage2.getRefreshToken() : await localStorage2.getAccessToken();
2889
+ if (token) {
2890
+ config2.headers["Authorization"] = "Bearer " + token;
2891
+ }
2892
+ return config2;
2893
+ },
2894
+ (error) => {
2895
+ Promise.reject(error);
2896
+ }
2897
+ );
2898
+ instance.interceptors.response.use(
2899
+ (response) => {
2900
+ return handleResponse(response);
2901
+ },
2902
+ async (error) => {
2903
+ const handleError3 = async (error2) => {
2904
+ if (!error2.response) {
2905
+ return error2;
2906
+ }
2907
+ const { data } = error2.response;
2908
+ if (data && data.code === 400 && ["invalid_grant"].includes(data.data?.error)) {
2909
+ await clearAuthToken();
2910
+ }
2911
+ return data;
2912
+ };
2913
+ const originalRequest = error.config;
2914
+ if ((error.response?.status === 403 || error.response?.status === 401 || error.response?.status === 404) && ["TOKEN_EXPIRED", "AUTHEN_FAIL", 401, "ERR_2FA_006"].includes(
2915
+ error.response.data.code
2916
+ )) {
2917
+ if (isRefreshing) {
2918
+ return new Promise(function(resolve, reject) {
2919
+ failedQueue.push({ resolve, reject });
2920
+ }).then((token) => {
2921
+ originalRequest.headers["Authorization"] = "Bearer " + token;
2922
+ originalRequest.data = updateTokenParamInOriginalRequest(
2923
+ originalRequest,
2924
+ token
2925
+ );
2926
+ return instance.request(originalRequest);
2927
+ }).catch(async (err) => {
2928
+ if ((err.response?.status === 400 || err.response?.status === 401) && ["invalid_grant"].includes(err.response.data.error)) {
2929
+ await clearAuthToken();
2930
+ }
2931
+ });
2932
+ }
2933
+ const browserSession = await sessionStorage2.getBrowserSession();
2934
+ const refreshToken = await localStorage2.getRefreshToken();
2935
+ const accessTokenExp = await localStorage2.getAccessToken();
2936
+ isRefreshing = true;
2937
+ if (!refreshToken && (!browserSession || browserSession == "unActive")) {
2938
+ await clearAuthToken();
2939
+ } else {
2940
+ const payload = Object.fromEntries(
2941
+ Object.entries({
2942
+ refresh_token: refreshToken,
2943
+ grant_type: "refresh_token",
2944
+ client_id: config.config.clientId,
2945
+ client_secret: config.config.clientSecret
2946
+ }).filter(([_, value]) => !!value)
2947
+ );
2948
+ return new Promise(function(resolve) {
2949
+ import_axios.default.post(
2950
+ `${config.baseUrl}${config.refreshTokenEndpoint ?? "/authentication/oauth2/token" /* AUTH_TOKEN_PATH */}`,
2951
+ payload,
2952
+ {
2953
+ headers: {
2954
+ "Content-Type": config.refreshTokenEndpoint ? "application/x-www-form-urlencoded" : "multipart/form-data",
2955
+ Authorization: `Bearer ${accessTokenExp}`
2956
+ }
2957
+ }
2958
+ ).then(async (res) => {
2959
+ const data = res.data;
2960
+ await localStorage2.setToken(data.access_token);
2961
+ await localStorage2.setRefreshToken(data.refresh_token);
2962
+ import_axios.default.defaults.headers.common["Authorization"] = "Bearer " + data.access_token;
2963
+ originalRequest.headers["Authorization"] = "Bearer " + data.access_token;
2964
+ originalRequest.data = updateTokenParamInOriginalRequest(
2965
+ originalRequest,
2966
+ data.access_token
2967
+ );
2968
+ processQueue(null, data.access_token);
2969
+ resolve(instance.request(originalRequest));
2970
+ }).catch(async (err) => {
2971
+ if (err && (err?.error_code === "AUTHEN_FAIL" || err?.error_code === "TOKEN_EXPIRED" || err?.error_code === "TOKEN_INCORRECT" || err?.code === "ERR_BAD_REQUEST") || err?.error_code === "ERR_2FA_006") {
2972
+ await clearAuthToken();
2973
+ }
2974
+ if (err && err.response) {
2975
+ const { error_code } = err.response?.data || {};
2976
+ if (error_code === "AUTHEN_FAIL") {
2977
+ await clearAuthToken();
2978
+ }
2979
+ }
2980
+ processQueue(err, null);
2981
+ }).finally(() => {
2982
+ isRefreshing = false;
2983
+ });
2984
+ });
2985
+ }
2986
+ }
2987
+ return Promise.reject(await handleError3(error));
2988
+ }
2989
+ );
2990
+ const handleResponse = (res) => {
2991
+ if (res && res.data) {
2992
+ return res.data;
2993
+ }
2994
+ return res;
2995
+ };
2996
+ const handleError2 = (error) => {
2997
+ if (error.isAxiosError && error.code === "ECONNABORTED") {
2998
+ console.error("Request Timeout Error:", error);
2999
+ return "Request Timeout Error";
3000
+ } else if (error.isAxiosError && !error.response) {
3001
+ console.error("Network Error:", error);
3002
+ return "Network Error";
3003
+ } else {
3004
+ console.error("Other Error:", error?.response);
3005
+ const errorMessage = error?.response?.data?.message || "An error occurred";
3006
+ return { message: errorMessage, status: error?.response?.status };
3007
+ }
3008
+ };
3009
+ const clearAuthToken = async () => {
3010
+ await localStorage2.clearToken();
3011
+ if (typeof window !== "undefined") {
3012
+ window.location.href = `/login`;
3013
+ }
3014
+ };
3015
+ function formatUrl(url, db2) {
3016
+ return url + (db2 ? "?db=" + db2 : "");
3017
+ }
3018
+ const responseBody = (response) => response;
3019
+ const requests = {
3020
+ get: (url, headers) => instance.get(formatUrl(url, db), headers).then(responseBody),
3021
+ post: (url, body, headers) => instance.post(formatUrl(url, db), body, headers).then(responseBody),
3022
+ post_excel: (url, body, headers) => instance.post(formatUrl(url, db), body, {
3023
+ responseType: "arraybuffer",
3024
+ headers: {
3025
+ "Content-Type": typeof window !== "undefined" ? "application/json" : "application/javascript",
3026
+ Accept: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
3027
+ }
3028
+ }).then(responseBody),
3029
+ put: (url, body, headers) => instance.put(formatUrl(url, db), body, headers).then(responseBody),
3030
+ patch: (url, body) => instance.patch(formatUrl(url, db), body).then(responseBody),
3031
+ delete: (url, body) => instance.delete(formatUrl(url, db), body).then(responseBody)
3032
+ };
3033
+ return requests;
3034
+ }
3035
+ };
3036
+
3037
+ // src/environment/EnvStore.ts
3038
+ function createEnvStore(store, localUtils, sessionUtils) {
3039
+ let envData = store.getState().env;
3040
+ const getEnvData = () => {
3041
+ envData = store.getState().env;
3042
+ return envData;
3043
+ };
3044
+ const setupEnv = (envConfig) => {
3045
+ const requests = axiosClient.init({
3046
+ ...envConfig,
3047
+ localStorageUtils: localUtils,
3048
+ sessionStorageUtils: sessionUtils
3049
+ });
3050
+ store.dispatch(
3051
+ setEnv({
3052
+ ...envConfig,
3053
+ requests,
3054
+ localStorageUtils: localUtils,
3055
+ sessionStorageUtils: sessionUtils
3056
+ })
3057
+ );
3058
+ getEnvData();
3059
+ };
3060
+ const setLangValue = (lang) => {
3061
+ store.dispatch(setLang(lang));
3062
+ getEnvData();
3063
+ };
3064
+ const setUidValue = (uid) => {
3065
+ store.dispatch(setUid(uid));
3066
+ getEnvData();
3067
+ };
3068
+ const setAllowCompaniesValue = (allowCompanies) => {
3069
+ store.dispatch(setAllowCompanies(allowCompanies));
3070
+ getEnvData();
3071
+ };
3072
+ const setCompaniesValue = (companies) => {
3073
+ store.dispatch(setCompanies(companies));
3074
+ getEnvData();
3075
+ };
3076
+ const setDefaultCompanyValue = (company) => {
3077
+ store.dispatch(setDefaultCompany(company));
3078
+ getEnvData();
3079
+ };
3080
+ const setUserInfoValue = (user) => {
3081
+ store.dispatch(setUser(user));
3082
+ getEnvData();
3083
+ };
3084
+ return {
3085
+ getEnvData,
3086
+ setupEnv,
3087
+ setLangValue,
3088
+ setUidValue,
3089
+ setAllowCompaniesValue,
3090
+ setCompaniesValue,
3091
+ setDefaultCompanyValue,
3092
+ setUserInfoValue
3093
+ };
3094
+ }
3095
+ var env = null;
2820
3096
  function getEnv() {
2821
- const env = envStore.getState().env;
2822
- return { ...env, requests };
3097
+ if (!env) {
3098
+ env = createEnvStore(envStore, localStorageUtils(), sessionStorageUtils());
3099
+ }
3100
+ return env?.getEnvData();
2823
3101
  }
2824
3102
 
2825
3103
  // src/services/view-service/index.ts
@@ -2831,7 +3109,7 @@ var ViewService = {
2831
3109
  options = {},
2832
3110
  aid
2833
3111
  }) {
2834
- const env = getEnv();
3112
+ const env2 = getEnv();
2835
3113
  const defaultOptions = {
2836
3114
  load_filters: true,
2837
3115
  toolbar: true,
@@ -2846,14 +3124,14 @@ var ViewService = {
2846
3124
  },
2847
3125
  with_context: context
2848
3126
  };
2849
- return env?.requests?.post("/call" /* CALL_PATH */, jsonDataView, {
3127
+ return env2?.requests?.post("/call" /* CALL_PATH */, jsonDataView, {
2850
3128
  headers: {
2851
3129
  "Content-Type": "application/json"
2852
3130
  }
2853
3131
  });
2854
3132
  },
2855
3133
  async getMenu(context) {
2856
- const env = getEnv();
3134
+ const env2 = getEnv();
2857
3135
  const jsonData = {
2858
3136
  model: "ir.ui.menu" /* MENU */,
2859
3137
  method: "web_search_read" /* WEB_SEARCH_READ */,
@@ -2990,14 +3268,14 @@ var ViewService = {
2990
3268
  ]
2991
3269
  }
2992
3270
  };
2993
- return env?.requests?.post("/call" /* CALL_PATH */, jsonData, {
3271
+ return env2?.requests?.post("/call" /* CALL_PATH */, jsonData, {
2994
3272
  headers: {
2995
3273
  "Content-Type": "application/json"
2996
3274
  }
2997
3275
  });
2998
3276
  },
2999
3277
  async getActionDetail(aid, context) {
3000
- const env = getEnv();
3278
+ const env2 = getEnv();
3001
3279
  const jsonData = {
3002
3280
  model: "ir.actions.act_window" /* WINDOW_ACTION */,
3003
3281
  method: "web_read" /* WEB_READ */,
@@ -3018,7 +3296,7 @@ var ViewService = {
3018
3296
  }
3019
3297
  }
3020
3298
  };
3021
- return env?.requests?.post("/call" /* CALL_PATH */, jsonData, {
3299
+ return env2?.requests?.post("/call" /* CALL_PATH */, jsonData, {
3022
3300
  headers: {
3023
3301
  "Content-Type": "application/json"
3024
3302
  }
@@ -3030,7 +3308,7 @@ var ViewService = {
3030
3308
  context,
3031
3309
  offset
3032
3310
  }) {
3033
- const env = getEnv();
3311
+ const env2 = getEnv();
3034
3312
  const jsonData = {
3035
3313
  model,
3036
3314
  with_context: context,
@@ -3038,14 +3316,14 @@ var ViewService = {
3038
3316
  field: "sequence",
3039
3317
  ...offset > 0 ? { offset } : {}
3040
3318
  };
3041
- return env?.requests.post("/web/dataset/resequence", jsonData, {
3319
+ return env2?.requests.post("/web/dataset/resequence", jsonData, {
3042
3320
  headers: {
3043
3321
  "Content-Type": "application/json"
3044
3322
  }
3045
3323
  });
3046
3324
  },
3047
3325
  async getSelectionItem({ data }) {
3048
- const env = getEnv();
3326
+ const env2 = getEnv();
3049
3327
  const jsonData = {
3050
3328
  model: data.model,
3051
3329
  ids: [],
@@ -3063,15 +3341,15 @@ var ViewService = {
3063
3341
  }
3064
3342
  }
3065
3343
  };
3066
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3344
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3067
3345
  headers: {
3068
3346
  "Content-Type": "application/json"
3069
3347
  }
3070
3348
  });
3071
3349
  },
3072
3350
  async loadMessages() {
3073
- const env = getEnv();
3074
- return env.requests.post(
3351
+ const env2 = getEnv();
3352
+ return env2.requests.post(
3075
3353
  "/load_message_failures" /* LOAD_MESSAGE */,
3076
3354
  {},
3077
3355
  {
@@ -3082,8 +3360,9 @@ var ViewService = {
3082
3360
  );
3083
3361
  },
3084
3362
  async getVersion() {
3085
- const env = getEnv();
3086
- return env?.requests.get("", {
3363
+ const env2 = getEnv();
3364
+ console.log("env?.requests", env2, env2?.requests);
3365
+ return env2?.requests?.get("", {
3087
3366
  headers: {
3088
3367
  "Content-Type": "application/json"
3089
3368
  }
@@ -3093,12 +3372,12 @@ var ViewService = {
3093
3372
  method,
3094
3373
  with_context
3095
3374
  }) {
3096
- const env = getEnv();
3375
+ const env2 = getEnv();
3097
3376
  const jsonData = {
3098
3377
  method,
3099
3378
  with_context
3100
3379
  };
3101
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3380
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3102
3381
  headers: {
3103
3382
  "Content-Type": "application/json"
3104
3383
  }
@@ -3111,7 +3390,7 @@ var ViewService = {
3111
3390
  device,
3112
3391
  location
3113
3392
  }) {
3114
- const env = getEnv();
3393
+ const env2 = getEnv();
3115
3394
  const jsonData = {
3116
3395
  method,
3117
3396
  kwargs: {
@@ -3123,7 +3402,7 @@ var ViewService = {
3123
3402
  },
3124
3403
  with_context
3125
3404
  };
3126
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3405
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3127
3406
  headers: {
3128
3407
  "Content-Type": "application/json"
3129
3408
  },
@@ -3137,7 +3416,7 @@ var ViewService = {
3137
3416
  response_type,
3138
3417
  path
3139
3418
  }) {
3140
- const env = getEnv();
3419
+ const env2 = getEnv();
3141
3420
  const params = new URLSearchParams({
3142
3421
  response_type,
3143
3422
  client_id,
@@ -3145,7 +3424,7 @@ var ViewService = {
3145
3424
  state
3146
3425
  });
3147
3426
  const url = `${path}?${params.toString()}`;
3148
- return env?.requests.get(url, {
3427
+ return env2?.requests.get(url, {
3149
3428
  headers: {
3150
3429
  "Content-Type": "application/json"
3151
3430
  },
@@ -3158,14 +3437,14 @@ var ViewService = {
3158
3437
  client_id,
3159
3438
  scopes
3160
3439
  }) {
3161
- const env = getEnv();
3440
+ const env2 = getEnv();
3162
3441
  const jsonData = {
3163
3442
  redirect_uri,
3164
3443
  state,
3165
3444
  client_id,
3166
3445
  scopes
3167
3446
  };
3168
- return env?.requests.post("/grant-access" /* GRANT_ACCESS */, jsonData, {
3447
+ return env2?.requests.post("/grant-access" /* GRANT_ACCESS */, jsonData, {
3169
3448
  headers: {
3170
3449
  "Content-Type": "application/json"
3171
3450
  },
@@ -3177,7 +3456,7 @@ var ViewService = {
3177
3456
  token,
3178
3457
  views
3179
3458
  }) {
3180
- const env = getEnv();
3459
+ const env2 = getEnv();
3181
3460
  const jsonData = {
3182
3461
  method,
3183
3462
  kwargs: {
@@ -3187,7 +3466,7 @@ var ViewService = {
3187
3466
  token
3188
3467
  }
3189
3468
  };
3190
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3469
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3191
3470
  headers: {
3192
3471
  "Content-Type": "application/json"
3193
3472
  }
@@ -3199,7 +3478,7 @@ var ViewService = {
3199
3478
  kwargs,
3200
3479
  token
3201
3480
  }) {
3202
- const env = getEnv();
3481
+ const env2 = getEnv();
3203
3482
  const jsonData = {
3204
3483
  method,
3205
3484
  model,
@@ -3208,21 +3487,21 @@ var ViewService = {
3208
3487
  token
3209
3488
  }
3210
3489
  };
3211
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3490
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3212
3491
  headers: {
3213
3492
  "Content-Type": "application/json"
3214
3493
  }
3215
3494
  });
3216
3495
  },
3217
3496
  async requestSetupTotp({ method, token }) {
3218
- const env = getEnv();
3497
+ const env2 = getEnv();
3219
3498
  const jsonData = {
3220
3499
  method,
3221
3500
  with_context: {
3222
3501
  token
3223
3502
  }
3224
3503
  };
3225
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3504
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3226
3505
  headers: {
3227
3506
  "Content-Type": "application/json"
3228
3507
  }
@@ -3233,7 +3512,7 @@ var ViewService = {
3233
3512
  action_token,
3234
3513
  code
3235
3514
  }) {
3236
- const env = getEnv();
3515
+ const env2 = getEnv();
3237
3516
  const jsonData = {
3238
3517
  method,
3239
3518
  kwargs: {
@@ -3245,21 +3524,21 @@ var ViewService = {
3245
3524
  action_token
3246
3525
  }
3247
3526
  };
3248
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3527
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3249
3528
  headers: {
3250
3529
  "Content-Type": "application/json"
3251
3530
  }
3252
3531
  });
3253
3532
  },
3254
3533
  async removeTotpSetUp({ method, token }) {
3255
- const env = getEnv();
3534
+ const env2 = getEnv();
3256
3535
  const jsonData = {
3257
3536
  method,
3258
3537
  with_context: {
3259
3538
  token
3260
3539
  }
3261
3540
  };
3262
- return env?.requests.post("/call" /* CALL_PATH */, jsonData, {
3541
+ return env2?.requests.post("/call" /* CALL_PATH */, jsonData, {
3263
3542
  headers: {
3264
3543
  "Content-Type": "application/json"
3265
3544
  }
@@ -3280,6 +3559,7 @@ var VersionGate = ({ children }) => {
3280
3559
  };
3281
3560
  const validateVersion = async () => {
3282
3561
  const serverVersion = await view_service_default.getVersion();
3562
+ console.log("serverVersion", serverVersion);
3283
3563
  const cached = localStorage.getItem("__api_version__");
3284
3564
  if (cached !== serverVersion?.api_version) {
3285
3565
  clearVersion();