intelicoreact 2.0.8 → 2.0.10
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/Atomic/FormElements/Dropdown/components/DropdownLoader.d.ts +4 -0
- package/dist/Atomic/FormElements/FormattedRawSSN/FormattedRawSSN_old.d.ts +11 -0
- package/dist/Atomic/FormElements/NumericInput/NumericInput.d.ts +36 -0
- package/dist/Atomic/FormElements/RangeCalendar/RangeCalendar.d.ts +2 -0
- package/dist/Atomic/FormElements/RangeSlider/RangeSlider.d.ts +19 -0
- package/dist/Atomic/FormElements/SwitcherTagsDropdown/partial/States.d.ts +5 -0
- package/dist/Atomic/FormElements/VariantsListRadio/VariantsListRadio.d.ts +17 -0
- package/dist/Atomic/FormElements/VariantsListRadio/partials/VariantsListRadioItem.d.ts +12 -0
- package/dist/Atomic/FormElements/WidgetPseudoTable/WidgetPseudoTable.d.ts +2 -0
- package/dist/Atomic/FormElements/WidgetPseudoTable/partial/constructor.d.ts +23 -0
- package/dist/Atomic/FormElements/WidgetWithSwitchableRows/WidgetWithSwitchableRows.d.ts +2 -0
- package/dist/Atomic/FormElements/WidgetWithSwitchableRows/partial/constructor.d.ts +38 -0
- package/dist/Atomic/UI/AccordionTable/AccordionTable.d.ts +10 -0
- package/dist/Atomic/UI/AccordionText/AccordionText.d.ts +8 -0
- package/dist/Atomic/UI/Chart/partial/Chart.constants.d.ts +78 -0
- package/dist/Atomic/UI/Chart/partial/datasetSetters.d.ts +13 -0
- package/dist/Atomic/UI/Chart/partial/optionsConstructor.d.ts +145 -0
- package/dist/Atomic/UI/Chart/partial/optionsSetters.d.ts +4 -0
- package/dist/Atomic/UI/CircleProgressBar/CircleProgressBar.d.ts +14 -0
- package/dist/Atomic/UI/ExampleChartIntegration/ExampleChartIntegration.d.ts +2 -0
- package/dist/Atomic/UI/ExampleChartIntegration/partial/utils.d.ts +4 -0
- package/dist/Atomic/UI/MonoAccordion/MonoAccordion._test.d.ts +1 -0
- package/dist/Atomic/UI/MonoAccordion/MonoAccordion.d.ts +12 -0
- package/dist/Atomic/UI/PieChart/PieChart.d.ts +8 -0
- package/dist/Atomic/UI/Table/Partials/TdCell.d.ts +13 -0
- package/dist/Atomic/UI/Table/Partials/TdHeader.d.ts +5 -0
- package/dist/Atomic/UI/Table/Partials/TdRow.d.ts +17 -0
- package/dist/Atomic/UI/Table/Partials/TdTitle.d.ts +5 -0
- package/dist/Atomic/UI/Table/Table.d.ts +9 -0
- package/dist/Atomic/UI/Table/TdTypes/TdActions.d.ts +6 -0
- package/dist/Atomic/UI/Table/TdTypes/TdPriority.d.ts +6 -0
- package/dist/Atomic/UI/Table/TdTypes/TdRange.d.ts +4 -0
- package/dist/Atomic/UI/Table/TdTypes/TdWeight.d.ts +7 -0
- package/dist/Atomic/UI/WizardStepper/constructor.d.ts +51 -0
- package/dist/Classes/AbortableFetch.d.ts +43 -0
- package/dist/Classes/AnimatedHandler.d.ts +4 -0
- package/dist/Classes/RESTAPI/index.d.ts +31 -0
- package/dist/Classes/RESTAPI/partials/AbortableFetch.d.ts +48 -0
- package/dist/Classes/RESTAPI/partials/ApiBase.d.ts +21 -0
- package/dist/Classes/RESTAPI/partials/ApiRequestCreators.d.ts +58 -0
- package/dist/Classes/RESTAPI/partials/ApiUtils.d.ts +63 -0
- package/dist/Classes/RESTAPI/partials/CredentialsProcessing.d.ts +79 -0
- package/dist/Classes/RESTAPI/partials/Utils.d.ts +20 -0
- package/dist/Classes/RESTAPI/partials/_utils.d.ts +54 -0
- package/dist/Classes/RESTAPI/partials/sse.d.ts +27 -0
- package/dist/Classes/RESTAPI/partials/types.d.ts +159 -0
- package/dist/Constants/index.constants.d.ts +20 -0
- package/dist/Functions/Portal.d.ts +6 -0
- package/dist/Functions/customEventListener.d.ts +27 -0
- package/dist/Functions/dateTime.d.ts +95 -0
- package/dist/Functions/fieldValueFormatters.d.ts +19 -0
- package/dist/Functions/hooks/useFormFieldsChangesManager.d.ts +15 -0
- package/dist/Functions/locale/createTranslator.d.ts +3 -0
- package/dist/Functions/operations.d.ts +1 -0
- package/dist/Functions/presets/inputMaskPresets.d.ts +136 -0
- package/dist/Functions/presets/inputPresets.d.ts +16 -0
- package/dist/Functions/presets/mobileKeyboardTypesPresets.d.ts +17 -0
- package/dist/Functions/schemas.d.ts +3 -0
- package/dist/Functions/sdk/runtime-sdk/client.d.ts +1 -1
- package/dist/Functions/useBodyScrollLock.d.ts +2 -0
- package/dist/Functions/useClickOutside.d.ts +1 -0
- package/dist/Functions/useDebounce.d.ts +4 -0
- package/dist/Functions/useFieldFocus.d.ts +7 -0
- package/dist/Functions/useFormTools/form-drivers/ArrayWithObjects.d.ts +15 -0
- package/dist/Functions/useFormTools/form-drivers/ObjectWithIterableObjects.d.ts +12 -0
- package/dist/Functions/useFormTools/form-drivers/ObjectWithNamedKeyObjects.d.ts +15 -0
- package/dist/Functions/useFormTools/functions/General.d.ts +15 -0
- package/dist/Functions/useFormTools/functions/RenderFields.d.ts +7 -0
- package/dist/Functions/useFormTools/functions/usePrevious.d.ts +2 -0
- package/dist/Functions/useFormTools/index.d.ts +86 -0
- package/dist/Functions/useInputHighlightError.d.ts +12 -0
- package/dist/Functions/useLocalStorage.d.ts +2 -0
- package/dist/Functions/useLocationParams.d.ts +1 -0
- package/dist/Functions/useMediaQuery.d.ts +2 -0
- package/dist/Functions/useMetaInfo.d.ts +8 -0
- package/dist/Functions/useMouseUpOutside.d.ts +1 -0
- package/dist/Functions/useOnlineStatus.d.ts +2 -0
- package/dist/Functions/usePasswordChecker.d.ts +7 -0
- package/dist/Functions/usePrevious.d.ts +2 -0
- package/dist/Functions/useResize.d.ts +3 -0
- package/dist/Functions/useScrollTo.d.ts +2 -0
- package/dist/Functions/useToggle.d.ts +7 -0
- package/dist/Functions/utils.d.ts +40 -0
- package/dist/Langs.d.ts +179 -0
- package/dist/Molecular/FormWithDependOn/FormWithDependOn.d.ts +3 -0
- package/dist/Molecular/FormWithDependOn/FormWithDependOn.interface.d.ts +2 -2
- package/dist/Molecular/FormWithDependOn/partials/_utils.d.ts +8 -0
- package/dist/charts.cjs.map +1 -1
- package/dist/classes.cjs +629 -237
- package/dist/classes.cjs.map +4 -4
- package/dist/classes.js +629 -237
- package/dist/classes.js.map +4 -4
- package/dist/index.cjs +627 -235
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +627 -235
- package/dist/index.js.map +4 -4
- package/dist/sdk.cjs.map +1 -1
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js.map +1 -1
- package/package.json +5 -3
- package/styles.css +1 -0
package/dist/index.js
CHANGED
|
@@ -399,7 +399,11 @@ var AnimatedHandler = class {
|
|
|
399
399
|
};
|
|
400
400
|
var AnimatedHandler_default = AnimatedHandler;
|
|
401
401
|
|
|
402
|
-
// src/Classes/RESTAPI/partials/AbortableFetch.
|
|
402
|
+
// src/Classes/RESTAPI/partials/AbortableFetch.ts
|
|
403
|
+
var HTTP_METHODS = ["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"];
|
|
404
|
+
function isHttpMethod(value) {
|
|
405
|
+
return HTTP_METHODS.includes(value);
|
|
406
|
+
}
|
|
403
407
|
var RESPONSE_AS_OBJECT_ALWAYS_VALUE2 = "always";
|
|
404
408
|
var AbortableFetch2 = class {
|
|
405
409
|
#pathPrefix = null;
|
|
@@ -412,6 +416,8 @@ var AbortableFetch2 = class {
|
|
|
412
416
|
#catchCallback = null;
|
|
413
417
|
#everyPromiseCallback = null;
|
|
414
418
|
#isResponseAsObject = false;
|
|
419
|
+
// ? Последний вход request() — используется repeatRequest() для повторного запроса той же пачки.
|
|
420
|
+
requestInput = [];
|
|
415
421
|
constructor(input) {
|
|
416
422
|
if (input && !getIsOnlyAnObject(input)) throw new Error("Not valid input value!");
|
|
417
423
|
this.getPathPrefix = this.getPathPrefix.bind(this);
|
|
@@ -442,20 +448,30 @@ var AbortableFetch2 = class {
|
|
|
442
448
|
this.repeatRequest = this.repeatRequest.bind(this);
|
|
443
449
|
this.setProps(input || {});
|
|
444
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Выполняет один или несколько HTTP-запросов (нативный fetch) с возможностью отмены.
|
|
453
|
+
*
|
|
454
|
+
* Метод `abort()` навешивается на прототип возвращаемого промиса (как в исходной реализации),
|
|
455
|
+
* что позволяет отменить всю цепочку запросов вызовом `abort()` на возвращённом промисе.
|
|
456
|
+
*
|
|
457
|
+
* @param props - путь, объект описания запроса или их массив.
|
|
458
|
+
* @returns Промис с результатом (форма зависит от isResponseAsObject); на прототипе доступен `abort()`.
|
|
459
|
+
*/
|
|
445
460
|
async request(props) {
|
|
446
461
|
if (!getIsOnlyAnObject(props) && !Array.isArray(props) && typeof props !== "string") throw new Error("Not valid props value!");
|
|
447
|
-
const stringifyBody = (b) => b
|
|
462
|
+
const stringifyBody = (b) => b instanceof FormData ? b : JSON.stringify(b);
|
|
448
463
|
this.requestInput = props;
|
|
449
464
|
const requests = getRequestsArray.call(this, props);
|
|
450
465
|
const ABORTABLE_FETCH_CONTEXT = this;
|
|
451
466
|
const abortController = new AbortController();
|
|
452
467
|
const addProps = (response, request) => {
|
|
453
|
-
response
|
|
454
|
-
|
|
455
|
-
|
|
468
|
+
return Object.assign(response, {
|
|
469
|
+
request,
|
|
470
|
+
...ABORTABLE_FETCH_CONTEXT.#isResponseAsObject ? { name: getIsOnlyAnObject(request) ? request.name || "" : "" } : {}
|
|
471
|
+
});
|
|
456
472
|
};
|
|
457
|
-
|
|
458
|
-
requests.map(
|
|
473
|
+
const externalRequest = Promise.all(
|
|
474
|
+
requests.map((item) => {
|
|
459
475
|
const safelyQuery = item.path.includes("?") ? `&${item.queryParameters.replace(/\?/g, "")}` : item.queryParameters;
|
|
460
476
|
const internalRequest = fetch(`${item.pathPrefix}${item.path}${safelyQuery}`, {
|
|
461
477
|
...item.options,
|
|
@@ -466,12 +482,15 @@ var AbortableFetch2 = class {
|
|
|
466
482
|
}).then(async (response) => addProps(response, item)).catch(async (response) => {
|
|
467
483
|
if (response instanceof Response) return Promise.resolve(addProps(response, item));
|
|
468
484
|
return Promise.resolve(
|
|
469
|
-
addProps(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
485
|
+
addProps(
|
|
486
|
+
{
|
|
487
|
+
ok: false,
|
|
488
|
+
status: 0,
|
|
489
|
+
statusText: response,
|
|
490
|
+
request: item
|
|
491
|
+
},
|
|
492
|
+
item
|
|
493
|
+
)
|
|
475
494
|
);
|
|
476
495
|
}).then(async (res) => {
|
|
477
496
|
return typeof ABORTABLE_FETCH_CONTEXT.#everyPromiseCallback === "function" && !item.isSkipEveryPromiseCallback ? await ABORTABLE_FETCH_CONTEXT.#everyPromiseCallback(res) : res;
|
|
@@ -480,7 +499,8 @@ var AbortableFetch2 = class {
|
|
|
480
499
|
}).then(async (res) => {
|
|
481
500
|
return { request: item, response: res, ...item.name ? { name: item.name } : {} };
|
|
482
501
|
});
|
|
483
|
-
|
|
502
|
+
const internalProto = Object.getPrototypeOf(internalRequest);
|
|
503
|
+
internalProto.abort = () => abortController.abort();
|
|
484
504
|
return internalRequest;
|
|
485
505
|
})
|
|
486
506
|
).then(async (resArr) => {
|
|
@@ -488,16 +508,17 @@ var AbortableFetch2 = class {
|
|
|
488
508
|
return ABORTABLE_FETCH_CONTEXT.#isResponseAsObject ? {} : [];
|
|
489
509
|
}
|
|
490
510
|
if (ABORTABLE_FETCH_CONTEXT.#isResponseAsObject === RESPONSE_AS_OBJECT_ALWAYS_VALUE2) {
|
|
491
|
-
return resArr.reduce((ac, item) => ({ ...ac, [item.name]: item.response }), {});
|
|
511
|
+
return resArr.reduce((ac, item) => ({ ...ac, [String(item.name)]: item.response }), {});
|
|
492
512
|
}
|
|
493
513
|
if (resArr.length === 1) return resArr[0].response;
|
|
494
|
-
return ABORTABLE_FETCH_CONTEXT.#isResponseAsObject === true ? resArr.reduce((ac, item) => ({ ...ac, [item.name]: item.response }), {}) : resArr.map((item) => item.response);
|
|
514
|
+
return ABORTABLE_FETCH_CONTEXT.#isResponseAsObject === true ? resArr.reduce((ac, item) => ({ ...ac, [String(item.name)]: item.response }), {}) : resArr.map((item) => item.response);
|
|
495
515
|
}).then(async (res) => {
|
|
496
516
|
return typeof ABORTABLE_FETCH_CONTEXT.#callback === "function" ? await ABORTABLE_FETCH_CONTEXT.#callback(res) : res;
|
|
497
517
|
}).catch(async (res) => {
|
|
498
518
|
return typeof ABORTABLE_FETCH_CONTEXT.#catchCallback === "function" ? await ABORTABLE_FETCH_CONTEXT.#catchCallback(res) : res;
|
|
499
519
|
});
|
|
500
|
-
|
|
520
|
+
const externalProto = Object.getPrototypeOf(externalRequest);
|
|
521
|
+
externalProto.abort = () => abortController.abort();
|
|
501
522
|
return externalRequest;
|
|
502
523
|
function getRequestsArray(input) {
|
|
503
524
|
const requestsArray = (() => {
|
|
@@ -522,15 +543,17 @@ var AbortableFetch2 = class {
|
|
|
522
543
|
callback: omitCallback,
|
|
523
544
|
mergeProps: omitMergeProps,
|
|
524
545
|
...optionsRest
|
|
525
|
-
} = options
|
|
546
|
+
} = getIsOnlyAnObject(options) ? options : {};
|
|
547
|
+
const localMethod = typeof method === "string" && isHttpMethod(method) ? method : null;
|
|
548
|
+
const localCallback = typeof callback === "function" ? (res) => callback(res) : void 0;
|
|
526
549
|
return [
|
|
527
550
|
...ac,
|
|
528
551
|
{
|
|
529
552
|
path,
|
|
530
553
|
pathPrefix: getMergeProps("pathPrefix") === "onlyLocal" || isNoPathPrefix ? "" : this.#pathPrefix || "",
|
|
531
554
|
method: (() => {
|
|
532
|
-
if (getMergeProps("method") === "onlyLocal") return
|
|
533
|
-
return getMergeProps("method") === "onlyGlobal" ? this.#method || "GET" :
|
|
555
|
+
if (getMergeProps("method") === "onlyLocal") return localMethod || "GET";
|
|
556
|
+
return getMergeProps("method") === "onlyGlobal" ? this.#method || "GET" : localMethod || this.#method || "GET";
|
|
534
557
|
})(),
|
|
535
558
|
queryParameters: (() => {
|
|
536
559
|
if ((getMergeProps("queryParameters") ?? getMergeProps("query")) === "onlyLocal") {
|
|
@@ -539,31 +562,34 @@ var AbortableFetch2 = class {
|
|
|
539
562
|
return (getMergeProps("queryParameters") ?? getMergeProps("query")) === "onlyGlobal" ? this.getQueryString(this.getQueryArray(this.#queryParameters)) : this.getQueryString([...this.getQueryArray(this.#queryParameters), ...this.getQueryArray(queryParameters)]);
|
|
540
563
|
})(),
|
|
541
564
|
headers: (() => {
|
|
542
|
-
if (getMergeProps("headers") === "onlyLocal") return this.getHeadersObj(headers
|
|
565
|
+
if (getMergeProps("headers") === "onlyLocal") return this.getHeadersObj(getIsOnlyAnObject(headers) ? headers : {});
|
|
543
566
|
return getMergeProps("headers") === "onlyGlobal" ? this.getHeadersObj({ ...this.#headers || {} }) : this.getHeadersObj({
|
|
544
567
|
...this.#headers || {},
|
|
545
|
-
...headers
|
|
568
|
+
...getIsOnlyAnObject(headers) ? headers : {}
|
|
546
569
|
});
|
|
547
570
|
})(),
|
|
548
571
|
body: (() => {
|
|
549
572
|
if (getMergeProps("body") === "onlyLocal") return body || null;
|
|
550
573
|
if (getMergeProps("body") === "onlyGlobal") return this.#body || null;
|
|
551
574
|
if (body && this.#body && typeof body === typeof this.#body && typeof body === "object") {
|
|
552
|
-
return Array.isArray(body) ? [...this.#body, ...body] : { ...this.#body, ...body };
|
|
553
|
-
} else
|
|
575
|
+
return Array.isArray(body) ? [...Array.isArray(this.#body) ? this.#body : [], ...body] : { ...getIsOnlyAnObject(this.#body) ? this.#body : {}, ...getIsOnlyAnObject(body) ? body : {} };
|
|
576
|
+
} else {
|
|
577
|
+
return body || this.#body || null;
|
|
578
|
+
}
|
|
554
579
|
})(),
|
|
555
580
|
options: (() => {
|
|
556
|
-
if (getMergeProps("options") === "onlyLocal") return { ...optionsRest
|
|
557
|
-
return getMergeProps("options") === "onlyGlobal" ? { ...this.#options || {} } : { ...this.#options || {}, ...optionsRest
|
|
581
|
+
if (getMergeProps("options") === "onlyLocal") return { ...optionsRest };
|
|
582
|
+
return getMergeProps("options") === "onlyGlobal" ? { ...this.#options || {} } : { ...this.#options || {}, ...optionsRest };
|
|
558
583
|
})(),
|
|
559
|
-
callback,
|
|
560
|
-
isSkipEveryPromiseCallback,
|
|
584
|
+
callback: localCallback,
|
|
585
|
+
isSkipEveryPromiseCallback: Boolean(isSkipEveryPromiseCallback),
|
|
561
586
|
...this.getIsResponseAsObject() ? { name: name && typeof name === "string" ? name : index } : {}
|
|
562
587
|
}
|
|
563
588
|
];
|
|
564
589
|
}, []);
|
|
565
590
|
}
|
|
566
591
|
}
|
|
592
|
+
/** Повторяет последний выполненный запрос (ту же пачку). */
|
|
567
593
|
repeatRequest() {
|
|
568
594
|
return this.request(this.requestInput);
|
|
569
595
|
}
|
|
@@ -579,19 +605,23 @@ var AbortableFetch2 = class {
|
|
|
579
605
|
return this.#method;
|
|
580
606
|
}
|
|
581
607
|
setMethod(value) {
|
|
582
|
-
|
|
583
|
-
if (value && !methods.includes(value)) throw new Error("Not valid method value!");
|
|
608
|
+
if (value && !isHttpMethod(value)) throw new Error("Not valid method value!");
|
|
584
609
|
this.#method = value || null;
|
|
585
610
|
}
|
|
586
611
|
getQueryParameters() {
|
|
587
612
|
return this.#queryParameters;
|
|
588
613
|
}
|
|
589
614
|
getQueryArray(value) {
|
|
590
|
-
if (typeof value === "string")
|
|
591
|
-
|
|
592
|
-
else if (
|
|
593
|
-
return
|
|
594
|
-
} else
|
|
615
|
+
if (typeof value === "string") {
|
|
616
|
+
return (value[0] === "?" ? value.slice(1) : value).split("&");
|
|
617
|
+
} else if (Array.isArray(value)) {
|
|
618
|
+
return value.map((item) => `${item}`);
|
|
619
|
+
} else if (typeof value === "object" && value !== null) {
|
|
620
|
+
const record = getIsOnlyAnObject(value) ? value : {};
|
|
621
|
+
return Object.keys(record).filter((key) => Boolean(key) && Boolean(record[key])).map((key) => `${key}=${record[key]}`);
|
|
622
|
+
} else {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
595
625
|
}
|
|
596
626
|
getQueryString(value) {
|
|
597
627
|
if (!value) return "";
|
|
@@ -610,7 +640,7 @@ var AbortableFetch2 = class {
|
|
|
610
640
|
if (!obj) return headers;
|
|
611
641
|
if (typeof obj !== "object" || Array.isArray(obj)) throw new Error("Not valid headers value!");
|
|
612
642
|
Object.keys(obj).forEach((key) => {
|
|
613
|
-
headers.append(key, obj[key]);
|
|
643
|
+
headers.append(key, `${obj[key]}`);
|
|
614
644
|
});
|
|
615
645
|
return headers;
|
|
616
646
|
}
|
|
@@ -686,39 +716,64 @@ var AbortableFetch2 = class {
|
|
|
686
716
|
// isAnywayRunCallback,
|
|
687
717
|
isResponseAsObject
|
|
688
718
|
} = input;
|
|
689
|
-
if (pathPrefix) this.setPathPrefix(pathPrefix);
|
|
690
|
-
if (method) this.setMethod(method);
|
|
719
|
+
if (pathPrefix) this.setPathPrefix(typeof pathPrefix === "string" ? pathPrefix : null);
|
|
720
|
+
if (method) this.setMethod(typeof method === "string" && isHttpMethod(method) ? method : null);
|
|
691
721
|
if (queryParameters) this.setQueryParameters(this.getQueryArray(queryParameters));
|
|
692
|
-
if (headers) this.setHeaders(headers);
|
|
722
|
+
if (headers) this.setHeaders(getIsOnlyAnObject(headers) ? headers : null);
|
|
693
723
|
if (body) this.setBody(body);
|
|
694
|
-
if (options) this.setOptions(options);
|
|
695
|
-
if (callback
|
|
696
|
-
|
|
697
|
-
|
|
724
|
+
if (options) this.setOptions(getIsOnlyAnObject(options) ? options : null);
|
|
725
|
+
if (callback && typeof callback === "function") {
|
|
726
|
+
const fn = callback;
|
|
727
|
+
this.setCallback(function wrappedCallback(response) {
|
|
728
|
+
return fn.call(this, response);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
if (catchCallback && typeof catchCallback === "function") {
|
|
732
|
+
const fn = catchCallback;
|
|
733
|
+
this.setCatchCallback(function wrappedCatchCallback(response) {
|
|
734
|
+
return fn.call(this, response);
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
if (everyPromiseCallback && typeof everyPromiseCallback === "function") {
|
|
738
|
+
const fn = everyPromiseCallback;
|
|
739
|
+
this.setEveryPromiseCallback(function wrappedEveryPromiseCallback(response) {
|
|
740
|
+
return fn.call(this, response);
|
|
741
|
+
});
|
|
742
|
+
}
|
|
698
743
|
if (isResponseAsObject !== void 0) this.setIsResponseAsObject(isResponseAsObject);
|
|
699
744
|
}
|
|
700
745
|
};
|
|
701
746
|
var AbortableFetch_default2 = AbortableFetch2;
|
|
702
747
|
|
|
703
|
-
// src/Classes/RESTAPI/partials/_utils.
|
|
748
|
+
// src/Classes/RESTAPI/partials/_utils.ts
|
|
704
749
|
function getIsOnlyAnObject(input) {
|
|
705
|
-
return typeof input === "object" &&
|
|
706
|
-
input instanceof Object && // "отбивает" null
|
|
707
|
-
!Array.isArray(input) && // "отбивает" массивы
|
|
708
|
-
!(input instanceof Set) && // "отбивает" сеты
|
|
709
|
-
!(input instanceof Map);
|
|
750
|
+
return typeof input === "object" && input instanceof Object && !Array.isArray(input) && !(input instanceof Set) && !(input instanceof Map);
|
|
710
751
|
}
|
|
711
|
-
|
|
752
|
+
function clone(input) {
|
|
712
753
|
if (input === null || typeof input !== "object") return input;
|
|
713
|
-
|
|
714
|
-
|
|
754
|
+
if (Array.isArray(input)) return input.map((item) => clone(item));
|
|
755
|
+
const source = getIsOnlyAnObject(input) ? input : {};
|
|
756
|
+
const data = {};
|
|
757
|
+
Object.keys(source).forEach((key) => {
|
|
758
|
+
data[key] = clone(source[key]);
|
|
759
|
+
});
|
|
715
760
|
return data;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
}
|
|
721
|
-
|
|
761
|
+
}
|
|
762
|
+
function omitKeys(obj, keys = []) {
|
|
763
|
+
const source = obj ?? {};
|
|
764
|
+
const keySet = new Set(keys);
|
|
765
|
+
const result = {};
|
|
766
|
+
Object.keys(source).forEach((key) => {
|
|
767
|
+
if (!keySet.has(key)) result[key] = source[key];
|
|
768
|
+
});
|
|
769
|
+
return result;
|
|
770
|
+
}
|
|
771
|
+
async function getResponseClone(res) {
|
|
772
|
+
if (res && typeof res === "object" && "clone" in res && typeof res.clone === "function") {
|
|
773
|
+
return res.clone();
|
|
774
|
+
}
|
|
775
|
+
return clone(res);
|
|
776
|
+
}
|
|
722
777
|
function addCustomMethods(data, prefix) {
|
|
723
778
|
const preparedPrefix = (prefix || "").toString();
|
|
724
779
|
Object.keys(data).forEach((key) => {
|
|
@@ -732,7 +787,7 @@ async function getInstanceOfFetchSystem(isGetBody) {
|
|
|
732
787
|
let credentials = {};
|
|
733
788
|
if (CREDENTIALS_CONTEXT) {
|
|
734
789
|
const cred = await CREDENTIALS_CONTEXT.getCredentials();
|
|
735
|
-
credentials = CREDENTIALS_CONTEXT.isUseRefreshTokensPropcessing ? await CREDENTIALS_CONTEXT.waitRefresh(cred.isNeedRefresh && !CREDENTIALS_CONTEXT.getIsTokenStartRefresh()).then(
|
|
790
|
+
credentials = CREDENTIALS_CONTEXT.isUseRefreshTokensPropcessing ? await CREDENTIALS_CONTEXT.waitRefresh(Boolean(cred.isNeedRefresh) && !CREDENTIALS_CONTEXT.getIsTokenStartRefresh()).then(
|
|
736
791
|
async () => CREDENTIALS_CONTEXT.processCredentials(await CREDENTIALS_CONTEXT.getCredentials())
|
|
737
792
|
) : cred;
|
|
738
793
|
}
|
|
@@ -750,52 +805,57 @@ async function getInstanceOfFetchSystem(isGetBody) {
|
|
|
750
805
|
}
|
|
751
806
|
return (CREDENTIALS_CONTEXT?.isUseRefreshTokensPropcessing ? CREDENTIALS_CONTEXT.waitRefresh() : Promise.resolve()).then(
|
|
752
807
|
async () => new AbortableFetch_default2({
|
|
753
|
-
|
|
808
|
+
// ? Метод по умолчанию
|
|
754
809
|
method: "GET",
|
|
755
|
-
|
|
810
|
+
// ? Заголовки по умолчанию
|
|
756
811
|
headers: {
|
|
757
812
|
...headersToAbortableFetchInstance,
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
813
|
+
// ? (*) Тут обрабатываются кейсы
|
|
814
|
+
// ? - когда нет this.credentialsProcessing
|
|
815
|
+
// ? (мы НЕ хотим добавлять токены в принципе, не зашли в верхнюю if-ку)
|
|
816
|
+
// ? - когда есть this.credentialsProcessing, но нет credentials.token (не авторизованы)
|
|
817
|
+
// ? (зашли в верхнюю if-ку, попробовали получить/обновить креды)
|
|
763
818
|
...credentials.token ? CREDENTIALS_CONTEXT?.getHeadersForAuthorize?.(credentials.token) || {} : {}
|
|
764
819
|
},
|
|
765
820
|
queryParameters: await API_CONTEXT.processQueryParams(queryParamsToAbortableFetchInstance),
|
|
766
|
-
|
|
821
|
+
// ? Опции по умолчанию
|
|
767
822
|
options: {
|
|
768
823
|
mode: "cors",
|
|
769
824
|
redirect: "follow",
|
|
770
825
|
...optionsToAbortableFetchInstance
|
|
771
826
|
},
|
|
772
|
-
|
|
827
|
+
// ? Каждый запрос (каждый запрос мультизапроса), сделаный методом request этого экземпляра выполнит этот колбек
|
|
773
828
|
everyPromiseCallback: async function everyPromiseCallback(response) {
|
|
774
829
|
const ABORTABLE_FETCH_INSTANCE = this;
|
|
775
830
|
if (!response.status) {
|
|
776
|
-
const
|
|
777
|
-
const
|
|
831
|
+
const reqInput = ABORTABLE_FETCH_INSTANCE.requestInput;
|
|
832
|
+
const responseRequest = getIsOnlyAnObject(response.request) ? response.request : {};
|
|
833
|
+
const request = Array.isArray(reqInput) ? reqInput.find((item) => getIsOnlyAnObject(item) && item.path === responseRequest.path) : void 0;
|
|
834
|
+
const mesageOptions = getIsOnlyAnObject(request) && getIsOnlyAnObject(request.mesageOptions) ? request.mesageOptions : void 0;
|
|
835
|
+
const isUseErrorToast = mesageOptions?.isUseErrorToast;
|
|
778
836
|
if (isUseErrorToast) {
|
|
779
|
-
API_CONTEXT.sendMessage?.(API_CONTEXT.NO_INET,
|
|
837
|
+
API_CONTEXT.sendMessage?.(API_CONTEXT.NO_INET, mesageOptions, response);
|
|
780
838
|
}
|
|
781
839
|
}
|
|
782
|
-
return API_CONTEXT.REJECT_CODES.includes(response.status) || CREDENTIALS_CONTEXT?.isUseRefreshTokensPropcessing && CREDENTIALS_CONTEXT?.CODES_USING_THE_REFRESH_ATTEMPT?.includes
|
|
840
|
+
return API_CONTEXT.REJECT_CODES.includes(Number(response.status)) || CREDENTIALS_CONTEXT?.isUseRefreshTokensPropcessing && Boolean(CREDENTIALS_CONTEXT?.CODES_USING_THE_REFRESH_ATTEMPT?.includes(Number(response.status))) ? Promise.reject(new Error(`status:${Number(response.status)}`)) : Promise.resolve(!isGetBody ? response : await API_CONTEXT.getResponseBody(response));
|
|
783
841
|
},
|
|
784
|
-
|
|
785
|
-
|
|
842
|
+
// ? Если хоть один запрос из пачки будет зареджекчен, зареджектится вся пачка.
|
|
843
|
+
// ? В этом случае будет выполнен этот колбэк
|
|
786
844
|
catchCallback: async function catchCallback(res) {
|
|
787
845
|
const ABORTABLE_FETCH_INSTANCE = this;
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
846
|
+
if (!CREDENTIALS_CONTEXT) return Promise.reject(res);
|
|
847
|
+
const code = Number(res instanceof Error ? res.message.split(":")[1] : "");
|
|
848
|
+
if (CREDENTIALS_CONTEXT.CODES_USING_THE_REFRESH_ATTEMPT?.includes(code)) {
|
|
849
|
+
const isDoRefresh = !CREDENTIALS_CONTEXT.getIsTokenStartRefresh();
|
|
850
|
+
return CREDENTIALS_CONTEXT.waitRefresh(isDoRefresh).then(async () => {
|
|
851
|
+
const newCredentials = await CREDENTIALS_CONTEXT.processCredentials({
|
|
852
|
+
...await CREDENTIALS_CONTEXT.getCredentials(true),
|
|
794
853
|
isCatchCallbackProcess: isDoRefresh
|
|
795
854
|
});
|
|
855
|
+
const currentHeaders = ABORTABLE_FETCH_INSTANCE.getHeaders();
|
|
796
856
|
ABORTABLE_FETCH_INSTANCE.setHeaders({
|
|
797
|
-
...
|
|
798
|
-
...newCredentials
|
|
857
|
+
...getIsOnlyAnObject(currentHeaders) ? currentHeaders : {},
|
|
858
|
+
...newCredentials.token ? { Authorization: `Bearer ${newCredentials.token}` } : {}
|
|
799
859
|
});
|
|
800
860
|
if (newCredentials.token) return ABORTABLE_FETCH_INSTANCE.repeatRequest();
|
|
801
861
|
return Promise.reject(res);
|
|
@@ -807,13 +867,23 @@ async function getInstanceOfFetchSystem(isGetBody) {
|
|
|
807
867
|
);
|
|
808
868
|
}
|
|
809
869
|
|
|
810
|
-
// src/Classes/RESTAPI/partials/Utils.
|
|
870
|
+
// src/Classes/RESTAPI/partials/Utils.ts
|
|
811
871
|
var Utils = class {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
872
|
+
// ! Могут содержаться НЕасинхронные методы!
|
|
873
|
+
/**
|
|
874
|
+
* Нормализатор сложных query-параметров.
|
|
875
|
+
*
|
|
876
|
+
* Например: `&key1=&&&===&&&key2====&&&&&&&&key3=...`. Для корректной работы на вход нужно подавать
|
|
877
|
+
* НЕ кодированные значения. По умолчанию выходные значения query-параметров НЕ кодируются.
|
|
878
|
+
*
|
|
879
|
+
* @param queryParams - query в виде строки, массива или объекта.
|
|
880
|
+
* @param settings - формат вывода (`array` | `string` | `object`) и флаг кодирования.
|
|
881
|
+
* @returns Нормализованные query-параметры в выбранном формате.
|
|
882
|
+
*/
|
|
883
|
+
// ? Ф-я normalizeQueryParams является нормализатором сложных квери
|
|
884
|
+
// ? Например: &key1=&&&===&&&key2====&&&&&&&&key3=....
|
|
885
|
+
// ? Для корректной работы на вход нужно подавать НЕ кодированные значения
|
|
886
|
+
// ? По умолчанию выходные значения квери-параметров НЕ кодируются
|
|
817
887
|
normalizeQueryParams(queryParams, settings) {
|
|
818
888
|
if (settings && !getIsOnlyAnObject(settings)) {
|
|
819
889
|
throw new Error("Bad settings [class Utils, normalizeQueryParams]");
|
|
@@ -832,9 +902,13 @@ var Utils = class {
|
|
|
832
902
|
if (index === -1) return [item];
|
|
833
903
|
return [item.slice(0, index), item.slice(index + 1)];
|
|
834
904
|
}).filter(([key]) => Boolean(key)).map(([key, value]) => prepareParam(key, value));
|
|
835
|
-
} else if (getIsOnlyAnObject(input))
|
|
836
|
-
|
|
837
|
-
else
|
|
905
|
+
} else if (getIsOnlyAnObject(input)) {
|
|
906
|
+
return Object.keys(input).map((key) => prepareParam(key, input[key]));
|
|
907
|
+
} else if (typeof input === "string") {
|
|
908
|
+
return (input[0] === "?" ? input.slice(1) : input).split("&");
|
|
909
|
+
} else {
|
|
910
|
+
return [];
|
|
911
|
+
}
|
|
838
912
|
};
|
|
839
913
|
const { isEncode = false, outputAs = "array" } = settings || {};
|
|
840
914
|
const initialStructure = getInitialArr(queryParams);
|
|
@@ -851,7 +925,9 @@ var Utils = class {
|
|
|
851
925
|
if (index !== -1 && index !== 0) {
|
|
852
926
|
const key = str.slice(0, index);
|
|
853
927
|
acc.result.push(`${key}=${encodeURIComponent(str.slice(index + 1))}`);
|
|
854
|
-
} else
|
|
928
|
+
} else {
|
|
929
|
+
acc.toJoin = str;
|
|
930
|
+
}
|
|
855
931
|
if (idx === initialStructure.length - 1 && typeof acc.toJoin === "string") processToJoin(acc.toJoin);
|
|
856
932
|
return acc;
|
|
857
933
|
},
|
|
@@ -859,7 +935,7 @@ var Utils = class {
|
|
|
859
935
|
).result;
|
|
860
936
|
const outputStructure = normalizedStructure.map((item) => {
|
|
861
937
|
const [key, value] = item.split("=");
|
|
862
|
-
return [key, !isEncode ? decodeURIComponent(value) : value];
|
|
938
|
+
return [key, !isEncode ? decodeURIComponent(value ?? "") : value ?? ""];
|
|
863
939
|
});
|
|
864
940
|
switch (outputAs) {
|
|
865
941
|
case "string":
|
|
@@ -873,16 +949,25 @@ var Utils = class {
|
|
|
873
949
|
};
|
|
874
950
|
var Utils_default = Utils;
|
|
875
951
|
|
|
876
|
-
// src/Classes/RESTAPI/partials/ApiUtils.
|
|
952
|
+
// src/Classes/RESTAPI/partials/ApiUtils.ts
|
|
877
953
|
var ApiUtils = class extends Utils_default {
|
|
954
|
+
/** Корневой путь API (раскладывается в ApiBase из constants.API_PATH). */
|
|
955
|
+
API_PATH;
|
|
878
956
|
constructor(settings) {
|
|
879
957
|
super();
|
|
880
958
|
const { utils } = settings || {};
|
|
881
959
|
if (getIsOnlyAnObject(utils)) addCustomMethods.call(this, utils);
|
|
882
960
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
961
|
+
/**
|
|
962
|
+
* Разбирает единый объект параметров запроса на части в зависимости от варианта использования.
|
|
963
|
+
*
|
|
964
|
+
* @param apiParams - объединённые параметры запроса.
|
|
965
|
+
* @param variant - `'doRequestMapping'` для мультизапроса либо `undefined` для одиночного.
|
|
966
|
+
* @returns Для `doRequestMapping` — объект параметров; иначе кортеж `[параметры, настройки]`.
|
|
967
|
+
*/
|
|
968
|
+
// ? Планируется, что метод будет использоваться неизменно, однако технически его можно перегрузить на экземпляре
|
|
969
|
+
// ? Метод не планировался как асинхронный,
|
|
970
|
+
// ? однако ввиду того что он может быть перегружен на экземпляре асинхронность предусматривается
|
|
886
971
|
async splitProperties(apiParams, variant) {
|
|
887
972
|
const {
|
|
888
973
|
isResponseAsObject,
|
|
@@ -941,79 +1026,224 @@ var ApiUtils = class extends Utils_default {
|
|
|
941
1026
|
];
|
|
942
1027
|
}
|
|
943
1028
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1029
|
+
/**
|
|
1030
|
+
* Обработчик пропы `api` из объекта описания запроса — возвращает корневой путь API.
|
|
1031
|
+
*
|
|
1032
|
+
* @returns Корневой путь (по умолчанию — API_PATH).
|
|
1033
|
+
*/
|
|
1034
|
+
// ! Обработчик пропы api из объекта описания запроса
|
|
1035
|
+
// ? Планируется, что метод будет перегружаться на экземпляре,
|
|
1036
|
+
// ? т.к. на разных проектах могут быть разные интерпретации пропы api
|
|
1037
|
+
// ? Метод не планировался как асинхронный,
|
|
1038
|
+
// ? однако ввиду того что он может быть перегружен на экземпляре асинхронность предусматривается
|
|
949
1039
|
async getRootPath() {
|
|
950
1040
|
return this.API_PATH;
|
|
951
1041
|
}
|
|
952
|
-
|
|
953
|
-
|
|
1042
|
+
/**
|
|
1043
|
+
* Извлекает тело ответа в нужном виде (json/blob/text/arrayBuffer/formData) либо по content-type.
|
|
1044
|
+
*
|
|
1045
|
+
* @param response - объект Response (иначе значение возвращается как есть).
|
|
1046
|
+
* @param getBodyAs - желаемый способ извлечения тела.
|
|
1047
|
+
* @returns Промис с телом ответа (или исходным значением / null при ошибке).
|
|
1048
|
+
*/
|
|
1049
|
+
// ? Планируется, что метод будет использоваться неизменно, однако технически его можно перегрузить на экземпляре
|
|
1050
|
+
// ? Метод планировался как асинхронный
|
|
954
1051
|
async getResponseBody(response, getBodyAs) {
|
|
955
1052
|
if (!(response instanceof Response)) return response;
|
|
956
1053
|
const contentType = response.headers.get("content-type");
|
|
957
1054
|
if (!contentType) return "";
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1055
|
+
const bodyReaders = {
|
|
1056
|
+
json: (r) => r.json(),
|
|
1057
|
+
blob: (r) => r.blob(),
|
|
1058
|
+
text: (r) => r.text(),
|
|
1059
|
+
arrayBuffer: (r) => r.arrayBuffer(),
|
|
1060
|
+
formData: (r) => r.formData()
|
|
1061
|
+
};
|
|
1062
|
+
const isBodyKey = (key) => key in bodyReaders;
|
|
1063
|
+
const reader = typeof getBodyAs === "string" && isBodyKey(getBodyAs) ? bodyReaders[getBodyAs] : contentType.includes("text/csv") ? bodyReaders.blob : contentType.includes("json") ? bodyReaders.json : bodyReaders.text;
|
|
967
1064
|
try {
|
|
968
|
-
return await
|
|
1065
|
+
return await reader(response);
|
|
969
1066
|
} catch {
|
|
970
1067
|
console.log("Failed to extract the body");
|
|
971
1068
|
return null;
|
|
972
1069
|
}
|
|
973
1070
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1071
|
+
/**
|
|
1072
|
+
* Формирует структуру ошибки из тела ответа (точка расширения под формат бэкенда).
|
|
1073
|
+
*
|
|
1074
|
+
* @param res - уже извлечённое тело ответа.
|
|
1075
|
+
* @returns Структура сообщения об ошибке.
|
|
1076
|
+
*/
|
|
1077
|
+
// ! Обработчик структуры для ошибки (не рендер месседжа, а имеено формирование ошибки),
|
|
1078
|
+
// ? Планируется, что метод будет перегружаться на экземпляре,
|
|
1079
|
+
// ? т.к. на разных проектах могут быть разные структуры тела ответа для ошибки
|
|
1080
|
+
// ? Метод не планировался как асинхронный,
|
|
1081
|
+
// ? однако ввиду того что он может быть перегружен на экземпляре асинхронность предусматривается
|
|
1082
|
+
// ? res - уже тело ответа
|
|
980
1083
|
async getErrorMessage(res) {
|
|
981
1084
|
return res;
|
|
982
1085
|
}
|
|
983
|
-
|
|
984
|
-
|
|
1086
|
+
/**
|
|
1087
|
+
* Проверяет код ответа и (если задан sendMessage) информирует пользователя об успехе/ошибке.
|
|
1088
|
+
*
|
|
1089
|
+
* @param res - объект Response (или совместимый с полем `ok`).
|
|
1090
|
+
* @param settings - сообщения и настройки тостов для данного запроса.
|
|
1091
|
+
* @returns Исходный `res` (метод не меняет ответ, только сигналит сообщением).
|
|
1092
|
+
*/
|
|
1093
|
+
// ? Планируется, что метод будет использоваться неизменно, однако технически его можно перегрузить на экземпляре
|
|
1094
|
+
// ? Метод планировался как асинхронный
|
|
985
1095
|
async checkResponseCode(res, settings) {
|
|
986
|
-
|
|
1096
|
+
const send = this.sendMessage;
|
|
1097
|
+
if (typeof send !== "function") return res;
|
|
987
1098
|
const { successMess, errorMess } = settings || {};
|
|
988
|
-
const
|
|
1099
|
+
const isOk = getIsOnlyAnObject(res) ? Boolean(res.ok) : false;
|
|
1100
|
+
const message = isOk ? successMess || this.DEFAULT_SUCCESS_MESSAGE : errorMess || await this.getErrorMessage(await this.getResponseBody(await getResponseClone(res)));
|
|
989
1101
|
try {
|
|
990
|
-
|
|
991
|
-
} catch
|
|
1102
|
+
send(message, settings, await getResponseClone(res));
|
|
1103
|
+
} catch {
|
|
992
1104
|
}
|
|
993
1105
|
return res;
|
|
994
1106
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1107
|
+
/**
|
|
1108
|
+
* Структуры, которые будут использоваться для КАЖДОГО запроса, сделанного экземпляром RESTAPI.
|
|
1109
|
+
*
|
|
1110
|
+
* @returns Объект `{ headers, queryParameters, options }` (по умолчанию пустой).
|
|
1111
|
+
*/
|
|
1112
|
+
// ? Суть метода:
|
|
1113
|
+
// ? передать из вне структуры, которые будут использоваться для КАЖДОГО запроса сделанного экземпляром RESTAPI
|
|
1114
|
+
// ! Данные затрут прочие настройки КРОМЕ данных по логике авторизации
|
|
1115
|
+
// ? Планируется, что метод будет перегружаться на экземпляре
|
|
1116
|
+
// ? Метод не планировался как асинхронный,
|
|
1117
|
+
// ? однако ввиду того что он может быть перегружен на экземпляре асинхронность предусматривается
|
|
1001
1118
|
async addAsCommon() {
|
|
1002
1119
|
return {};
|
|
1003
1120
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1121
|
+
/**
|
|
1122
|
+
* Общий обработчик query-параметров (точка расширения, напр. кодирование/приведение к массиву).
|
|
1123
|
+
*
|
|
1124
|
+
* @param queryParams - входные query-параметры.
|
|
1125
|
+
* @returns Обработанные query-параметры (по умолчанию — без изменений).
|
|
1126
|
+
*/
|
|
1127
|
+
// ! Обработчик квери-параметров для общего процессинга,
|
|
1128
|
+
// ! Например для кодирования decodeURIComponent или/и приведения в формат массива
|
|
1129
|
+
// ? Планируется, что метод будет перегружаться на экземпляре,
|
|
1130
|
+
// ? т.к. на разных проектах могут быть разные договоренности по форматам
|
|
1131
|
+
// ? Метод не планировался как асинхронный,
|
|
1132
|
+
// ? однако ввиду того что он может быть перегружен на экземпляре асинхронность предусматривается
|
|
1010
1133
|
async processQueryParams(queryParams) {
|
|
1011
1134
|
return queryParams;
|
|
1012
1135
|
}
|
|
1013
1136
|
};
|
|
1014
1137
|
var ApiUtils_default = ApiUtils;
|
|
1015
1138
|
|
|
1016
|
-
// src/Classes/RESTAPI/partials/
|
|
1139
|
+
// src/Classes/RESTAPI/partials/sse.ts
|
|
1140
|
+
function parseSSEEvent(block) {
|
|
1141
|
+
if (typeof block !== "string") return null;
|
|
1142
|
+
const event = { event: "message", data: "", id: void 0, retry: void 0 };
|
|
1143
|
+
const dataLines = [];
|
|
1144
|
+
let hasField = false;
|
|
1145
|
+
block.split("\n").forEach((line) => {
|
|
1146
|
+
if (line === "" || line[0] === ":") return;
|
|
1147
|
+
const colonIdx = line.indexOf(":");
|
|
1148
|
+
const field = colonIdx === -1 ? line : line.slice(0, colonIdx);
|
|
1149
|
+
let value = colonIdx === -1 ? "" : line.slice(colonIdx + 1);
|
|
1150
|
+
if (value[0] === " ") value = value.slice(1);
|
|
1151
|
+
switch (field) {
|
|
1152
|
+
case "event":
|
|
1153
|
+
event.event = value;
|
|
1154
|
+
hasField = true;
|
|
1155
|
+
break;
|
|
1156
|
+
case "data":
|
|
1157
|
+
dataLines.push(value);
|
|
1158
|
+
hasField = true;
|
|
1159
|
+
break;
|
|
1160
|
+
case "id":
|
|
1161
|
+
event.id = value;
|
|
1162
|
+
hasField = true;
|
|
1163
|
+
break;
|
|
1164
|
+
case "retry":
|
|
1165
|
+
if (/^\d+$/.test(value)) {
|
|
1166
|
+
event.retry = Number(value);
|
|
1167
|
+
hasField = true;
|
|
1168
|
+
}
|
|
1169
|
+
break;
|
|
1170
|
+
default:
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1173
|
+
});
|
|
1174
|
+
if (!hasField) return null;
|
|
1175
|
+
event.data = dataLines.join("\n");
|
|
1176
|
+
return event;
|
|
1177
|
+
}
|
|
1178
|
+
function safeCall(fn, ...args) {
|
|
1179
|
+
if (typeof fn !== "function") return;
|
|
1180
|
+
try {
|
|
1181
|
+
fn(...args);
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
async function readSSE(response, { onEvent, onChunk, handlers, parseJson } = {}) {
|
|
1186
|
+
const events = [];
|
|
1187
|
+
const stream = response.body;
|
|
1188
|
+
if (!stream) return events;
|
|
1189
|
+
const reader = stream.getReader();
|
|
1190
|
+
const decoder = new TextDecoder("utf-8");
|
|
1191
|
+
let buffer = "";
|
|
1192
|
+
let pendingCR = "";
|
|
1193
|
+
const normalize = (chunk) => chunk.replace(/\r\n|\r/g, "\n");
|
|
1194
|
+
const flush = (rawBlock) => {
|
|
1195
|
+
const event = parseSSEEvent(rawBlock);
|
|
1196
|
+
if (!event) return;
|
|
1197
|
+
if (parseJson && typeof event.data === "string" && event.data !== "") {
|
|
1198
|
+
try {
|
|
1199
|
+
event.data = JSON.parse(event.data);
|
|
1200
|
+
} catch {
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
events.push(event);
|
|
1204
|
+
safeCall(onEvent, event);
|
|
1205
|
+
safeCall(onChunk, event.data, event);
|
|
1206
|
+
if (handlers) safeCall(handlers[event.event], event.data, event);
|
|
1207
|
+
};
|
|
1208
|
+
try {
|
|
1209
|
+
while (true) {
|
|
1210
|
+
const { done, value } = await reader.read();
|
|
1211
|
+
if (done) break;
|
|
1212
|
+
let chunk = pendingCR + decoder.decode(value, { stream: true });
|
|
1213
|
+
pendingCR = "";
|
|
1214
|
+
if (chunk.endsWith("\r")) {
|
|
1215
|
+
pendingCR = "\r";
|
|
1216
|
+
chunk = chunk.slice(0, -1);
|
|
1217
|
+
}
|
|
1218
|
+
buffer += normalize(chunk);
|
|
1219
|
+
let sepIdx;
|
|
1220
|
+
while ((sepIdx = buffer.indexOf("\n\n")) !== -1) {
|
|
1221
|
+
const rawBlock = buffer.slice(0, sepIdx);
|
|
1222
|
+
buffer = buffer.slice(sepIdx + 2);
|
|
1223
|
+
flush(rawBlock);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
buffer += normalize(pendingCR + decoder.decode());
|
|
1227
|
+
buffer = buffer.replace(/\n+$/g, "");
|
|
1228
|
+
if (buffer !== "") flush(buffer);
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
const name = error && typeof error === "object" && "name" in error ? error.name : void 0;
|
|
1231
|
+
if (name !== "AbortError") throw error;
|
|
1232
|
+
} finally {
|
|
1233
|
+
try {
|
|
1234
|
+
reader.releaseLock?.();
|
|
1235
|
+
} catch {
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return events;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// src/Classes/RESTAPI/partials/ApiRequestCreators.ts
|
|
1242
|
+
function isStreamResponse(value) {
|
|
1243
|
+
if (!getIsOnlyAnObject(value) || !value.ok) return false;
|
|
1244
|
+
const body = value.body;
|
|
1245
|
+
return typeof body === "object" && body !== null && "getReader" in body && typeof body.getReader === "function";
|
|
1246
|
+
}
|
|
1017
1247
|
var PREFIX_OF_CLASS_UTILS = "createRequest_";
|
|
1018
1248
|
var ApiRequestCreators = class extends ApiUtils_default {
|
|
1019
1249
|
constructor(settings) {
|
|
@@ -1021,8 +1251,14 @@ var ApiRequestCreators = class extends ApiUtils_default {
|
|
|
1021
1251
|
const { requestsCreators } = settings || {};
|
|
1022
1252
|
if (getIsOnlyAnObject(requestsCreators)) addCustomMethods.call(this, requestsCreators, PREFIX_OF_CLASS_UTILS);
|
|
1023
1253
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1254
|
+
/**
|
|
1255
|
+
* Крейтер типа `toJson`: пользовательский callback получит тело ответа (Response.json()).
|
|
1256
|
+
*
|
|
1257
|
+
* @param props - объект описания запроса.
|
|
1258
|
+
* @param variant - вариант обработки splitProperties.
|
|
1259
|
+
*/
|
|
1260
|
+
// ? При типе toJson пользовательский callback на вход получит тело ответа реализованное методом Response.json()
|
|
1261
|
+
// ? независимо от насторойки isGetBody
|
|
1026
1262
|
async createRequest_toJson(props, variant) {
|
|
1027
1263
|
const safelyProps = omitKeys(props, ["getBodyAs"]);
|
|
1028
1264
|
return this.splitProperties(
|
|
@@ -1031,15 +1267,21 @@ var ApiRequestCreators = class extends ApiUtils_default {
|
|
|
1031
1267
|
isGetBody: false,
|
|
1032
1268
|
callback: async (res) => {
|
|
1033
1269
|
const output = await this.getResponseBody(res, "json");
|
|
1034
|
-
return await (
|
|
1270
|
+
return await (props.callback?.(output) ?? output);
|
|
1035
1271
|
}
|
|
1036
1272
|
},
|
|
1037
1273
|
variant
|
|
1038
1274
|
);
|
|
1039
1275
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1276
|
+
/**
|
|
1277
|
+
* Крейтер типа `toJsonAdvanced`: callback получит структуру `{ response, result }`.
|
|
1278
|
+
*
|
|
1279
|
+
* @param props - объект описания запроса.
|
|
1280
|
+
* @param variant - вариант обработки splitProperties.
|
|
1281
|
+
*/
|
|
1282
|
+
// ? При типе toJsonAdvanced пользовательский callback на вход получит структуру { response, result },
|
|
1283
|
+
// ? где response - объект Response, result - тело ответа реализованное методом Response.json()
|
|
1284
|
+
// ? независимо от насторойки isGetBody
|
|
1043
1285
|
async createRequest_toJsonAdvanced(props, variant) {
|
|
1044
1286
|
const safelyProps = omitKeys(props, ["getBodyAs"]);
|
|
1045
1287
|
return this.splitProperties(
|
|
@@ -1051,14 +1293,20 @@ var ApiRequestCreators = class extends ApiUtils_default {
|
|
|
1051
1293
|
response: res,
|
|
1052
1294
|
result: await this.getResponseBody(await getResponseClone(res), "json")
|
|
1053
1295
|
};
|
|
1054
|
-
return await (
|
|
1296
|
+
return await (props.callback?.(output) ?? output);
|
|
1055
1297
|
}
|
|
1056
1298
|
},
|
|
1057
1299
|
variant
|
|
1058
1300
|
);
|
|
1059
1301
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1302
|
+
/**
|
|
1303
|
+
* Крейтер типа `blob`: callback получит тело ответа (Response.blob()).
|
|
1304
|
+
*
|
|
1305
|
+
* @param props - объект описания запроса.
|
|
1306
|
+
* @param variant - вариант обработки splitProperties.
|
|
1307
|
+
*/
|
|
1308
|
+
// ? При типе blob пользовательский callback на вход получит тело ответа реализованное методом Response.blob(),
|
|
1309
|
+
// ? независимо от насторойки isGetBody
|
|
1062
1310
|
async createRequest_blob(props, variant) {
|
|
1063
1311
|
const safelyProps = omitKeys(props, ["getBodyAs"]);
|
|
1064
1312
|
return this.splitProperties(
|
|
@@ -1067,14 +1315,67 @@ var ApiRequestCreators = class extends ApiUtils_default {
|
|
|
1067
1315
|
isGetBody: false,
|
|
1068
1316
|
callback: async (res) => {
|
|
1069
1317
|
const output = await this.getResponseBody(await getResponseClone(res), "blob");
|
|
1070
|
-
return await (
|
|
1318
|
+
return await (props.callback?.(output) ?? output);
|
|
1319
|
+
}
|
|
1320
|
+
},
|
|
1321
|
+
variant
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Крейтер типа `sse`: обрабатывает ответ как поток Server-Sent Events (text/event-stream).
|
|
1326
|
+
*
|
|
1327
|
+
* Дополнительные пропы объекта описания запроса: `onEvent`, `onChunk`, `handlers`, `parseJson`.
|
|
1328
|
+
* По умолчанию добавляется заголовок `Accept: text/event-stream`. Метод doRequest резолвится
|
|
1329
|
+
* массивом всех событий ПОСЛЕ закрытия потока; отмена — методом `.abort()` возвращённого промиса.
|
|
1330
|
+
*
|
|
1331
|
+
* @param props - объект описания запроса (плюс onEvent/onChunk/handlers/parseJson).
|
|
1332
|
+
* @param variant - вариант обработки splitProperties.
|
|
1333
|
+
*/
|
|
1334
|
+
// ? При типе sse запрос обрабатывается как поток Server-Sent Events (text/event-stream).
|
|
1335
|
+
// ? Дополнительные пропы объекта описания запроса:
|
|
1336
|
+
// ? - onEvent(event) — вызывается на каждое распарсенное событие { event, data, id, retry };
|
|
1337
|
+
// ? - onChunk(data, event) — вызывается на полезную нагрузку (поле data) каждого события;
|
|
1338
|
+
// ? - handlers — карта { имяСобытия: cb } для типизированной обработки по полю event
|
|
1339
|
+
// ? (например { delta, status, done }); cb получает (data, event);
|
|
1340
|
+
// ? - parseJson — если true, поле data каждого события прогоняется через JSON.parse.
|
|
1341
|
+
// ? По умолчанию добавляется заголовок Accept: text/event-stream (его можно переопределить через headers).
|
|
1342
|
+
// ? Пользовательский callback (если передан) на вход получит итоговый массив всех событий.
|
|
1343
|
+
// ? Метод doRequest резолвится массивом всех событий ПОСЛЕ закрытия потока;
|
|
1344
|
+
// ? отмена потока выполняется методом .abort() возвращённого промиса (через встроенный AbortController).
|
|
1345
|
+
async createRequest_sse(props, variant) {
|
|
1346
|
+
const safelyProps = omitKeys(props, ["getBodyAs", "onChunk", "onEvent", "handlers", "parseJson"]);
|
|
1347
|
+
const { onChunk, onEvent, handlers, parseJson } = props;
|
|
1348
|
+
const sseOptions = {
|
|
1349
|
+
onChunk: typeof onChunk === "function" ? (data, event) => onChunk(data, event) : void 0,
|
|
1350
|
+
onEvent: typeof onEvent === "function" ? (event) => onEvent(event) : void 0,
|
|
1351
|
+
parseJson: Boolean(parseJson),
|
|
1352
|
+
handlers: getIsOnlyAnObject(handlers) ? Object.keys(handlers).reduce((acc, key) => {
|
|
1353
|
+
const fn = handlers[key];
|
|
1354
|
+
acc[key] = typeof fn === "function" ? (data, event) => fn(data, event) : void 0;
|
|
1355
|
+
return acc;
|
|
1356
|
+
}, {}) : void 0
|
|
1357
|
+
};
|
|
1358
|
+
return this.splitProperties(
|
|
1359
|
+
{
|
|
1360
|
+
...safelyProps,
|
|
1361
|
+
headers: { Accept: "text/event-stream", ...getIsOnlyAnObject(safelyProps.headers) ? safelyProps.headers : {} },
|
|
1362
|
+
isGetBody: false,
|
|
1363
|
+
callback: async (res) => {
|
|
1364
|
+
const output = isStreamResponse(res) ? await readSSE(res, sseOptions) : res;
|
|
1365
|
+
return await (props.callback?.(output) ?? output);
|
|
1071
1366
|
}
|
|
1072
1367
|
},
|
|
1073
1368
|
variant
|
|
1074
1369
|
);
|
|
1075
1370
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1371
|
+
/**
|
|
1372
|
+
* Крейтер типа `testBadResponse`: callback получит синтетический Response со статусом 401.
|
|
1373
|
+
*
|
|
1374
|
+
* @param props - объект описания запроса.
|
|
1375
|
+
* @param variant - вариант обработки splitProperties.
|
|
1376
|
+
*/
|
|
1377
|
+
// ? При типе testBadResponse пользовательский callback на вход получит объект Response в 401-м статусе
|
|
1378
|
+
// ? независимо независимо вообще ни от чего
|
|
1078
1379
|
async createRequest_testBadResponse(props, variant) {
|
|
1079
1380
|
return this.splitProperties(
|
|
1080
1381
|
{
|
|
@@ -1094,17 +1395,33 @@ var ApiRequestCreators = class extends ApiUtils_default {
|
|
|
1094
1395
|
variant
|
|
1095
1396
|
);
|
|
1096
1397
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1398
|
+
/**
|
|
1399
|
+
* Крейтер по умолчанию: callback получит объект Response (или тело ответа при isGetBody: 'first').
|
|
1400
|
+
*
|
|
1401
|
+
* @param props - объект описания запроса.
|
|
1402
|
+
* @param variant - вариант обработки splitProperties.
|
|
1403
|
+
*/
|
|
1404
|
+
// ? По умолчанию пользовательский callback на вход получит
|
|
1405
|
+
// ? - либо объект Response
|
|
1406
|
+
// ? - либо тело ответа (в случае isGetBody: 'first') полученное методом getResponseBody (смотри класс ApiUtils)
|
|
1100
1407
|
async createRequest_default(props, variant) {
|
|
1101
1408
|
return this.splitProperties(props, variant);
|
|
1102
1409
|
}
|
|
1103
1410
|
};
|
|
1104
1411
|
var ApiRequestCreators_default = ApiRequestCreators;
|
|
1105
1412
|
|
|
1106
|
-
// src/Classes/RESTAPI/partials/ApiBase.
|
|
1413
|
+
// src/Classes/RESTAPI/partials/ApiBase.ts
|
|
1107
1414
|
var ApiBase = class extends ApiRequestCreators_default {
|
|
1415
|
+
/** Текст ошибки невалидных данных запроса. */
|
|
1416
|
+
BAD_REQUEST_DATA_TEXT;
|
|
1417
|
+
/** Текст сообщения об успехе по умолчанию. */
|
|
1418
|
+
DEFAULT_SUCCESS_MESSAGE;
|
|
1419
|
+
/** Текст сообщения об ошибке по умолчанию. */
|
|
1420
|
+
DEFAULT_ERROR_MESSAGE;
|
|
1421
|
+
/** Текст сообщения об отсутствии сети. */
|
|
1422
|
+
NO_INET;
|
|
1423
|
+
/** Коды ответа, по которым запрос считается отклонённым. */
|
|
1424
|
+
REJECT_CODES;
|
|
1108
1425
|
constructor(settings) {
|
|
1109
1426
|
super(settings);
|
|
1110
1427
|
const {
|
|
@@ -1133,49 +1450,76 @@ var ApiBase = class extends ApiRequestCreators_default {
|
|
|
1133
1450
|
};
|
|
1134
1451
|
var ApiBase_default = ApiBase;
|
|
1135
1452
|
|
|
1136
|
-
// src/Classes/RESTAPI/partials/CredentialsProcessing.
|
|
1453
|
+
// src/Classes/RESTAPI/partials/CredentialsProcessing.ts
|
|
1137
1454
|
function decodeJWT(token) {
|
|
1138
1455
|
try {
|
|
1139
|
-
const payload = token.split(".")[1];
|
|
1140
|
-
|
|
1141
|
-
|
|
1456
|
+
const payload = token.split(".")[1] ?? "";
|
|
1457
|
+
const decoded = JSON.parse(atob(payload));
|
|
1458
|
+
return getIsOnlyAnObject(decoded) ? decoded : null;
|
|
1459
|
+
} catch {
|
|
1142
1460
|
return null;
|
|
1143
1461
|
}
|
|
1144
1462
|
}
|
|
1145
1463
|
var CredentialsProcessing = class {
|
|
1464
|
+
/** Включён ли флоу рефреша токенов. */
|
|
1465
|
+
isUseRefreshTokensPropcessing = false;
|
|
1466
|
+
/** Учётные данные по умолчанию (пустые). */
|
|
1467
|
+
DEFAULT_CREDENTIALS = {};
|
|
1468
|
+
/** Внешний метод получения учётных данных. */
|
|
1469
|
+
getCredentialsByOuter;
|
|
1470
|
+
/** Внешний метод формирования заголовков авторизации по токену. */
|
|
1471
|
+
getHeadersForAuthorize;
|
|
1472
|
+
/** Необязательный преобразователь учётных данных после getCredentials. */
|
|
1473
|
+
importCredentials;
|
|
1474
|
+
/** Необязательный метод сохранения учётных данных (обязателен для флоу рефреша). */
|
|
1475
|
+
saveCredentials;
|
|
1476
|
+
/** Внешний метод рефреша учётных данных. */
|
|
1477
|
+
refreshCredentialsByOuter;
|
|
1478
|
+
/** Полный путь до эндпоинта рефреша токена. */
|
|
1479
|
+
REFRESH_TOKEN_PATH;
|
|
1480
|
+
/** Коды ответа, по которым триггерится попытка рефреша. */
|
|
1481
|
+
CODES_USING_THE_REFRESH_ATTEMPT;
|
|
1482
|
+
/** Ключ флага «рефреш начат» в localStorage. */
|
|
1483
|
+
REFRESH_TOKEN_FLAG = "refreshTokenFlag";
|
|
1484
|
+
/** Интервал опроса флага обновления токена, мс. */
|
|
1485
|
+
INTERVAL_FOR_CHECKING_TOKEN_UPDATE = 50;
|
|
1486
|
+
/** Колбэк, вызываемый после неудачного рефреша. */
|
|
1487
|
+
callbackAfterRejectRefresh;
|
|
1488
|
+
/** Доступ к верхнему контексту RESTAPI (навешивается из конструктора RESTAPI). */
|
|
1489
|
+
getRESTAPIContext;
|
|
1146
1490
|
constructor(settings) {
|
|
1147
1491
|
const { credentialsProcessing } = settings;
|
|
1148
1492
|
const {
|
|
1149
|
-
|
|
1150
|
-
|
|
1493
|
+
// ? Для корректной работы установки токенов в запросы от API
|
|
1494
|
+
// ? нужно передавать методы getCredentials и getHeadersForAuthorize
|
|
1151
1495
|
getCredentials,
|
|
1152
1496
|
getHeadersForAuthorize,
|
|
1153
|
-
|
|
1154
|
-
|
|
1497
|
+
// ? На вход получит токен, должна вернуть объект для коструктора Headers
|
|
1498
|
+
// ? Так же опционально можно передавать
|
|
1155
1499
|
importCredentials,
|
|
1156
|
-
|
|
1500
|
+
// ? если он передается, то он будет применяться к выводу от вызова getCredentials
|
|
1157
1501
|
saveCredentials,
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1502
|
+
// ? можно будет найти в объекте credentialsProcessing, чтобы использовать кастомными методами
|
|
1503
|
+
// ! Либо вызов getCredentials, либо цепочка вызовов importCredentials(getCredentials())
|
|
1504
|
+
// ! должна возвращать требуемую классом структуру
|
|
1505
|
+
// ! Класс требует структуры
|
|
1506
|
+
// ! - { token }, если флоу рефреш токена НЕ используется
|
|
1507
|
+
// ! - { token, refreshToken, expires }, если используется флоу рефреш токена
|
|
1164
1508
|
// *********
|
|
1165
|
-
|
|
1509
|
+
// ? Подключает флоу рефреша токенов
|
|
1166
1510
|
isUseRefreshTokensPropcessing = false,
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1511
|
+
// ! Для корректной работы флоу рефреша токенов передача метода saveCredentials становится ОБЯЗАТЕЛЬНОЙ
|
|
1512
|
+
// ? Так же для корректной работы флоу рефреша токенов нужно
|
|
1513
|
+
// ? - либо
|
|
1170
1514
|
refreshCredentials,
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1515
|
+
// ! метод должен возвращать такую же структуру как и метод getCredentials,
|
|
1516
|
+
// ? на вход получит текущие креды, к выходу будет применен importCredentials, если этот метод передавался
|
|
1517
|
+
// ? - либо
|
|
1174
1518
|
REFRESH_TOKEN_PATH,
|
|
1175
|
-
|
|
1519
|
+
// ! REFRESH_TOKEN_PATH передается ПОЛНОСТЬЮ (глобальная адресация)!
|
|
1176
1520
|
CODES_USING_THE_REFRESH_ATTEMPT,
|
|
1177
|
-
|
|
1178
|
-
|
|
1521
|
+
// ? указывает по каким кодам ответа триггериться рефреш, массив чисел
|
|
1522
|
+
// ? Опционально
|
|
1179
1523
|
REFRESH_TOKEN_FLAG = "refreshTokenFlag",
|
|
1180
1524
|
INTERVAL_FOR_CHECKING_TOKEN_UPDATE = 50,
|
|
1181
1525
|
// ms
|
|
@@ -1183,40 +1527,44 @@ var CredentialsProcessing = class {
|
|
|
1183
1527
|
} = credentialsProcessing || {};
|
|
1184
1528
|
if (typeof getCredentials !== "function") throw new Error("No getCredentials method [CredentialsProcessing]");
|
|
1185
1529
|
if (typeof getHeadersForAuthorize !== "function") {
|
|
1186
|
-
throw new
|
|
1530
|
+
throw new TypeError("No getHeadersForAuthorize method [CredentialsProcessing]");
|
|
1187
1531
|
}
|
|
1188
1532
|
if (importCredentials && typeof importCredentials !== "function") {
|
|
1189
1533
|
throw new Error("Bad importCredentials method [CredentialsProcessing]");
|
|
1190
1534
|
}
|
|
1191
|
-
this.isUseRefreshTokensPropcessing = isUseRefreshTokensPropcessing;
|
|
1535
|
+
this.isUseRefreshTokensPropcessing = Boolean(isUseRefreshTokensPropcessing);
|
|
1192
1536
|
this.DEFAULT_CREDENTIALS = {};
|
|
1193
|
-
this.getCredentialsByOuter = getCredentials;
|
|
1194
|
-
this.getHeadersForAuthorize =
|
|
1195
|
-
|
|
1537
|
+
this.getCredentialsByOuter = () => getCredentials();
|
|
1538
|
+
this.getHeadersForAuthorize = (token) => {
|
|
1539
|
+
const result = getHeadersForAuthorize(token);
|
|
1540
|
+
return getIsOnlyAnObject(result) ? result : {};
|
|
1541
|
+
};
|
|
1542
|
+
if (typeof importCredentials === "function") this.importCredentials = (cred) => importCredentials(cred);
|
|
1196
1543
|
if (saveCredentials || isUseRefreshTokensPropcessing) {
|
|
1197
1544
|
if (typeof saveCredentials !== "function") throw new Error("No saveCredentials method [CredentialsProcessing]");
|
|
1198
|
-
else this.saveCredentials = saveCredentials;
|
|
1545
|
+
else this.saveCredentials = (cred) => saveCredentials(cred);
|
|
1199
1546
|
}
|
|
1200
1547
|
if (isUseRefreshTokensPropcessing) {
|
|
1201
|
-
if (typeof refreshCredentials === "function")
|
|
1202
|
-
|
|
1548
|
+
if (typeof refreshCredentials === "function") {
|
|
1549
|
+
this.refreshCredentialsByOuter = (cred) => refreshCredentials(cred);
|
|
1550
|
+
} else {
|
|
1203
1551
|
if (typeof this.refreshCredentialsByOuter === "function" && typeof REFRESH_TOKEN_PATH !== "string") {
|
|
1204
|
-
throw new
|
|
1552
|
+
throw new TypeError("No REFRESH_TOKEN_PATH [CredentialsProcessing]");
|
|
1205
1553
|
}
|
|
1206
|
-
this.REFRESH_TOKEN_PATH = REFRESH_TOKEN_PATH;
|
|
1554
|
+
this.REFRESH_TOKEN_PATH = typeof REFRESH_TOKEN_PATH === "string" ? REFRESH_TOKEN_PATH : void 0;
|
|
1207
1555
|
}
|
|
1208
1556
|
if (!Array.isArray(CODES_USING_THE_REFRESH_ATTEMPT)) {
|
|
1209
|
-
throw new
|
|
1557
|
+
throw new TypeError("No CODES_USING_THE_REFRESH_ATTEMPT [CredentialsProcessing]");
|
|
1210
1558
|
}
|
|
1211
1559
|
if (!CODES_USING_THE_REFRESH_ATTEMPT.every((i) => typeof i === "number")) {
|
|
1212
1560
|
throw new Error("Invalid format of CODES_USING_THE_REFRESH_ATTEMPT elements [CredentialsProcessing]");
|
|
1213
1561
|
}
|
|
1214
1562
|
if (typeof REFRESH_TOKEN_FLAG !== "string") throw new Error("Bad REFRESH_TOKEN_FLAG [CredentialsProcessing]");
|
|
1215
1563
|
if (typeof INTERVAL_FOR_CHECKING_TOKEN_UPDATE !== "number") {
|
|
1216
|
-
throw new
|
|
1564
|
+
throw new TypeError("Bad INTERVAL_FOR_CHECKING_TOKEN_UPDATE [CredentialsProcessing]");
|
|
1217
1565
|
}
|
|
1218
1566
|
if (typeof INTERVAL_FOR_CHECKING_TOKEN_UPDATE !== "number") {
|
|
1219
|
-
throw new
|
|
1567
|
+
throw new TypeError("Bad INTERVAL_FOR_CHECKING_TOKEN_UPDATE [CredentialsProcessing]");
|
|
1220
1568
|
}
|
|
1221
1569
|
if (callbackAfterRejectRefresh && typeof callbackAfterRejectRefresh !== "function") {
|
|
1222
1570
|
throw new Error("Bad INTERVAL_FOR_CHECKING_TOKEN_UPDATE [CredentialsProcessing]");
|
|
@@ -1224,23 +1572,33 @@ var CredentialsProcessing = class {
|
|
|
1224
1572
|
this.CODES_USING_THE_REFRESH_ATTEMPT = CODES_USING_THE_REFRESH_ATTEMPT;
|
|
1225
1573
|
this.REFRESH_TOKEN_FLAG = REFRESH_TOKEN_FLAG;
|
|
1226
1574
|
this.INTERVAL_FOR_CHECKING_TOKEN_UPDATE = INTERVAL_FOR_CHECKING_TOKEN_UPDATE;
|
|
1227
|
-
if (callbackAfterRejectRefresh) this.callbackAfterRejectRefresh = callbackAfterRejectRefresh;
|
|
1575
|
+
if (typeof callbackAfterRejectRefresh === "function") this.callbackAfterRejectRefresh = () => callbackAfterRejectRefresh();
|
|
1228
1576
|
}
|
|
1229
1577
|
}
|
|
1578
|
+
/** Возвращает значение флага «рефреш начат» из localStorage. */
|
|
1230
1579
|
getIsTokenStartRefresh() {
|
|
1231
1580
|
return localStorage.getItem(this.REFRESH_TOKEN_FLAG);
|
|
1232
1581
|
}
|
|
1582
|
+
/** Выставляет флаг «рефреш начат» в localStorage. */
|
|
1233
1583
|
setIsTokenStartRefresh() {
|
|
1234
|
-
localStorage.setItem(this.REFRESH_TOKEN_FLAG, true);
|
|
1584
|
+
localStorage.setItem(this.REFRESH_TOKEN_FLAG, String(true));
|
|
1235
1585
|
}
|
|
1586
|
+
/** Снимает флаг «рефреш начат» из localStorage. */
|
|
1236
1587
|
removeIsTokenStartRefresh() {
|
|
1237
1588
|
localStorage.removeItem(this.REFRESH_TOKEN_FLAG);
|
|
1238
1589
|
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Возвращает текущие учётные данные (с учётом importCredentials и проверки истечения JWT).
|
|
1592
|
+
*
|
|
1593
|
+
* @param isCatchCallbackProcess - флаг процесса обработки ошибки (отключает проверку истечения).
|
|
1594
|
+
* @param callback - необязательный колбэк, получающий итоговые учётные данные.
|
|
1595
|
+
* @returns Промис с учётными данными.
|
|
1596
|
+
*/
|
|
1239
1597
|
async getCredentials(isCatchCallbackProcess, callback) {
|
|
1240
1598
|
let credentials = await this.getCredentialsByOuter();
|
|
1241
1599
|
if (typeof this.importCredentials === "function") credentials = await this.importCredentials(credentials);
|
|
1242
|
-
|
|
1243
|
-
const token =
|
|
1600
|
+
const credObj = getIsOnlyAnObject(credentials) ? credentials : {};
|
|
1601
|
+
const token = credObj.token || null;
|
|
1244
1602
|
if (token && !isCatchCallbackProcess) {
|
|
1245
1603
|
let decoded;
|
|
1246
1604
|
try {
|
|
@@ -1249,11 +1607,18 @@ var CredentialsProcessing = class {
|
|
|
1249
1607
|
decoded = null;
|
|
1250
1608
|
}
|
|
1251
1609
|
const currentTime = Date.now() / 1e3;
|
|
1252
|
-
if (decoded !== null && Number(decoded.exp) < currentTime)
|
|
1610
|
+
if (decoded !== null && Number(decoded.exp) < currentTime) credObj.isNeedRefresh = true;
|
|
1253
1611
|
}
|
|
1254
|
-
if (callback) await callback({ ...
|
|
1255
|
-
return { ...
|
|
1612
|
+
if (callback) await callback({ ...credObj, isCatchCallbackProcess });
|
|
1613
|
+
return { ...credObj, isCatchCallbackProcess };
|
|
1256
1614
|
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Выполняет рефреш учётных данных (внешним методом либо дефолтным OAuth-флоу).
|
|
1617
|
+
*
|
|
1618
|
+
* @param currentCredentials - текущие учётные данные.
|
|
1619
|
+
* @param callback - необязательный колбэк сохранения новых учётных данных.
|
|
1620
|
+
* @returns Промис с новыми учётными данными.
|
|
1621
|
+
*/
|
|
1257
1622
|
async refreshCredentials(currentCredentials, callback) {
|
|
1258
1623
|
const CONTEXT = this;
|
|
1259
1624
|
async function finalize(cred) {
|
|
@@ -1266,11 +1631,10 @@ var CredentialsProcessing = class {
|
|
|
1266
1631
|
if (!currentCredentials?.token || !currentCredentials?.refreshToken) return finalize(CONTEXT.DEFAULT_CREDENTIALS);
|
|
1267
1632
|
async function refreshCredentialsByDefault() {
|
|
1268
1633
|
const { token, refreshToken } = currentCredentials;
|
|
1269
|
-
const headers = new Headers();
|
|
1270
|
-
headers.append(...Object.entries(CONTEXT.getHeadersForAuthorize(token)));
|
|
1634
|
+
const headers = new Headers(Object.entries(CONTEXT.getHeadersForAuthorize(token || "")).map(([key, value]) => [key, `${value}`]));
|
|
1271
1635
|
const body = new FormData();
|
|
1272
1636
|
body.append("grant_type", "refresh_token");
|
|
1273
|
-
body.append("refresh_token", refreshToken);
|
|
1637
|
+
body.append("refresh_token", refreshToken || "");
|
|
1274
1638
|
body.append("client_id", "oauth");
|
|
1275
1639
|
body.append("client_secret", "secret");
|
|
1276
1640
|
body.append("access_type", "offline");
|
|
@@ -1283,8 +1647,14 @@ var CredentialsProcessing = class {
|
|
|
1283
1647
|
});
|
|
1284
1648
|
}
|
|
1285
1649
|
const newCredentials = typeof CONTEXT.refreshCredentialsByOuter === "function" ? await CONTEXT.refreshCredentialsByOuter(currentCredentials) : await refreshCredentialsByDefault();
|
|
1286
|
-
return finalize(newCredentials);
|
|
1650
|
+
return finalize(getIsOnlyAnObject(newCredentials) ? newCredentials : CONTEXT.DEFAULT_CREDENTIALS);
|
|
1287
1651
|
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Ожидает завершения рефреша токена (или запускает его флаг при необходимости).
|
|
1654
|
+
*
|
|
1655
|
+
* @param isNeedRefresh - выставить ли флаг «рефреш начат» при отсутствии активного рефреша.
|
|
1656
|
+
* @returns Промис, который резолвится по завершении ожидания.
|
|
1657
|
+
*/
|
|
1288
1658
|
async waitRefresh(isNeedRefresh) {
|
|
1289
1659
|
return new Promise((resolve) => {
|
|
1290
1660
|
if (!this.getIsTokenStartRefresh()) {
|
|
@@ -1300,10 +1670,16 @@ var CredentialsProcessing = class {
|
|
|
1300
1670
|
}
|
|
1301
1671
|
});
|
|
1302
1672
|
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Обрабатывает учётные данные: при необходимости рефрешит токен и возвращает актуальные данные.
|
|
1675
|
+
*
|
|
1676
|
+
* @param cred - текущие учётные данные.
|
|
1677
|
+
* @returns Промис с актуальными учётными данными (с флагом isRefreshFailed при неудаче).
|
|
1678
|
+
*/
|
|
1303
1679
|
async processCredentials(cred) {
|
|
1304
1680
|
if (!cred.isNeedRefresh && !cred.isCatchCallbackProcess) return cred;
|
|
1305
1681
|
const CONTEXT = this;
|
|
1306
|
-
return CONTEXT.refreshCredentials(cred, (response) => CONTEXT.saveCredentials(response)).then(() => {
|
|
1682
|
+
return CONTEXT.refreshCredentials(cred, (response) => CONTEXT.saveCredentials?.(response)).then(() => {
|
|
1307
1683
|
return new Promise((resolve) => {
|
|
1308
1684
|
setTimeout(async () => {
|
|
1309
1685
|
const newCredentials = await CONTEXT.getCredentials();
|
|
@@ -1321,11 +1697,15 @@ var CredentialsProcessing = class {
|
|
|
1321
1697
|
};
|
|
1322
1698
|
var CredentialsProcessing_default = CredentialsProcessing;
|
|
1323
1699
|
|
|
1324
|
-
// src/Classes/RESTAPI/index.
|
|
1700
|
+
// src/Classes/RESTAPI/index.ts
|
|
1325
1701
|
function returnTheContext() {
|
|
1326
1702
|
return this;
|
|
1327
1703
|
}
|
|
1328
1704
|
var RESTAPI = class extends ApiBase_default {
|
|
1705
|
+
/** Функция показа сообщений пользователю (или null, если информирование отключено). */
|
|
1706
|
+
sendMessage = null;
|
|
1707
|
+
/** Обработчик учётных данных (создаётся при наличии настройки credentialsProcessing). */
|
|
1708
|
+
credentialsProcessing;
|
|
1329
1709
|
constructor(settings) {
|
|
1330
1710
|
super(settings);
|
|
1331
1711
|
const { sendMessage, credentialsProcessing } = settings || {};
|
|
@@ -1335,7 +1715,14 @@ var RESTAPI = class extends ApiBase_default {
|
|
|
1335
1715
|
this.credentialsProcessing.getRESTAPIContext = returnTheContext.bind(this);
|
|
1336
1716
|
}
|
|
1337
1717
|
}
|
|
1338
|
-
|
|
1718
|
+
/**
|
|
1719
|
+
* Основной (базовый) метод API: выполняет мультизапрос (или одиночный запрос).
|
|
1720
|
+
*
|
|
1721
|
+
* @param inputRequests - путь, объект описания запроса или их массив.
|
|
1722
|
+
* @param settings - общие настройки запроса (формат ответа, тосты, сообщения и т.п.).
|
|
1723
|
+
* @returns Промис с результатом (форма зависит от isResponseAsObject).
|
|
1724
|
+
*/
|
|
1725
|
+
// ! Основной (базовый) метод API, мультизапросы
|
|
1339
1726
|
async doRequest(inputRequests, settings) {
|
|
1340
1727
|
if (typeof inputRequests !== "string" && typeof inputRequests !== "object") {
|
|
1341
1728
|
return Promise.reject(new Error(this.BAD_REQUEST_DATA_TEXT));
|
|
@@ -1359,24 +1746,29 @@ var RESTAPI = class extends ApiBase_default {
|
|
|
1359
1746
|
throw new Error("It is impossible to make a request!");
|
|
1360
1747
|
}
|
|
1361
1748
|
instance.setIsResponseAsObject(isResponseAsObject ?? true);
|
|
1362
|
-
if (callback) instance.setCallback(callback);
|
|
1749
|
+
if (typeof callback === "function") instance.setCallback(callback);
|
|
1363
1750
|
function getRequests() {
|
|
1364
1751
|
let output;
|
|
1365
1752
|
if (typeof inputRequests === "string") output = [{ path: inputRequests }];
|
|
1366
1753
|
else output = Array.isArray(inputRequests) ? inputRequests : [inputRequests];
|
|
1367
1754
|
function getCallback(item, mesageOptions) {
|
|
1368
|
-
const
|
|
1755
|
+
const getBodyAs = typeof item.getBodyAs === "string" ? item.getBodyAs : void 0;
|
|
1369
1756
|
const finalIsGetBody = typeof item.isGetBody === "boolean" || typeof item.isGetBody === "string" ? item.isGetBody : isGetBody;
|
|
1370
1757
|
const finalIsGetBodyFirst = finalIsGetBody === "first";
|
|
1371
|
-
const checkResponseCallback = finalIsGetBodyFirst ? async (res,
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1758
|
+
const checkResponseCallback = finalIsGetBodyFirst ? async (res, options) => API_CONTEXT.getResponseBody(await API_CONTEXT.checkResponseCode(res, options), getBodyAs) : async (res, options) => API_CONTEXT.checkResponseCode(res, options);
|
|
1759
|
+
const itemCallback = item.callback;
|
|
1760
|
+
const callbackFn = typeof itemCallback === "function" ? async (res) => itemCallback(await checkResponseCallback(res, mesageOptions)) : async (res) => checkResponseCallback(res, mesageOptions);
|
|
1761
|
+
return finalIsGetBody && !finalIsGetBodyFirst ? async (res) => API_CONTEXT.getResponseBody(await callbackFn(res), getBodyAs) : callbackFn;
|
|
1374
1762
|
}
|
|
1375
1763
|
async function getRequestItem(item, idx) {
|
|
1376
|
-
const
|
|
1764
|
+
const itemObj = getIsOnlyAnObject(item) ? item : { path: item };
|
|
1765
|
+
const { type, ...partialRestFirst } = itemObj;
|
|
1377
1766
|
const itemAfterRequestCreating = await (async () => {
|
|
1378
|
-
const key = `createRequest_${type
|
|
1379
|
-
|
|
1767
|
+
const key = `createRequest_${typeof type === "string" ? type : "default"}`;
|
|
1768
|
+
const creator = key in API_CONTEXT ? API_CONTEXT[key] : void 0;
|
|
1769
|
+
const result = typeof creator === "function" ? creator.call(API_CONTEXT, partialRestFirst, "doRequestMapping") : API_CONTEXT.createRequest_default(partialRestFirst, "doRequestMapping");
|
|
1770
|
+
const awaited = await result;
|
|
1771
|
+
return getIsOnlyAnObject(awaited) ? awaited : {};
|
|
1380
1772
|
})();
|
|
1381
1773
|
const { fullPath, api, path, query, queryParameters, ...partialRestSecond } = itemAfterRequestCreating;
|
|
1382
1774
|
const {
|
|
@@ -1392,23 +1784,25 @@ var RESTAPI = class extends ApiBase_default {
|
|
|
1392
1784
|
isNoToast,
|
|
1393
1785
|
...rest
|
|
1394
1786
|
} = partialRestSecond;
|
|
1787
|
+
const successMessagesArr = Array.isArray(successMessages) ? successMessages : void 0;
|
|
1788
|
+
const errorMessagesArr = Array.isArray(errorMessages) ? errorMessages : void 0;
|
|
1395
1789
|
const useSuccessMessageFlag = Boolean(
|
|
1396
|
-
successMess || commonSuccessMessage ||
|
|
1790
|
+
successMess || commonSuccessMessage || successMessagesArr?.[idx] || CSMfromSettings || isToastResult || isToastFromSettings
|
|
1397
1791
|
);
|
|
1398
1792
|
const isUseSuccessToast = useSuccessMessageFlag && !isNoToast && !isNoToastFromSettings && !isNoToastSuccess && !isNTSFromSettings;
|
|
1399
1793
|
const isUseErrorToast = !isNoToast && !isNoToastFromSettings && !isNoToastError && !isNTEFromSettings;
|
|
1400
1794
|
const mesageOptions = {
|
|
1401
|
-
successMess: successMess || commonSuccessMessage ||
|
|
1795
|
+
successMess: successMess || commonSuccessMessage || successMessagesArr?.[idx] || CSMfromSettings,
|
|
1402
1796
|
successMessageType: successMessageType || SMTfromSettings,
|
|
1403
|
-
errorMess: errorMess || commonErrorMessage ||
|
|
1797
|
+
errorMess: errorMess || commonErrorMessage || errorMessagesArr?.[idx] || CEMfromSettings,
|
|
1404
1798
|
isUseSuccessToast,
|
|
1405
1799
|
isUseErrorToast,
|
|
1406
1800
|
toastOptions: toastOptions || TOFomSettings
|
|
1407
1801
|
};
|
|
1408
1802
|
return {
|
|
1409
1803
|
...rest,
|
|
1410
|
-
path: `${fullPath || await API_CONTEXT.getRootPath(
|
|
1411
|
-
|
|
1804
|
+
path: `${fullPath || await API_CONTEXT.getRootPath() + (typeof path === "string" ? path : "")}`,
|
|
1805
|
+
// ? Запускаем самовызывающуюся асинхронную ф-ю, ждем резолва промиса от нее
|
|
1412
1806
|
queryParameters: await (async () => {
|
|
1413
1807
|
const queryParams = query || queryParameters || "";
|
|
1414
1808
|
const preparedQueryParams = await API_CONTEXT.processQueryParams(queryParams);
|
|
@@ -1418,16 +1812,25 @@ var RESTAPI = class extends ApiBase_default {
|
|
|
1418
1812
|
callback: getCallback(itemAfterRequestCreating, mesageOptions)
|
|
1419
1813
|
};
|
|
1420
1814
|
}
|
|
1421
|
-
return output
|
|
1815
|
+
return output.map((item, idx) => getRequestItem(item, idx));
|
|
1422
1816
|
}
|
|
1423
1817
|
return instance.request(await Promise.all(getRequests()));
|
|
1424
1818
|
}
|
|
1425
|
-
|
|
1819
|
+
/**
|
|
1820
|
+
* Метод API для одиночных запросов: применяет крейтер по `type` и делегирует в {@link RESTAPI.doRequest}.
|
|
1821
|
+
*
|
|
1822
|
+
* @param requestSettings - объект описания одиночного запроса (с необязательным `type`).
|
|
1823
|
+
* @returns Промис с результатом запроса.
|
|
1824
|
+
*/
|
|
1825
|
+
// ! Метод API для одиночных запросов
|
|
1426
1826
|
async doMonoRequest(requestSettings) {
|
|
1427
1827
|
if (!getIsOnlyAnObject(requestSettings)) throw new Error(`${this.BAD_REQUEST_DATA_TEXT} [RESTAPI.doMonoRequest]`);
|
|
1428
1828
|
const { type, ...requestSettingsRest } = requestSettings;
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1829
|
+
const key = `createRequest_${typeof type === "string" ? type : ""}`;
|
|
1830
|
+
const creator = type && key in this ? this[key] : void 0;
|
|
1831
|
+
const created = typeof creator === "function" ? await creator.call(this, requestSettingsRest) : await this.createRequest_default(requestSettingsRest);
|
|
1832
|
+
const doRequestArgs = Array.isArray(created) ? created : [created];
|
|
1833
|
+
return this.doRequest(doRequestArgs[0], doRequestArgs[1]);
|
|
1431
1834
|
}
|
|
1432
1835
|
};
|
|
1433
1836
|
var RESTAPI_default = RESTAPI;
|
|
@@ -23053,17 +23456,6 @@ export {
|
|
|
23053
23456
|
uuid,
|
|
23054
23457
|
voidFn
|
|
23055
23458
|
};
|
|
23056
|
-
//! При получении экземпляра, если sendMessage не является ф-ей,
|
|
23057
|
-
//! то это воспринимается как отключение шага информирования об ошибке запроса как такового
|
|
23058
|
-
//! Планируемая структура { headers, queryParameters, options }, где
|
|
23059
|
-
//! - headers и options - объекты,
|
|
23060
|
-
//! - queryParameters - строка, массив или объект
|
|
23061
|
-
//! ВНИМАНИЕ! Данный механизм рефреша принят на проекте finturfreactfrontend (админка)
|
|
23062
|
-
//! На кабинете он ТОЧНО НЕ ТАКОЙ
|
|
23063
|
-
//! currentCredentials передаются в формате класса! ({ token, refreshToken, expires })
|
|
23064
|
-
//! Наличие credentialsProcessing подключает ДОБАВЛЕНИЕ ТОКЕНОВ В ЗАПРОСЫ,
|
|
23065
|
-
//! НО по умолчанию НЕ добавляет флоу рефреша токенов
|
|
23066
|
-
//! (флоу рефреша подключается пропой isUseRefreshTokensPropcessing из credentialsProcessing)
|
|
23067
23459
|
//! Важно! Если придет велью с количеством дробных разрядов БОЛЬШИМ
|
|
23068
23460
|
//! чем указано в quantity - округлит по правилам округления
|
|
23069
23461
|
//! чем указано в decimalPlaces - округлит по правилам округления
|