@tap-payments/auth-jsconnect 2.3.47-test → 2.3.51-test

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.
@@ -51,7 +51,7 @@ import API from '../../../api';
51
51
  import { DocumentPurpose, FlowsTypes } from '../../../@types';
52
52
  import { handleCurrentActiveScreen, handleNextScreenStep, handleSetCountryByIso2 } from '../../../app/settings';
53
53
  import { BANK_STEP_NAMES } from '../../../constants';
54
- import { hasNoneEditableValue, sleep } from '../../../utils';
54
+ import { hasNoneEditableValue, isStringMasked, sleep } from '../../../utils';
55
55
  export var verifyLeadToken = createAsyncThunk('bankVerifyLeadToken', function (_a, thunkApi) {
56
56
  var token = _a.token, isInternally = _a.isInternally;
57
57
  return __awaiter(void 0, void 0, void 0, function () {
@@ -165,7 +165,7 @@ export var retrieveBoardDetails = createAsyncThunk('retrieveBankEntityInfo', fun
165
165
  });
166
166
  }); });
167
167
  export var createBankAccount = createAsyncThunk('createBankAccount', function (params, thunkApi) { return __awaiter(void 0, void 0, void 0, function () {
168
- var _a, settings, bank, iban, beneficiaryName, bankName, bankStatementId, confirmPolicy, _b, data_status, document, isIBANNonEditable, isBeneficiaryNameNonEditable, isBankNameNonEditable, isBankStatementIdNonEditable, isConfirmPolicyNonEditable, documentResponse, hasDocument, documentId, requestBody, data, documentBody;
168
+ var _a, settings, bank, iban, beneficiaryName, bankName, bankStatementId, confirmPolicy, _b, data_status, document, isIBANMasked, isIBANNonEditable, isBeneficiaryNameNonEditable, isBankNameNonEditable, isBankStatementIdNonEditable, isConfirmPolicyNonEditable, documentResponse, hasDocument, documentId, requestBody, data, documentBody;
169
169
  var _c, _d, _e, _f, _g, _h, _j, _k, _l;
170
170
  return __generator(this, function (_m) {
171
171
  switch (_m.label) {
@@ -173,6 +173,7 @@ export var createBankAccount = createAsyncThunk('createBankAccount', function (p
173
173
  _a = thunkApi.getState(), settings = _a.settings, bank = _a.bank;
174
174
  iban = params.iban, beneficiaryName = params.beneficiaryName, bankName = params.bankName, bankStatementId = params.bankStatementId, confirmPolicy = params.confirmPolicy;
175
175
  _b = ((_c = bank.data.verify.responseBody) === null || _c === void 0 ? void 0 : _c.bank_account) || {}, data_status = _b.data_status, document = _b.document;
176
+ isIBANMasked = isStringMasked(iban);
176
177
  isIBANNonEditable = hasNoneEditableValue(data_status, 'iban');
177
178
  isBeneficiaryNameNonEditable = hasNoneEditableValue(data_status, 'beneficiary_name');
178
179
  isBankNameNonEditable = hasNoneEditableValue(data_status, 'bank_name');
@@ -183,7 +184,7 @@ export var createBankAccount = createAsyncThunk('createBankAccount', function (p
183
184
  documentId = document === null || document === void 0 ? void 0 : document.id;
184
185
  requestBody = {
185
186
  wallet_id: (_h = (_g = (_f = (_e = (_d = bank.data.verify.responseBody) === null || _d === void 0 ? void 0 : _d.business) === null || _e === void 0 ? void 0 : _e.entity) === null || _f === void 0 ? void 0 : _f.merchant) === null || _g === void 0 ? void 0 : _g.wallet) === null || _h === void 0 ? void 0 : _h.id,
186
- bank_account: __assign({ iban: isIBANNonEditable ? undefined : iban, beneficiary_name: isBeneficiaryNameNonEditable ? undefined : beneficiaryName, bank_name: isBankNameNonEditable ? undefined : bankName, is_acknowledged: isConfirmPolicyNonEditable ? undefined : confirmPolicy }, (!(isBankStatementIdNonEditable || documentId || !hasDocument) && {
187
+ bank_account: __assign({ iban: isIBANNonEditable || isIBANMasked ? undefined : iban, beneficiary_name: isBeneficiaryNameNonEditable ? undefined : beneficiaryName, bank_name: isBankNameNonEditable ? undefined : bankName, is_acknowledged: isConfirmPolicyNonEditable ? undefined : confirmPolicy }, (!(isBankStatementIdNonEditable || documentId || !hasDocument) && {
187
188
  document: {
188
189
  type: DocumentPurpose.BANK_STATEMENT,
189
190
  files: bankStatementId || []
@@ -744,10 +744,10 @@ export var businessSlice = createSlice({
744
744
  state.customLoading = true;
745
745
  })
746
746
  .addCase(verifyLeadToken.fulfilled, function (state, action) {
747
- var _a, _b, _c;
747
+ var _a, _b;
748
748
  state.error = null;
749
749
  state.customLoading = false;
750
- var _d = action.payload, data = _d.data, boardId = _d.boardId, brandData = _d.brandData, leadData = _d.leadData, token = _d.token, isRequireOTP = _d.isRequireOTP;
750
+ var _c = action.payload, data = _c.data, boardId = _c.boardId, brandData = _c.brandData, leadData = _c.leadData, token = _c.token, isRequireOTP = _c.isRequireOTP;
751
751
  var responseBody = state.data.verify.responseBody;
752
752
  state.data.verify.responseBody = __assign(__assign(__assign(__assign({}, data), leadData), responseBody), { board_id: boardId, lead_id: data === null || data === void 0 ? void 0 : data.id });
753
753
  if (token)
@@ -755,7 +755,7 @@ export var businessSlice = createSlice({
755
755
  state.data.otpData.isNID = false;
756
756
  if (isRequireOTP)
757
757
  return;
758
- var _e = state.data.verify.responseBody || {}, entity = _e.entity, entity_activities = _e.entity_activities, business_type = _e.business_type;
758
+ var _d = state.data.verify.responseBody || {}, entity = _d.entity, entity_activities = _d.entity_activities, business_type = _d.business_type;
759
759
  var issuingDate = ((_a = brandData === null || brandData === void 0 ? void 0 : brandData.operations) === null || _a === void 0 ? void 0 : _a.start_date) || ((_b = entity === null || entity === void 0 ? void 0 : entity.license) === null || _b === void 0 ? void 0 : _b.issuing_date);
760
760
  if (issuingDate)
761
761
  state.data.activitiesData.operationStartDate = convertNumbers2English(dateFormat(issuingDate));
@@ -769,21 +769,30 @@ export var businessSlice = createSlice({
769
769
  var license = (entity || {}).license;
770
770
  if (license) {
771
771
  var number_1 = license.number;
772
- var _f = state.data.businessTypeData || {}, busResData = _f.responseBody, selectedLicense = _f.selectedLicense;
773
- var licenseList = (busResData || {}).licenseList;
772
+ var _e = state.data.businessTypeData || {}, busResData = _e.responseBody, selectedLicense = _e.selectedLicense;
773
+ var licenseList = (busResData === null || busResData === void 0 ? void 0 : busResData.licenseList) || [];
774
774
  var selectedLicenseData = selectedLicense;
775
775
  if (number_1) {
776
- var findLicense = (_c = licenseList === null || licenseList === void 0 ? void 0 : licenseList.find) === null || _c === void 0 ? void 0 : _c.call(licenseList, function (_a) {
776
+ var findLicense = licenseList.find(function (_a) {
777
777
  var license = _a.license;
778
- return (license === null || license === void 0 ? void 0 : license.number) === number_1;
778
+ return license.number === number_1;
779
779
  });
780
780
  if (findLicense)
781
781
  selectedLicenseData = findLicense;
782
782
  else if (!findLicense && business_type === BusinessType.FL) {
783
- selectedLicenseData = __assign(__assign({}, OTHER_FL_LICENSE), { license: { number: number_1 } });
783
+ selectedLicenseData = __assign(__assign({}, OTHER_FL_LICENSE), { legal_name: {
784
+ ar: number_1,
785
+ en: number_1
786
+ }, license: { number: number_1 } });
787
+ state.data.businessTypeData.responseBody = __assign(__assign({}, state.data.businessTypeData.responseBody), { licenseList: __spreadArray([selectedLicenseData], licenseList, true) });
788
+ }
789
+ else {
790
+ selectedLicenseData = __assign(__assign({}, OTHER_CR_LICENSE), { legal_name: {
791
+ ar: number_1,
792
+ en: number_1
793
+ }, license: { number: number_1 } });
794
+ state.data.businessTypeData.responseBody = __assign(__assign({}, state.data.businessTypeData.responseBody), { licenseList: __spreadArray([selectedLicenseData], licenseList, true) });
784
795
  }
785
- else
786
- selectedLicenseData = __assign(__assign({}, OTHER_CR_LICENSE), { license: { number: number_1 } });
787
796
  state.data.businessTypeData.licenseNumber = number_1;
788
797
  }
789
798
  state.data.businessTypeData.selectedLicense = __assign(__assign({}, selectedLicenseData), { type: business_type });
@@ -815,14 +824,14 @@ export var businessSlice = createSlice({
815
824
  state.error = null;
816
825
  })
817
826
  .addCase(verifyLeadOTP.fulfilled, function (state, action) {
818
- var _a, _b, _c;
827
+ var _a, _b;
819
828
  state.loading = false;
820
829
  state.error = null;
821
- var _d = action.payload, data = _d.data, formData = _d.formData, brand = _d.brand, leadData = _d.leadData;
830
+ var _c = action.payload, data = _c.data, formData = _c.formData, brand = _c.brand, leadData = _c.leadData;
822
831
  state.data.otpData = formData;
823
832
  state.data.otpData.responseBody = data;
824
833
  state.data.verify.responseBody = __assign(__assign(__assign({}, leadData), state.data.verify.responseBody), { flows: (leadData === null || leadData === void 0 ? void 0 : leadData.flows) || [] });
825
- var _e = leadData || {}, entity = _e.entity, entity_activities = _e.entity_activities, business_type = _e.business_type;
834
+ var _d = leadData || {}, entity = _d.entity, entity_activities = _d.entity_activities, business_type = _d.business_type;
826
835
  var issuingDate = ((_a = brand === null || brand === void 0 ? void 0 : brand.operations) === null || _a === void 0 ? void 0 : _a.start_date) || ((_b = entity === null || entity === void 0 ? void 0 : entity.license) === null || _b === void 0 ? void 0 : _b.issuing_date);
827
836
  if (issuingDate)
828
837
  state.data.activitiesData.operationStartDate = convertNumbers2English(dateFormat(issuingDate));
@@ -836,21 +845,30 @@ export var businessSlice = createSlice({
836
845
  var license = (entity || {}).license;
837
846
  if (license) {
838
847
  var number_2 = license.number;
839
- var _f = state.data.businessTypeData || {}, busResData = _f.responseBody, selectedLicense = _f.selectedLicense;
840
- var licenseList = (busResData || {}).licenseList;
848
+ var _e = state.data.businessTypeData || {}, busResData = _e.responseBody, selectedLicense = _e.selectedLicense;
849
+ var licenseList = (busResData === null || busResData === void 0 ? void 0 : busResData.licenseList) || [];
841
850
  var selectedLicenseData = selectedLicense;
842
851
  if (number_2) {
843
- var findLicense = (_c = licenseList === null || licenseList === void 0 ? void 0 : licenseList.find) === null || _c === void 0 ? void 0 : _c.call(licenseList, function (_a) {
852
+ var findLicense = licenseList.find(function (_a) {
844
853
  var license = _a.license;
845
- return (license === null || license === void 0 ? void 0 : license.number) === number_2;
854
+ return license.number === number_2;
846
855
  });
847
856
  if (findLicense)
848
857
  selectedLicenseData = findLicense;
849
858
  else if (!findLicense && business_type === BusinessType.FL) {
850
- selectedLicenseData = __assign(__assign({}, OTHER_FL_LICENSE), { license: { number: number_2 } });
859
+ selectedLicenseData = __assign(__assign({}, OTHER_FL_LICENSE), { legal_name: {
860
+ ar: number_2,
861
+ en: number_2
862
+ }, license: { number: number_2 } });
863
+ state.data.businessTypeData.responseBody = __assign(__assign({}, state.data.businessTypeData.responseBody), { licenseList: __spreadArray([selectedLicenseData], licenseList, true) });
864
+ }
865
+ else {
866
+ selectedLicenseData = __assign(__assign({}, OTHER_CR_LICENSE), { legal_name: {
867
+ ar: number_2,
868
+ en: number_2
869
+ }, license: { number: number_2 } });
870
+ state.data.businessTypeData.responseBody = __assign(__assign({}, state.data.businessTypeData.responseBody), { licenseList: __spreadArray([selectedLicenseData], licenseList, true) });
851
871
  }
852
- else
853
- selectedLicenseData = __assign(__assign({}, OTHER_CR_LICENSE), { license: { number: number_2 } });
854
872
  state.data.businessTypeData.licenseNumber = number_2;
855
873
  }
856
874
  state.data.businessTypeData.selectedLicense = __assign(__assign({}, selectedLicenseData), { type: business_type });
@@ -29,7 +29,7 @@ import Form from '../../../../components/Form';
29
29
  import { handleCurrentActiveScreen, settingsSelector } from '../../../../app/settings';
30
30
  import ConfirmPolicy from './ConfirmPolicy';
31
31
  import BankStatement from './BankStatement';
32
- import { getFileDetailsFromDocument, isKW } from '../../../../utils';
32
+ import { getFileDetailsFromDocument, isKW, isStringMasked } from '../../../../utils';
33
33
  var FormStyled = styled(Form)(function () { return ({
34
34
  display: 'flex',
35
35
  flexDirection: 'column',
@@ -53,8 +53,9 @@ var BankDetails = function () {
53
53
  var isKWCountry = React.useMemo(function () { return isKW(settingsData.businessCountry.iso2); }, [settingsData.businessCountry.iso2]);
54
54
  var isFL = ((_a = entity === null || entity === void 0 ? void 0 : entity.type) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === BusinessType.FL ? true : false;
55
55
  var bankHasDocument = ((_c = (_b = bank_account === null || bank_account === void 0 ? void 0 : bank_account.document) === null || _b === void 0 ? void 0 : _b.file_details) === null || _c === void 0 ? void 0 : _c.length) > 0;
56
+ var isIBANMasked = isStringMasked(iban);
56
57
  var methods = useForm({
57
- resolver: yupResolver(isKWCountry && isFL && !bankHasDocument ? BankKWFLValidation() : BankValidation()),
58
+ resolver: yupResolver(isKWCountry && isFL && !bankHasDocument ? BankKWFLValidation(isIBANMasked) : BankValidation(isIBANMasked)),
58
59
  defaultValues: {
59
60
  iban: iban,
60
61
  beneficiaryName: beneficiaryName,
@@ -1,37 +1,37 @@
1
1
  import * as yup from 'yup';
2
- export declare const BankValidation: () => yup.ObjectSchema<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
3
- iban: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
2
+ export declare const BankValidation: (isIBANMasked: boolean) => yup.ObjectSchema<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
3
+ iban: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
4
4
  bankName: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
5
5
  beneficiaryName: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
6
6
  bankStatementId: yup.ArraySchema<yup.AnySchema<any, any, any>, import("yup/lib/types").AnyObject, any[] | undefined, any[] | undefined>;
7
7
  confirmPolicy: yup.BooleanSchema<boolean | undefined, import("yup/lib/types").AnyObject, true>;
8
8
  }>, import("yup/lib/object").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
9
- iban: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
9
+ iban: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
10
10
  bankName: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
11
11
  beneficiaryName: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
12
12
  bankStatementId: yup.ArraySchema<yup.AnySchema<any, any, any>, import("yup/lib/types").AnyObject, any[] | undefined, any[] | undefined>;
13
13
  confirmPolicy: yup.BooleanSchema<boolean | undefined, import("yup/lib/types").AnyObject, true>;
14
14
  }>>, import("yup/lib/object").AssertsShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
15
- iban: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
15
+ iban: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
16
16
  bankName: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
17
17
  beneficiaryName: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
18
18
  bankStatementId: yup.ArraySchema<yup.AnySchema<any, any, any>, import("yup/lib/types").AnyObject, any[] | undefined, any[] | undefined>;
19
19
  confirmPolicy: yup.BooleanSchema<boolean | undefined, import("yup/lib/types").AnyObject, true>;
20
20
  }>>>;
21
- export declare const BankKWFLValidation: () => yup.ObjectSchema<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
22
- iban: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
21
+ export declare const BankKWFLValidation: (isIBANMasked: boolean) => yup.ObjectSchema<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
22
+ iban: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
23
23
  bankName: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
24
24
  beneficiaryName: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
25
25
  bankStatementId: import("yup/lib/array").RequiredArraySchema<yup.AnySchema<any, any, any>, import("yup/lib/types").AnyObject, any[] | undefined>;
26
26
  confirmPolicy: yup.BooleanSchema<boolean | undefined, import("yup/lib/types").AnyObject, true>;
27
27
  }>, import("yup/lib/object").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
28
- iban: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
28
+ iban: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
29
29
  bankName: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
30
30
  beneficiaryName: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
31
31
  bankStatementId: import("yup/lib/array").RequiredArraySchema<yup.AnySchema<any, any, any>, import("yup/lib/types").AnyObject, any[] | undefined>;
32
32
  confirmPolicy: yup.BooleanSchema<boolean | undefined, import("yup/lib/types").AnyObject, true>;
33
33
  }>>, import("yup/lib/object").AssertsShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
34
- iban: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
34
+ iban: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
35
35
  bankName: yup.StringSchema<string | undefined, import("yup/lib/types").AnyObject, string | undefined>;
36
36
  beneficiaryName: import("yup/lib/string").RequiredStringSchema<string | undefined, import("yup/lib/types").AnyObject>;
37
37
  bankStatementId: import("yup/lib/array").RequiredArraySchema<yup.AnySchema<any, any, any>, import("yup/lib/types").AnyObject, any[] | undefined>;
@@ -1,27 +1,21 @@
1
1
  import * as yup from 'yup';
2
2
  import { REGEX_ALPHANUMERIC, REGEX_IBAN } from '../../../../constants';
3
- export var BankValidation = function () {
3
+ export var BankValidation = function (isIBANMasked) {
4
4
  return yup.object().shape({
5
- iban: yup
6
- .string()
7
- .matches(REGEX_IBAN, 'invalid_iban_format')
8
- .min(15, 'invalid_iban_format')
9
- .max(34, 'invalid_iban_format')
10
- .required('alert_iban'),
5
+ iban: isIBANMasked
6
+ ? yup.string().optional()
7
+ : yup.string().matches(REGEX_IBAN, 'invalid_iban_format').min(15, 'invalid_iban_format').max(34, 'invalid_iban_format').required('alert_iban'),
11
8
  bankName: yup.string().optional(),
12
9
  beneficiaryName: yup.string().matches(REGEX_ALPHANUMERIC, 'beneficiary_name_invalid').required('beneficiary_name_required'),
13
10
  bankStatementId: yup.array().optional(),
14
11
  confirmPolicy: yup.boolean().required('alert_bank_confirm').isTrue('alert_bank_confirm')
15
12
  });
16
13
  };
17
- export var BankKWFLValidation = function () {
14
+ export var BankKWFLValidation = function (isIBANMasked) {
18
15
  return yup.object().shape({
19
- iban: yup
20
- .string()
21
- .matches(REGEX_IBAN, 'invalid_iban_format')
22
- .min(15, 'invalid_iban_format')
23
- .max(34, 'invalid_iban_format')
24
- .required('alert_iban'),
16
+ iban: isIBANMasked
17
+ ? yup.string().optional()
18
+ : yup.string().matches(REGEX_IBAN, 'invalid_iban_format').min(15, 'invalid_iban_format').max(34, 'invalid_iban_format').required('alert_iban'),
25
19
  bankName: yup.string().optional(),
26
20
  beneficiaryName: yup.string().matches(REGEX_ALPHANUMERIC, 'beneficiary_name_invalid').required('beneficiary_name_required'),
27
21
  bankStatementId: yup.array().min(1, 'bank_statement_required').required('bank_statement_required'),
@@ -74,7 +74,7 @@ var SuccessFlowButtons = function (_a) {
74
74
  var name = _a.name, url = _a.url, status = _a.status, token = _a.token;
75
75
  var type = status === 'completed' ? 'completed' : 'pending';
76
76
  var title = t("".concat(name, "_flow_").concat(type), {
77
- individual_name: is_authorized && count > 1 ? t('company_individuals_details', { count: count }) : username.toLowerCase() + maskedId,
77
+ individual_name: (is_authorized && count > 1 ? t('company_individuals_details', { count: count }) : username.toLowerCase() + maskedId) || t('individual'),
78
78
  brand: brandName,
79
79
  business_type: entityLegalName,
80
80
  license_number: licenseNumber ? t('masking_symbols') + showLastFour(licenseNumber) : '',
@@ -69,3 +69,4 @@ export declare const getUserNameObject: (name: string) => {
69
69
  last: string;
70
70
  };
71
71
  export declare const getFileType: (type: string) => "" | "image/jpeg" | "image/png" | "image/jpg" | "application/pdf";
72
+ export declare const isStringMasked: (value: string) => boolean;
@@ -298,3 +298,6 @@ export var getFileType = function (type) {
298
298
  return '';
299
299
  }
300
300
  };
301
+ export var isStringMasked = function (value) {
302
+ return value.includes('*') ? true : false;
303
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tap-payments/auth-jsconnect",
3
- "version": "2.3.47-test",
3
+ "version": "2.3.51-test",
4
4
  "description": "connect library, auth",
5
5
  "private": false,
6
6
  "main": "build/index.js",