@rogatio/cli 5.8.2 → 5.9.0
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/README.md +2 -2
- package/dist/editor/index.js +681 -739
- package/dist/node/index.js +229 -264
- package/package.json +1 -1
package/dist/editor/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var LIMITS = Object.freeze({
|
|
|
27
27
|
maxMockDelayMs: 3e4,
|
|
28
28
|
maxMockFilePathLength: 2048,
|
|
29
29
|
maxResponseBodyReplacements: 64,
|
|
30
|
+
maxResponseBodyBytes: 4 * 1024 * 1024,
|
|
30
31
|
maxResponseBodyPatternLength: 2048,
|
|
31
32
|
maxResponseBodyReplacementLength: 4096,
|
|
32
33
|
maxRequestBodyBytes: 4 * 1024 * 1024,
|
|
@@ -213,6 +214,21 @@ var HTTP_METHODS = Object.freeze([
|
|
|
213
214
|
"CONNECT",
|
|
214
215
|
"TRACE"
|
|
215
216
|
]);
|
|
217
|
+
function hasLoneSurrogate(value) {
|
|
218
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
219
|
+
const code = value.charCodeAt(i);
|
|
220
|
+
if (code >= 55296 && code <= 56319) {
|
|
221
|
+
if (i + 1 >= value.length) return true;
|
|
222
|
+
const next = value.charCodeAt(i + 1);
|
|
223
|
+
if (next < 56320 || next > 57343) return true;
|
|
224
|
+
} else if (code >= 56320 && code <= 57343) {
|
|
225
|
+
if (i === 0) return true;
|
|
226
|
+
const prev = value.charCodeAt(i - 1);
|
|
227
|
+
if (prev < 55296 || prev > 56319) return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
216
232
|
|
|
217
233
|
// packages/editor/src/ai-assist-panel.ts
|
|
218
234
|
function createAIAssistPanel(_container, _editor, onApply, onClose) {
|
|
@@ -561,307 +577,261 @@ function createAIAssistPanel(_container, _editor, onApply, onClose) {
|
|
|
561
577
|
};
|
|
562
578
|
}
|
|
563
579
|
|
|
564
|
-
// packages/editor/src/rule-types/
|
|
565
|
-
function
|
|
566
|
-
|
|
580
|
+
// packages/editor/src/rule-types/dom.ts
|
|
581
|
+
function normalizeOption(option) {
|
|
582
|
+
if (typeof option === "string") {
|
|
583
|
+
return { value: option, label: option };
|
|
584
|
+
}
|
|
585
|
+
return option;
|
|
567
586
|
}
|
|
568
|
-
function
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
587
|
+
function createSelect(document2, options, value, onChange) {
|
|
588
|
+
const select = document2.createElement("select");
|
|
589
|
+
for (const option of options) {
|
|
590
|
+
const { value: optionValue, label } = normalizeOption(option);
|
|
591
|
+
const opt = document2.createElement("option");
|
|
592
|
+
opt.value = optionValue;
|
|
593
|
+
opt.textContent = label;
|
|
594
|
+
if (optionValue === value) opt.selected = true;
|
|
595
|
+
select.append(opt);
|
|
573
596
|
}
|
|
574
|
-
|
|
597
|
+
select.addEventListener("change", () => onChange(select.value));
|
|
598
|
+
return select;
|
|
575
599
|
}
|
|
576
|
-
function
|
|
577
|
-
|
|
578
|
-
|
|
600
|
+
function createTextInput(document2, value, onChange, maxLength) {
|
|
601
|
+
const input = document2.createElement("input");
|
|
602
|
+
input.type = "text";
|
|
603
|
+
input.value = value;
|
|
604
|
+
if (maxLength !== void 0) input.maxLength = maxLength;
|
|
605
|
+
input.addEventListener("input", () => onChange(input.value));
|
|
606
|
+
return input;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// packages/editor/src/rule-types/header.ts
|
|
610
|
+
var FORBIDDEN_REQUEST_HEADERS2 = Object.freeze([
|
|
611
|
+
"accept-charset",
|
|
612
|
+
"accept-encoding",
|
|
613
|
+
"access-control-request-headers",
|
|
614
|
+
"access-control-request-method",
|
|
615
|
+
"connection",
|
|
616
|
+
"content-length",
|
|
617
|
+
"cookie",
|
|
618
|
+
"cookie2",
|
|
619
|
+
"date",
|
|
620
|
+
"dnt",
|
|
621
|
+
"expect",
|
|
622
|
+
"host",
|
|
623
|
+
"keep-alive",
|
|
624
|
+
"origin",
|
|
625
|
+
"proxy-authenticate",
|
|
626
|
+
"proxy-authorization",
|
|
627
|
+
"te",
|
|
628
|
+
"trailer",
|
|
629
|
+
"transfer-encoding",
|
|
630
|
+
"upgrade",
|
|
631
|
+
"via"
|
|
632
|
+
]);
|
|
633
|
+
var FORBIDDEN_RESPONSE_HEADERS2 = Object.freeze([
|
|
634
|
+
"connection",
|
|
635
|
+
"content-encoding",
|
|
636
|
+
"content-length",
|
|
637
|
+
"date",
|
|
638
|
+
"keep-alive",
|
|
639
|
+
"proxy-authenticate",
|
|
640
|
+
"proxy-authorization",
|
|
641
|
+
"set-cookie",
|
|
642
|
+
"set-cookie2",
|
|
643
|
+
"te",
|
|
644
|
+
"trailer",
|
|
645
|
+
"transfer-encoding",
|
|
646
|
+
"upgrade",
|
|
647
|
+
"via"
|
|
648
|
+
]);
|
|
649
|
+
var FORBIDDEN_REQUEST_PREFIXES2 = Object.freeze(["proxy-", "sec-"]);
|
|
650
|
+
function isForbiddenHeader(name, direction) {
|
|
651
|
+
const normalized = name.toLowerCase();
|
|
652
|
+
const forbidden = direction === "request" ? FORBIDDEN_REQUEST_HEADERS2 : FORBIDDEN_RESPONSE_HEADERS2;
|
|
653
|
+
return forbidden.includes(normalized) || direction === "request" && FORBIDDEN_REQUEST_PREFIXES2.some(
|
|
654
|
+
(prefix) => normalized.startsWith(prefix)
|
|
579
655
|
);
|
|
580
656
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
657
|
+
var HEADER_DIRECTIONS = [
|
|
658
|
+
"request",
|
|
659
|
+
"response"
|
|
660
|
+
];
|
|
661
|
+
var HEADER_OPERATIONS = [
|
|
662
|
+
"set",
|
|
663
|
+
"append",
|
|
664
|
+
"remove"
|
|
665
|
+
];
|
|
666
|
+
var HEADER_EDITOR_OPERATIONS = Object.freeze(["set", "remove"]);
|
|
667
|
+
function operationOptions(current) {
|
|
668
|
+
if (current === "append") {
|
|
669
|
+
return ["set", "append", "remove"];
|
|
585
670
|
}
|
|
586
|
-
return
|
|
671
|
+
return HEADER_EDITOR_OPERATIONS;
|
|
587
672
|
}
|
|
588
|
-
function
|
|
673
|
+
function createHeaderRuleType() {
|
|
589
674
|
return {
|
|
590
|
-
id: "
|
|
591
|
-
label: "
|
|
592
|
-
actionField: "mock",
|
|
675
|
+
id: "header",
|
|
676
|
+
label: "Header",
|
|
593
677
|
matches(rule) {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
return
|
|
678
|
+
return rule.type === "header";
|
|
679
|
+
},
|
|
680
|
+
defaultFields() {
|
|
681
|
+
return {
|
|
682
|
+
headerDirection: "request",
|
|
683
|
+
headerOperation: "set",
|
|
684
|
+
headerName: "",
|
|
685
|
+
headerValue: ""
|
|
686
|
+
};
|
|
687
|
+
},
|
|
688
|
+
mount(context) {
|
|
689
|
+
const document2 = context.document;
|
|
690
|
+
const fieldset = document2.createElement("fieldset");
|
|
691
|
+
const legend = document2.createElement("legend");
|
|
692
|
+
legend.textContent = "Header rule";
|
|
693
|
+
fieldset.append(legend);
|
|
694
|
+
const directionLabel = document2.createElement("label");
|
|
695
|
+
directionLabel.textContent = "Direction";
|
|
696
|
+
const directionValue = context.getField("headerDirection") ?? "request";
|
|
697
|
+
const directionSelect = createSelect(
|
|
698
|
+
document2,
|
|
699
|
+
HEADER_DIRECTIONS,
|
|
700
|
+
directionValue,
|
|
701
|
+
(value) => {
|
|
702
|
+
context.setField("headerDirection", value);
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
context.registerControl("/headerDirection", directionSelect);
|
|
706
|
+
directionLabel.append(directionSelect);
|
|
707
|
+
fieldset.append(directionLabel);
|
|
708
|
+
const operationLabel = document2.createElement("label");
|
|
709
|
+
operationLabel.textContent = "Operation";
|
|
710
|
+
let operationValue = context.getField("headerOperation") ?? "set";
|
|
711
|
+
const valueLabel = document2.createElement("label");
|
|
712
|
+
valueLabel.textContent = "Header value";
|
|
713
|
+
const syncValueVisibility = () => {
|
|
714
|
+
const showValue = operationValue === "set" || operationValue === "append";
|
|
715
|
+
valueLabel.hidden = !showValue;
|
|
716
|
+
};
|
|
717
|
+
const operationSelect = createSelect(
|
|
718
|
+
document2,
|
|
719
|
+
operationOptions(operationValue),
|
|
720
|
+
operationValue,
|
|
721
|
+
(value) => {
|
|
722
|
+
operationValue = value;
|
|
723
|
+
context.setField("headerOperation", value);
|
|
724
|
+
if (value === "remove") {
|
|
725
|
+
context.deleteField("headerValue");
|
|
726
|
+
}
|
|
727
|
+
syncValueVisibility();
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
context.registerControl("/headerOperation", operationSelect);
|
|
731
|
+
operationLabel.append(operationSelect);
|
|
732
|
+
fieldset.append(operationLabel);
|
|
733
|
+
const nameLabel = document2.createElement("label");
|
|
734
|
+
nameLabel.textContent = "Header name";
|
|
735
|
+
const nameValue = context.getField("headerName") ?? "";
|
|
736
|
+
const nameInput = createTextInput(
|
|
737
|
+
document2,
|
|
738
|
+
nameValue,
|
|
739
|
+
(value) => {
|
|
740
|
+
context.setField("headerName", value);
|
|
741
|
+
},
|
|
742
|
+
256
|
|
743
|
+
);
|
|
744
|
+
context.registerControl("/headerName", nameInput);
|
|
745
|
+
nameLabel.append(nameInput);
|
|
746
|
+
fieldset.append(nameLabel);
|
|
747
|
+
const valueValue = context.getField("headerValue") ?? "";
|
|
748
|
+
const valueInput = createTextInput(
|
|
749
|
+
document2,
|
|
750
|
+
valueValue,
|
|
751
|
+
(value) => {
|
|
752
|
+
context.setField("headerValue", value);
|
|
753
|
+
},
|
|
754
|
+
4096
|
|
755
|
+
);
|
|
756
|
+
context.registerControl("/headerValue", valueInput);
|
|
757
|
+
valueLabel.append(valueInput);
|
|
758
|
+
fieldset.append(valueLabel);
|
|
759
|
+
syncValueVisibility();
|
|
760
|
+
context.container.append(fieldset);
|
|
761
|
+
return {
|
|
762
|
+
destroy() {
|
|
763
|
+
directionSelect.removeEventListener("change", () => {
|
|
764
|
+
});
|
|
765
|
+
operationSelect.removeEventListener("change", () => {
|
|
766
|
+
});
|
|
767
|
+
nameInput.removeEventListener("input", () => {
|
|
768
|
+
});
|
|
769
|
+
valueInput.removeEventListener("input", () => {
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
};
|
|
598
773
|
},
|
|
599
774
|
validate(rule, rulePath) {
|
|
600
|
-
const mock = asMockAction(rule.mock);
|
|
601
|
-
if (mock === void 0) return [];
|
|
602
775
|
const diagnostics = [];
|
|
603
|
-
const
|
|
604
|
-
|
|
776
|
+
const direction = rule.headerDirection;
|
|
777
|
+
const operation = rule.headerOperation;
|
|
778
|
+
const headerName = rule.headerName;
|
|
779
|
+
const headerValue = rule.headerValue;
|
|
780
|
+
if (!direction || !HEADER_DIRECTIONS.includes(direction)) {
|
|
605
781
|
diagnostics.push({
|
|
606
|
-
code: "
|
|
782
|
+
code: "schema.invalid-value",
|
|
607
783
|
severity: "error",
|
|
608
|
-
path: `${rulePath}/
|
|
609
|
-
message:
|
|
784
|
+
path: `${rulePath}/headerDirection`,
|
|
785
|
+
message: 'Direction must be "request" or "response".'
|
|
610
786
|
});
|
|
611
787
|
}
|
|
612
|
-
|
|
613
|
-
const hasFile = typeof mock.file === "string";
|
|
614
|
-
if (hasBody === hasFile) {
|
|
788
|
+
if (!operation || !HEADER_OPERATIONS.includes(operation)) {
|
|
615
789
|
diagnostics.push({
|
|
616
|
-
code: "
|
|
790
|
+
code: "schema.invalid-value",
|
|
617
791
|
severity: "error",
|
|
618
|
-
path: `${rulePath}/
|
|
619
|
-
message:
|
|
792
|
+
path: `${rulePath}/headerOperation`,
|
|
793
|
+
message: 'Operation must be "set", "append", or "remove".'
|
|
620
794
|
});
|
|
621
|
-
} else if (hasBody) {
|
|
622
|
-
const body = mock.body;
|
|
623
|
-
if (body.length > LIMITS.maxMockInlineBodyLength) {
|
|
624
|
-
diagnostics.push({
|
|
625
|
-
code: "editor.mock-body-too-long",
|
|
626
|
-
severity: "error",
|
|
627
|
-
path: `${rulePath}/mock/body`,
|
|
628
|
-
message: `Mock inline body must be at most ${LIMITS.maxMockInlineBodyLength} characters.`
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
} else if (hasFile) {
|
|
632
|
-
const file = mock.file;
|
|
633
|
-
if (file.length === 0 || file.length > LIMITS.maxMockFilePathLength) {
|
|
634
|
-
diagnostics.push({
|
|
635
|
-
code: "editor.mock-file-path",
|
|
636
|
-
severity: "error",
|
|
637
|
-
path: `${rulePath}/mock/file`,
|
|
638
|
-
message: `Mock file path must be 1-${LIMITS.maxMockFilePathLength} characters.`
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
795
|
}
|
|
642
|
-
if (
|
|
796
|
+
if (typeof headerName !== "string" || headerName.length === 0 || headerName.length > 256) {
|
|
643
797
|
diagnostics.push({
|
|
644
|
-
code: "
|
|
798
|
+
code: "schema.out-of-range",
|
|
645
799
|
severity: "error",
|
|
646
|
-
path: `${rulePath}/
|
|
647
|
-
message:
|
|
800
|
+
path: `${rulePath}/headerName`,
|
|
801
|
+
message: "Header name must be 1-256 characters."
|
|
648
802
|
});
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
path: `${rulePath}/mock/headers`,
|
|
656
|
-
message: `A mock rule may define at most ${LIMITS.maxMockHeadersPerRule} headers.`
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
mock.headers.forEach((header, index) => {
|
|
660
|
-
const namePath = `${rulePath}/mock/headers/${index}/name`;
|
|
661
|
-
const valuePath = `${rulePath}/mock/headers/${index}/value`;
|
|
662
|
-
if (typeof header?.name !== "string" || header.name.length === 0 || header.name.length > LIMITS.maxMockHeaderNameLength) {
|
|
663
|
-
diagnostics.push({
|
|
664
|
-
code: "editor.mock-header-name",
|
|
665
|
-
severity: "error",
|
|
666
|
-
path: namePath,
|
|
667
|
-
message: `Mock header name must be 1-${LIMITS.maxMockHeaderNameLength} characters.`
|
|
668
|
-
});
|
|
669
|
-
} else if (hasControlOrColon(header.name)) {
|
|
670
|
-
diagnostics.push({
|
|
671
|
-
code: "editor.mock-header-name",
|
|
672
|
-
severity: "error",
|
|
673
|
-
path: namePath,
|
|
674
|
-
message: "Mock header name must not contain control characters or a colon."
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
if (typeof header?.value !== "string" || header.value.length > LIMITS.maxMockHeaderValueLength) {
|
|
678
|
-
diagnostics.push({
|
|
679
|
-
code: "editor.mock-header-value",
|
|
680
|
-
severity: "error",
|
|
681
|
-
path: valuePath,
|
|
682
|
-
message: `Mock header value must be at most ${LIMITS.maxMockHeaderValueLength} characters.`
|
|
683
|
-
});
|
|
684
|
-
}
|
|
803
|
+
} else if (direction && isForbiddenHeader(headerName, direction)) {
|
|
804
|
+
diagnostics.push({
|
|
805
|
+
code: "extension.forbidden-header",
|
|
806
|
+
severity: "error",
|
|
807
|
+
path: `${rulePath}/headerName`,
|
|
808
|
+
message: `Header "${headerName}" is forbidden for ${direction} headers.`
|
|
685
809
|
});
|
|
686
810
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
context.setField("mock", next);
|
|
694
|
-
};
|
|
695
|
-
const render = () => {
|
|
696
|
-
const current = getCurrent();
|
|
697
|
-
container.replaceChildren();
|
|
698
|
-
if (current === void 0) return;
|
|
699
|
-
const statusLabel = document2.createElement("label");
|
|
700
|
-
statusLabel.textContent = "Status";
|
|
701
|
-
const statusInput = document2.createElement("input");
|
|
702
|
-
statusInput.type = "number";
|
|
703
|
-
statusInput.min = String(LIMITS.minMockStatus);
|
|
704
|
-
statusInput.max = String(LIMITS.maxMockStatus);
|
|
705
|
-
statusInput.step = "1";
|
|
706
|
-
statusInput.value = String(current.status);
|
|
707
|
-
statusInput.dataset.editorField = "true";
|
|
708
|
-
statusInput.addEventListener("input", () => {
|
|
709
|
-
const c = getCurrent();
|
|
710
|
-
if (c === void 0) return;
|
|
711
|
-
setAction({ ...c, status: Number(statusInput.value) });
|
|
811
|
+
if ((operation === "set" || operation === "append") && typeof headerValue !== "string") {
|
|
812
|
+
diagnostics.push({
|
|
813
|
+
code: "schema.required",
|
|
814
|
+
severity: "error",
|
|
815
|
+
path: `${rulePath}/headerValue`,
|
|
816
|
+
message: "Header value is required for set and append operations."
|
|
712
817
|
});
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
delayInput.min = "0";
|
|
720
|
-
delayInput.max = String(LIMITS.maxMockDelayMs);
|
|
721
|
-
delayInput.step = "1";
|
|
722
|
-
delayInput.value = current.delayMs === void 0 ? "" : String(current.delayMs);
|
|
723
|
-
delayInput.dataset.editorField = "true";
|
|
724
|
-
delayInput.addEventListener("input", () => {
|
|
725
|
-
const c = getCurrent();
|
|
726
|
-
if (c === void 0) return;
|
|
727
|
-
const raw = delayInput.value;
|
|
728
|
-
const next = raw === "" ? { ...c, delayMs: void 0 } : { ...c, delayMs: Number(raw) };
|
|
729
|
-
setAction(next);
|
|
818
|
+
} else if ((operation === "set" || operation === "append") && typeof headerValue === "string" && headerValue.length > 4096) {
|
|
819
|
+
diagnostics.push({
|
|
820
|
+
code: "schema.out-of-range",
|
|
821
|
+
severity: "error",
|
|
822
|
+
path: `${rulePath}/headerValue`,
|
|
823
|
+
message: "Header value must be at most 4096 characters."
|
|
730
824
|
});
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
bodyOption.value = "body";
|
|
739
|
-
bodyOption.textContent = "Inline body";
|
|
740
|
-
const fileOption = document2.createElement("option");
|
|
741
|
-
fileOption.value = "file";
|
|
742
|
-
fileOption.textContent = "File snapshot";
|
|
743
|
-
sourceSelect.append(bodyOption, fileOption);
|
|
744
|
-
sourceSelect.value = typeof current.file === "string" ? "file" : "body";
|
|
745
|
-
context.registerControl("/mock/body-source", sourceSelect);
|
|
746
|
-
const bodyArea = document2.createElement("textarea");
|
|
747
|
-
bodyArea.value = typeof current.body === "string" ? current.body : "";
|
|
748
|
-
bodyArea.dataset.editorField = "true";
|
|
749
|
-
bodyArea.addEventListener("input", () => {
|
|
750
|
-
const c = getCurrent();
|
|
751
|
-
if (c === void 0) return;
|
|
752
|
-
setAction({ ...c, body: bodyArea.value, file: void 0 });
|
|
825
|
+
}
|
|
826
|
+
if (operation === "remove" && headerValue !== void 0) {
|
|
827
|
+
diagnostics.push({
|
|
828
|
+
code: "schema.unexpected",
|
|
829
|
+
severity: "error",
|
|
830
|
+
path: `${rulePath}/headerValue`,
|
|
831
|
+
message: "Header value must not be provided for remove operation."
|
|
753
832
|
});
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
fileLabel.textContent = "File path";
|
|
757
|
-
const fileInput = document2.createElement("input");
|
|
758
|
-
fileInput.type = "text";
|
|
759
|
-
fileInput.value = typeof current.file === "string" ? current.file : "";
|
|
760
|
-
fileInput.dataset.editorField = "true";
|
|
761
|
-
fileInput.addEventListener("input", () => {
|
|
762
|
-
const c = getCurrent();
|
|
763
|
-
if (c === void 0) return;
|
|
764
|
-
setAction({ ...c, body: void 0, file: fileInput.value });
|
|
765
|
-
});
|
|
766
|
-
context.registerControl("/mock/file", fileInput);
|
|
767
|
-
fileLabel.append(fileInput);
|
|
768
|
-
const syncSource = () => {
|
|
769
|
-
const useFile = sourceSelect.value === "file";
|
|
770
|
-
bodyArea.style.display = useFile ? "none" : "";
|
|
771
|
-
fileLabel.style.display = useFile ? "" : "none";
|
|
772
|
-
};
|
|
773
|
-
sourceSelect.addEventListener("change", syncSource);
|
|
774
|
-
syncSource();
|
|
775
|
-
const headersFieldset = document2.createElement("fieldset");
|
|
776
|
-
const headersLegend = document2.createElement("legend");
|
|
777
|
-
headersLegend.textContent = "Response headers";
|
|
778
|
-
headersFieldset.append(headersLegend);
|
|
779
|
-
const renderHeaders = () => {
|
|
780
|
-
const c = getCurrent();
|
|
781
|
-
if (c === void 0) return;
|
|
782
|
-
headersFieldset.querySelectorAll("[data-mock-header-row]").forEach((row) => {
|
|
783
|
-
row.remove();
|
|
784
|
-
});
|
|
785
|
-
const headers = c.headers ?? [];
|
|
786
|
-
headers.forEach((header, index) => {
|
|
787
|
-
const row = document2.createElement("div");
|
|
788
|
-
row.dataset.mockHeaderRow = String(index);
|
|
789
|
-
const nameLabel = document2.createElement("label");
|
|
790
|
-
nameLabel.textContent = "Name";
|
|
791
|
-
const nameInput = document2.createElement("input");
|
|
792
|
-
nameInput.type = "text";
|
|
793
|
-
nameInput.value = header.name;
|
|
794
|
-
nameInput.dataset.editorField = "true";
|
|
795
|
-
nameInput.addEventListener("input", () => {
|
|
796
|
-
const cc = getCurrent();
|
|
797
|
-
if (cc === void 0) return;
|
|
798
|
-
const next = (cc.headers ?? []).map(
|
|
799
|
-
(h, i) => i === index ? { ...h, name: nameInput.value } : h
|
|
800
|
-
);
|
|
801
|
-
setAction({ ...cc, headers: next });
|
|
802
|
-
});
|
|
803
|
-
context.registerControl(`/mock/headers/${index}/name`, nameInput);
|
|
804
|
-
nameLabel.append(nameInput);
|
|
805
|
-
const valueLabel = document2.createElement("label");
|
|
806
|
-
valueLabel.textContent = "Value";
|
|
807
|
-
const valueInput = document2.createElement("input");
|
|
808
|
-
valueInput.type = "text";
|
|
809
|
-
valueInput.value = header.value;
|
|
810
|
-
valueInput.dataset.editorField = "true";
|
|
811
|
-
valueInput.addEventListener("input", () => {
|
|
812
|
-
const cc = getCurrent();
|
|
813
|
-
if (cc === void 0) return;
|
|
814
|
-
const next = (cc.headers ?? []).map(
|
|
815
|
-
(h, i) => i === index ? { ...h, value: valueInput.value } : h
|
|
816
|
-
);
|
|
817
|
-
setAction({ ...cc, headers: next });
|
|
818
|
-
});
|
|
819
|
-
context.registerControl(`/mock/headers/${index}/value`, valueInput);
|
|
820
|
-
valueLabel.append(valueInput);
|
|
821
|
-
const remove = document2.createElement("button");
|
|
822
|
-
remove.type = "button";
|
|
823
|
-
remove.textContent = "Remove";
|
|
824
|
-
remove.dataset.editorField = "true";
|
|
825
|
-
remove.addEventListener("click", () => {
|
|
826
|
-
const cc = getCurrent();
|
|
827
|
-
if (cc === void 0) return;
|
|
828
|
-
const next = (cc.headers ?? []).filter((_, i) => i !== index);
|
|
829
|
-
setAction({ ...cc, headers: next });
|
|
830
|
-
});
|
|
831
|
-
row.append(nameLabel, valueLabel, remove);
|
|
832
|
-
headersFieldset.append(row);
|
|
833
|
-
});
|
|
834
|
-
const add = document2.createElement("button");
|
|
835
|
-
add.type = "button";
|
|
836
|
-
add.textContent = "Add header";
|
|
837
|
-
add.dataset.editorField = "true";
|
|
838
|
-
add.dataset.mockAddHeader = "true";
|
|
839
|
-
add.addEventListener("click", () => {
|
|
840
|
-
const cc = getCurrent();
|
|
841
|
-
if (cc === void 0) return;
|
|
842
|
-
setAction({
|
|
843
|
-
...cc,
|
|
844
|
-
headers: [...cc.headers ?? [], { name: "", value: "" }]
|
|
845
|
-
});
|
|
846
|
-
});
|
|
847
|
-
headersFieldset.append(add);
|
|
848
|
-
};
|
|
849
|
-
renderHeaders();
|
|
850
|
-
container.append(
|
|
851
|
-
statusLabel,
|
|
852
|
-
delayLabel,
|
|
853
|
-
sourceLabel,
|
|
854
|
-
bodyArea,
|
|
855
|
-
fileLabel,
|
|
856
|
-
headersFieldset
|
|
857
|
-
);
|
|
858
|
-
};
|
|
859
|
-
render();
|
|
860
|
-
return { destroy() {
|
|
861
|
-
} };
|
|
862
|
-
},
|
|
863
|
-
defaultAction() {
|
|
864
|
-
return { status: 200, body: "" };
|
|
833
|
+
}
|
|
834
|
+
return diagnostics;
|
|
865
835
|
}
|
|
866
836
|
};
|
|
867
837
|
}
|
|
@@ -870,17 +840,33 @@ function createMockRuleType() {
|
|
|
870
840
|
var MAX_QUERY_NAME_LENGTH = 256;
|
|
871
841
|
var MAX_QUERY_VALUE_LENGTH = 2048;
|
|
872
842
|
var MAX_QUERY_PARAMS = 64;
|
|
873
|
-
|
|
843
|
+
var QUERY_OPERATIONS = Object.freeze(["set", "remove"]);
|
|
844
|
+
function isRecord(value) {
|
|
874
845
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
875
846
|
}
|
|
876
847
|
function asQueryAction(value) {
|
|
877
|
-
if (!
|
|
848
|
+
if (!isRecord(value)) return void 0;
|
|
878
849
|
if (value.type !== "query") return void 0;
|
|
879
850
|
const params = value.params;
|
|
880
851
|
if (!Array.isArray(params)) return void 0;
|
|
881
852
|
return value;
|
|
882
853
|
}
|
|
883
|
-
function
|
|
854
|
+
function paramOperation(param) {
|
|
855
|
+
return param.operation ?? "set";
|
|
856
|
+
}
|
|
857
|
+
function buildSetParam(name, value) {
|
|
858
|
+
return { name, operation: "set", value };
|
|
859
|
+
}
|
|
860
|
+
function buildRemoveParam(name) {
|
|
861
|
+
return { name, operation: "remove" };
|
|
862
|
+
}
|
|
863
|
+
function normalizeParam(param) {
|
|
864
|
+
if (paramOperation(param) === "remove") {
|
|
865
|
+
return buildRemoveParam(param.name);
|
|
866
|
+
}
|
|
867
|
+
return buildSetParam(param.name, param.value ?? "");
|
|
868
|
+
}
|
|
869
|
+
function stable(diagnostics) {
|
|
884
870
|
return [...diagnostics].sort(
|
|
885
871
|
(a, b) => a.path === b.path ? a.code.localeCompare(b.code) : a.path.localeCompare(b.path)
|
|
886
872
|
);
|
|
@@ -889,7 +875,7 @@ var queryRuleType = {
|
|
|
889
875
|
id: "query",
|
|
890
876
|
label: "Query parameters",
|
|
891
877
|
matches(rule) {
|
|
892
|
-
return
|
|
878
|
+
return rule.type === "query";
|
|
893
879
|
},
|
|
894
880
|
validate(rule, rulePath) {
|
|
895
881
|
const action = asQueryAction(rule.action);
|
|
@@ -902,7 +888,7 @@ var queryRuleType = {
|
|
|
902
888
|
path: `${rulePath}/action/params`,
|
|
903
889
|
message: "Query rules need at least one parameter."
|
|
904
890
|
});
|
|
905
|
-
return
|
|
891
|
+
return stable(diagnostics);
|
|
906
892
|
}
|
|
907
893
|
if (action.params.length > MAX_QUERY_PARAMS) {
|
|
908
894
|
diagnostics.push({
|
|
@@ -916,6 +902,7 @@ var queryRuleType = {
|
|
|
916
902
|
action.params.forEach((param, index) => {
|
|
917
903
|
const namePath = `${rulePath}/action/params/${index}/name`;
|
|
918
904
|
const valuePath = `${rulePath}/action/params/${index}/value`;
|
|
905
|
+
const operation = paramOperation(param);
|
|
919
906
|
if (typeof param?.name !== "string" || param.name.length === 0) {
|
|
920
907
|
diagnostics.push({
|
|
921
908
|
code: "editor.query-param-name-required",
|
|
@@ -931,19 +918,28 @@ var queryRuleType = {
|
|
|
931
918
|
message: "Query parameter name is too long."
|
|
932
919
|
});
|
|
933
920
|
}
|
|
934
|
-
if (
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
921
|
+
if (operation === "set") {
|
|
922
|
+
if (typeof param?.value !== "string" || param.value.length === 0) {
|
|
923
|
+
diagnostics.push({
|
|
924
|
+
code: "editor.query-param-value-required",
|
|
925
|
+
severity: "error",
|
|
926
|
+
path: valuePath,
|
|
927
|
+
message: "Query parameter value is required."
|
|
928
|
+
});
|
|
929
|
+
} else if (param.value.length > MAX_QUERY_VALUE_LENGTH) {
|
|
930
|
+
diagnostics.push({
|
|
931
|
+
code: "editor.query-param-value-too-long",
|
|
932
|
+
severity: "error",
|
|
933
|
+
path: valuePath,
|
|
934
|
+
message: "Query parameter value is too long."
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
} else if (param?.value !== void 0) {
|
|
942
938
|
diagnostics.push({
|
|
943
|
-
code: "
|
|
939
|
+
code: "schema.unexpected",
|
|
944
940
|
severity: "error",
|
|
945
941
|
path: valuePath,
|
|
946
|
-
message: "Query parameter value
|
|
942
|
+
message: "Query parameter value must not be provided for remove operation."
|
|
947
943
|
});
|
|
948
944
|
}
|
|
949
945
|
if (typeof param?.name === "string" && seen.has(param.name)) {
|
|
@@ -956,7 +952,7 @@ var queryRuleType = {
|
|
|
956
952
|
}
|
|
957
953
|
if (typeof param?.name === "string") seen.add(param.name);
|
|
958
954
|
});
|
|
959
|
-
return
|
|
955
|
+
return stable(diagnostics);
|
|
960
956
|
},
|
|
961
957
|
mount(context) {
|
|
962
958
|
const { document: document2, container } = context;
|
|
@@ -971,6 +967,7 @@ var queryRuleType = {
|
|
|
971
967
|
current.params.forEach((param, index) => {
|
|
972
968
|
const row = document2.createElement("div");
|
|
973
969
|
row.dataset.queryParamRow = String(index);
|
|
970
|
+
const operation = paramOperation(param);
|
|
974
971
|
const nameLabel = document2.createElement("label");
|
|
975
972
|
nameLabel.textContent = "Name";
|
|
976
973
|
const nameInput = document2.createElement("input");
|
|
@@ -980,29 +977,57 @@ var queryRuleType = {
|
|
|
980
977
|
nameInput.addEventListener("input", () => {
|
|
981
978
|
const c = getCurrent();
|
|
982
979
|
if (c === void 0) return;
|
|
983
|
-
const params = c.params.map(
|
|
984
|
-
(
|
|
985
|
-
|
|
980
|
+
const params = c.params.map((p, i) => {
|
|
981
|
+
if (i !== index) return p;
|
|
982
|
+
return normalizeParam({ ...p, name: nameInput.value });
|
|
983
|
+
});
|
|
986
984
|
setAction({ type: "query", params });
|
|
987
985
|
});
|
|
988
986
|
context.registerControl(`/action/params/${index}/name`, nameInput);
|
|
989
987
|
nameLabel.append(nameInput);
|
|
988
|
+
const operationLabel = document2.createElement("label");
|
|
989
|
+
operationLabel.textContent = "Operation";
|
|
990
|
+
const operationSelect = createSelect(
|
|
991
|
+
document2,
|
|
992
|
+
QUERY_OPERATIONS,
|
|
993
|
+
operation,
|
|
994
|
+
(value) => {
|
|
995
|
+
const c = getCurrent();
|
|
996
|
+
if (c === void 0) return;
|
|
997
|
+
const params = c.params.map((p, i) => {
|
|
998
|
+
if (i !== index) return p;
|
|
999
|
+
if (value === "remove") {
|
|
1000
|
+
return buildRemoveParam(p.name);
|
|
1001
|
+
}
|
|
1002
|
+
return buildSetParam(p.name, p.value ?? "");
|
|
1003
|
+
});
|
|
1004
|
+
setAction({ type: "query", params });
|
|
1005
|
+
render();
|
|
1006
|
+
}
|
|
1007
|
+
);
|
|
1008
|
+
operationSelect.dataset.queryParamOperation = String(index);
|
|
1009
|
+
context.registerControl(
|
|
1010
|
+
`/action/params/${index}/operation`,
|
|
1011
|
+
operationSelect
|
|
1012
|
+
);
|
|
1013
|
+
operationLabel.append(operationSelect);
|
|
990
1014
|
const valueLabel = document2.createElement("label");
|
|
991
1015
|
valueLabel.textContent = "Value";
|
|
992
1016
|
const valueInput = document2.createElement("input");
|
|
993
1017
|
valueInput.type = "text";
|
|
994
|
-
valueInput.value = param.value;
|
|
1018
|
+
valueInput.value = param.value ?? "";
|
|
995
1019
|
valueInput.dataset.editorField = "true";
|
|
996
1020
|
valueInput.addEventListener("input", () => {
|
|
997
1021
|
const c = getCurrent();
|
|
998
1022
|
if (c === void 0) return;
|
|
999
1023
|
const params = c.params.map(
|
|
1000
|
-
(p, i) => i === index ?
|
|
1024
|
+
(p, i) => i === index ? buildSetParam(p.name, valueInput.value) : p
|
|
1001
1025
|
);
|
|
1002
1026
|
setAction({ type: "query", params });
|
|
1003
1027
|
});
|
|
1004
1028
|
context.registerControl(`/action/params/${index}/value`, valueInput);
|
|
1005
1029
|
valueLabel.append(valueInput);
|
|
1030
|
+
valueLabel.hidden = operation === "remove";
|
|
1006
1031
|
const remove = document2.createElement("button");
|
|
1007
1032
|
remove.type = "button";
|
|
1008
1033
|
remove.textContent = "Remove";
|
|
@@ -1012,8 +1037,9 @@ var queryRuleType = {
|
|
|
1012
1037
|
if (c === void 0) return;
|
|
1013
1038
|
const params = c.params.filter((_, i) => i !== index);
|
|
1014
1039
|
setAction({ type: "query", params });
|
|
1040
|
+
render();
|
|
1015
1041
|
});
|
|
1016
|
-
row.append(nameLabel, valueLabel, remove);
|
|
1042
|
+
row.append(nameLabel, operationLabel, valueLabel, remove);
|
|
1017
1043
|
container.append(row);
|
|
1018
1044
|
});
|
|
1019
1045
|
const add = document2.createElement("button");
|
|
@@ -1025,8 +1051,9 @@ var queryRuleType = {
|
|
|
1025
1051
|
if (c === void 0) return;
|
|
1026
1052
|
setAction({
|
|
1027
1053
|
type: "query",
|
|
1028
|
-
params: [...c.params,
|
|
1054
|
+
params: [...c.params, buildSetParam("", "")]
|
|
1029
1055
|
});
|
|
1056
|
+
render();
|
|
1030
1057
|
});
|
|
1031
1058
|
container.append(add);
|
|
1032
1059
|
};
|
|
@@ -1035,34 +1062,71 @@ var queryRuleType = {
|
|
|
1035
1062
|
} };
|
|
1036
1063
|
},
|
|
1037
1064
|
defaultAction() {
|
|
1038
|
-
return {
|
|
1065
|
+
return {
|
|
1066
|
+
type: "query",
|
|
1067
|
+
params: [buildSetParam("", "")]
|
|
1068
|
+
};
|
|
1039
1069
|
}
|
|
1040
1070
|
};
|
|
1041
1071
|
|
|
1072
|
+
// packages/editor/src/rule-types/redirect.ts
|
|
1073
|
+
function createRedirectRuleType() {
|
|
1074
|
+
return {
|
|
1075
|
+
id: "redirect",
|
|
1076
|
+
label: "Redirect",
|
|
1077
|
+
actionField: "redirect",
|
|
1078
|
+
matches(rule) {
|
|
1079
|
+
return rule.type === "redirect";
|
|
1080
|
+
},
|
|
1081
|
+
defaultAction() {
|
|
1082
|
+
return { destination: "" };
|
|
1083
|
+
},
|
|
1084
|
+
mount(context) {
|
|
1085
|
+
const document2 = context.document;
|
|
1086
|
+
const label = document2.createElement("label");
|
|
1087
|
+
label.textContent = "Redirect destination URL";
|
|
1088
|
+
const input = document2.createElement("input");
|
|
1089
|
+
input.type = "text";
|
|
1090
|
+
input.maxLength = 2048;
|
|
1091
|
+
const existing = context.getField("redirect.destination");
|
|
1092
|
+
input.value = typeof existing === "string" ? existing : "";
|
|
1093
|
+
input.addEventListener("input", () => {
|
|
1094
|
+
context.setField("redirect.destination", input.value);
|
|
1095
|
+
});
|
|
1096
|
+
context.registerControl("/redirect/destination", input);
|
|
1097
|
+
label.append(input);
|
|
1098
|
+
context.container.append(label);
|
|
1099
|
+
return {
|
|
1100
|
+
destroy() {
|
|
1101
|
+
input.removeEventListener("input", () => {
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
},
|
|
1106
|
+
validate(rule, rulePath) {
|
|
1107
|
+
const redirect = rule.redirect;
|
|
1108
|
+
const destination = redirect !== null && typeof redirect === "object" && typeof redirect.destination === "string" ? redirect.destination : "";
|
|
1109
|
+
const urlRegex = typeof rule.urlRegex === "string" ? rule.urlRegex : "";
|
|
1110
|
+
const issues = validateRedirectDestination(destination, urlRegex);
|
|
1111
|
+
return issues.map((issue) => ({
|
|
1112
|
+
code: `schema.${issue.code}`,
|
|
1113
|
+
severity: "error",
|
|
1114
|
+
path: `${rulePath}/redirect/destination`,
|
|
1115
|
+
message: issue.message
|
|
1116
|
+
}));
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1042
1121
|
// packages/editor/src/rule-types/request-body.ts
|
|
1043
|
-
function
|
|
1122
|
+
function isRecord2(value) {
|
|
1044
1123
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1045
1124
|
}
|
|
1046
1125
|
function requestBodyOf(value) {
|
|
1047
|
-
if (!
|
|
1126
|
+
if (!isRecord2(value) || !("mode" in value)) return void 0;
|
|
1048
1127
|
return value;
|
|
1049
1128
|
}
|
|
1050
|
-
function
|
|
1051
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
1052
|
-
const code = value.charCodeAt(i);
|
|
1053
|
-
if (code >= 55296 && code <= 56319) {
|
|
1054
|
-
if (i + 1 >= value.length) return true;
|
|
1055
|
-
const next = value.charCodeAt(i + 1);
|
|
1056
|
-
if (next < 56320 || next > 57343) return true;
|
|
1057
|
-
} else if (code >= 56320 && code <= 57343) {
|
|
1058
|
-
if (i === 0) return true;
|
|
1059
|
-
const prev = value.charCodeAt(i - 1);
|
|
1060
|
-
if (prev < 55296 || prev > 56319) return true;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
return false;
|
|
1064
|
-
}
|
|
1065
|
-
function stable3(values) {
|
|
1129
|
+
function stable2(values) {
|
|
1066
1130
|
return [...values].sort(
|
|
1067
1131
|
(left, right) => left.path.localeCompare(right.path) || left.code.localeCompare(right.code)
|
|
1068
1132
|
);
|
|
@@ -1178,7 +1242,7 @@ function createRequestBodyRuleType() {
|
|
|
1178
1242
|
message: 'Request-body rules require exactly one resource type: "xmlhttprequest".'
|
|
1179
1243
|
});
|
|
1180
1244
|
}
|
|
1181
|
-
return
|
|
1245
|
+
return stable2(diagnostics);
|
|
1182
1246
|
},
|
|
1183
1247
|
mount(context) {
|
|
1184
1248
|
const { document: document2, container } = context;
|
|
@@ -1186,26 +1250,25 @@ function createRequestBodyRuleType() {
|
|
|
1186
1250
|
const current = requestBodyOf(context.getField("requestBody"));
|
|
1187
1251
|
container.replaceChildren();
|
|
1188
1252
|
if (current === void 0) return;
|
|
1189
|
-
const modeSelect =
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1253
|
+
const modeSelect = createSelect(
|
|
1254
|
+
document2,
|
|
1255
|
+
[
|
|
1256
|
+
{ value: "replace", label: "Replace body" },
|
|
1257
|
+
{ value: "regex", label: "Regex replace" }
|
|
1258
|
+
],
|
|
1259
|
+
current.mode,
|
|
1260
|
+
(mode) => {
|
|
1261
|
+
const next = { ...current, mode };
|
|
1262
|
+
if (mode === "replace") {
|
|
1263
|
+
delete next.pattern;
|
|
1264
|
+
delete next.replacement;
|
|
1265
|
+
} else {
|
|
1266
|
+
delete next.body;
|
|
1267
|
+
}
|
|
1268
|
+
context.setField("requestBody", next);
|
|
1269
|
+
render();
|
|
1205
1270
|
}
|
|
1206
|
-
|
|
1207
|
-
render();
|
|
1208
|
-
});
|
|
1271
|
+
);
|
|
1209
1272
|
context.registerControl("/requestBody/mode", modeSelect);
|
|
1210
1273
|
container.append(modeSelect);
|
|
1211
1274
|
if (current.mode === "replace") {
|
|
@@ -1252,71 +1315,135 @@ function createRequestBodyRuleType() {
|
|
|
1252
1315
|
}
|
|
1253
1316
|
|
|
1254
1317
|
// packages/editor/src/rule-types/response-body.ts
|
|
1255
|
-
function
|
|
1318
|
+
function isRecord3(value) {
|
|
1256
1319
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1257
1320
|
}
|
|
1258
|
-
function
|
|
1259
|
-
if (!
|
|
1260
|
-
|
|
1321
|
+
function responseBodyOf(value) {
|
|
1322
|
+
if (!isRecord3(value)) return void 0;
|
|
1323
|
+
if (value.mode === "replace") {
|
|
1324
|
+
return { mode: "replace", body: value.body };
|
|
1325
|
+
}
|
|
1326
|
+
if (value.mode === "regex" && Array.isArray(value.replacements)) {
|
|
1327
|
+
return {
|
|
1328
|
+
mode: "regex",
|
|
1329
|
+
replacements: value.replacements
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
if (Array.isArray(value.replacements)) {
|
|
1333
|
+
return {
|
|
1334
|
+
mode: "regex",
|
|
1335
|
+
replacements: value.replacements
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
return void 0;
|
|
1261
1339
|
}
|
|
1262
|
-
function
|
|
1340
|
+
function stable3(values) {
|
|
1263
1341
|
return [...values].sort(
|
|
1264
1342
|
(left, right) => left.path.localeCompare(right.path) || left.code.localeCompare(right.code)
|
|
1265
1343
|
);
|
|
1266
1344
|
}
|
|
1345
|
+
function validateRegexReplacements(replacements, rulePath) {
|
|
1346
|
+
const diagnostics = [];
|
|
1347
|
+
if (replacements.length === 0 || replacements.length > LIMITS.maxResponseBodyReplacements) {
|
|
1348
|
+
diagnostics.push({
|
|
1349
|
+
code: "editor.response-body-replacements",
|
|
1350
|
+
severity: "error",
|
|
1351
|
+
path: `${rulePath}/responseBody/replacements`,
|
|
1352
|
+
message: `Response-body rules need 1-${LIMITS.maxResponseBodyReplacements} replacements.`
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
replacements.forEach((entry, index) => {
|
|
1356
|
+
const patternPath = `${rulePath}/responseBody/replacements/${index}/pattern`;
|
|
1357
|
+
const replacementPath = `${rulePath}/responseBody/replacements/${index}/replacement`;
|
|
1358
|
+
try {
|
|
1359
|
+
if (typeof entry?.pattern !== "string" || entry.pattern.length === 0 || entry.pattern.length > LIMITS.maxResponseBodyPatternLength)
|
|
1360
|
+
throw new Error();
|
|
1361
|
+
new RegExp(entry.pattern, "u");
|
|
1362
|
+
} catch {
|
|
1363
|
+
diagnostics.push({
|
|
1364
|
+
code: "editor.response-body-pattern",
|
|
1365
|
+
severity: "error",
|
|
1366
|
+
path: patternPath,
|
|
1367
|
+
message: "Replacement pattern must be a valid bounded regular expression."
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
if (typeof entry?.replacement !== "string" || entry.replacement.length > LIMITS.maxResponseBodyReplacementLength) {
|
|
1371
|
+
diagnostics.push({
|
|
1372
|
+
code: "editor.response-body-replacement",
|
|
1373
|
+
severity: "error",
|
|
1374
|
+
path: replacementPath,
|
|
1375
|
+
message: "Replacement text exceeds the permitted size."
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
return diagnostics;
|
|
1380
|
+
}
|
|
1267
1381
|
function createResponseBodyRuleType() {
|
|
1268
1382
|
return {
|
|
1269
1383
|
id: "response-body",
|
|
1270
|
-
label: "Response body
|
|
1384
|
+
label: "Response body",
|
|
1271
1385
|
actionField: "responseBody",
|
|
1272
1386
|
matches(rule) {
|
|
1273
|
-
return rule.type === "response-body" ||
|
|
1387
|
+
return rule.type === "response-body" || responseBodyOf(rule.responseBody) !== void 0;
|
|
1274
1388
|
},
|
|
1275
1389
|
validate(rule, rulePath) {
|
|
1276
|
-
const
|
|
1277
|
-
if (
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1390
|
+
const raw = rule.responseBody;
|
|
1391
|
+
if (isRecord3(raw) && "mode" in raw && raw.mode !== "replace" && raw.mode !== "regex") {
|
|
1392
|
+
return stable3([
|
|
1393
|
+
{
|
|
1394
|
+
code: "editor.response-body-mode",
|
|
1395
|
+
severity: "error",
|
|
1396
|
+
path: `${rulePath}/responseBody/mode`,
|
|
1397
|
+
message: 'responseBody.mode must be "replace" or "regex"'
|
|
1398
|
+
}
|
|
1399
|
+
]);
|
|
1286
1400
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1401
|
+
const action = responseBodyOf(raw);
|
|
1402
|
+
if (action === void 0) {
|
|
1403
|
+
if (isRecord3(raw) && raw.mode === "regex") {
|
|
1404
|
+
return stable3(validateRegexReplacements([], rulePath));
|
|
1405
|
+
}
|
|
1406
|
+
return [];
|
|
1407
|
+
}
|
|
1408
|
+
const diagnostics = [];
|
|
1409
|
+
if (action.mode === "replace") {
|
|
1410
|
+
if (typeof action.body !== "string") {
|
|
1295
1411
|
diagnostics.push({
|
|
1296
|
-
code: "editor.response-body-
|
|
1412
|
+
code: "editor.response-body-replace-body",
|
|
1297
1413
|
severity: "error",
|
|
1298
|
-
path:
|
|
1299
|
-
message: "
|
|
1414
|
+
path: `${rulePath}/responseBody/body`,
|
|
1415
|
+
message: "Replace mode requires a body string."
|
|
1300
1416
|
});
|
|
1301
|
-
}
|
|
1302
|
-
if (typeof entry?.replacement !== "string" || entry.replacement.length > LIMITS.maxResponseBodyReplacementLength) {
|
|
1417
|
+
} else if (action.body.length > LIMITS.maxResponseBodyBytes) {
|
|
1303
1418
|
diagnostics.push({
|
|
1304
|
-
code: "editor.response-body-
|
|
1419
|
+
code: "editor.response-body-replace-body",
|
|
1305
1420
|
severity: "error",
|
|
1306
|
-
path:
|
|
1307
|
-
message:
|
|
1421
|
+
path: `${rulePath}/responseBody/body`,
|
|
1422
|
+
message: `Replace body exceeds the maximum size of ${LIMITS.maxResponseBodyBytes} bytes.`
|
|
1423
|
+
});
|
|
1424
|
+
} else if (hasLoneSurrogate(action.body)) {
|
|
1425
|
+
diagnostics.push({
|
|
1426
|
+
code: "editor.response-body-lone-surrogate",
|
|
1427
|
+
severity: "error",
|
|
1428
|
+
path: `${rulePath}/responseBody/body`,
|
|
1429
|
+
message: "Replace body must not contain lone UTF-16 surrogates."
|
|
1308
1430
|
});
|
|
1309
1431
|
}
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1432
|
+
} else {
|
|
1433
|
+
diagnostics.push(
|
|
1434
|
+
...validateRegexReplacements(action.replacements, rulePath)
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
return stable3(diagnostics);
|
|
1312
1438
|
},
|
|
1313
1439
|
mount(context) {
|
|
1314
1440
|
const { document: document2, container } = context;
|
|
1315
|
-
const
|
|
1316
|
-
const
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1441
|
+
const currentReplacements = () => {
|
|
1442
|
+
const action = responseBodyOf(context.getField("responseBody"));
|
|
1443
|
+
return action?.mode === "regex" ? action.replacements : [];
|
|
1444
|
+
};
|
|
1445
|
+
const renderRegexRows = (onUpdate) => {
|
|
1446
|
+
currentReplacements().forEach((entry, index) => {
|
|
1320
1447
|
const row = document2.createElement("div");
|
|
1321
1448
|
row.dataset.responseBodyReplacement = String(index);
|
|
1322
1449
|
const pattern = document2.createElement("input");
|
|
@@ -1324,11 +1451,9 @@ function createResponseBodyRuleType() {
|
|
|
1324
1451
|
pattern.value = entry.pattern;
|
|
1325
1452
|
pattern.placeholder = "Pattern";
|
|
1326
1453
|
pattern.addEventListener("input", () => {
|
|
1327
|
-
const next = [
|
|
1328
|
-
...replacementsOf(context.getField("responseBody")) ?? []
|
|
1329
|
-
];
|
|
1454
|
+
const next = [...currentReplacements()];
|
|
1330
1455
|
next[index] = { ...next[index], pattern: pattern.value };
|
|
1331
|
-
|
|
1456
|
+
onUpdate(next);
|
|
1332
1457
|
});
|
|
1333
1458
|
context.registerControl(
|
|
1334
1459
|
`/responseBody/replacements/${index}/pattern`,
|
|
@@ -1339,11 +1464,9 @@ function createResponseBodyRuleType() {
|
|
|
1339
1464
|
replacement.value = entry.replacement;
|
|
1340
1465
|
replacement.placeholder = "Replacement";
|
|
1341
1466
|
replacement.addEventListener("input", () => {
|
|
1342
|
-
const next = [
|
|
1343
|
-
...replacementsOf(context.getField("responseBody")) ?? []
|
|
1344
|
-
];
|
|
1467
|
+
const next = [...currentReplacements()];
|
|
1345
1468
|
next[index] = { ...next[index], replacement: replacement.value };
|
|
1346
|
-
|
|
1469
|
+
onUpdate(next);
|
|
1347
1470
|
});
|
|
1348
1471
|
context.registerControl(
|
|
1349
1472
|
`/responseBody/replacements/${index}/replacement`,
|
|
@@ -1353,8 +1476,11 @@ function createResponseBodyRuleType() {
|
|
|
1353
1476
|
remove.type = "button";
|
|
1354
1477
|
remove.textContent = "Remove replacement";
|
|
1355
1478
|
remove.addEventListener("click", () => {
|
|
1356
|
-
|
|
1357
|
-
|
|
1479
|
+
onUpdate(
|
|
1480
|
+
currentReplacements().filter(
|
|
1481
|
+
(_, itemIndex) => itemIndex !== index
|
|
1482
|
+
)
|
|
1483
|
+
);
|
|
1358
1484
|
render();
|
|
1359
1485
|
});
|
|
1360
1486
|
row.append(pattern, replacement, remove);
|
|
@@ -1364,29 +1490,81 @@ function createResponseBodyRuleType() {
|
|
|
1364
1490
|
add.type = "button";
|
|
1365
1491
|
add.textContent = "Add replacement";
|
|
1366
1492
|
add.addEventListener("click", () => {
|
|
1367
|
-
|
|
1368
|
-
...
|
|
1493
|
+
onUpdate([
|
|
1494
|
+
...currentReplacements(),
|
|
1369
1495
|
{ pattern: "", replacement: "" }
|
|
1370
|
-
];
|
|
1371
|
-
context.setField("responseBody", { replacements: next });
|
|
1496
|
+
]);
|
|
1372
1497
|
render();
|
|
1373
1498
|
});
|
|
1374
1499
|
container.append(add);
|
|
1375
1500
|
};
|
|
1501
|
+
const render = () => {
|
|
1502
|
+
const current = responseBodyOf(context.getField("responseBody"));
|
|
1503
|
+
container.replaceChildren();
|
|
1504
|
+
if (current === void 0) return;
|
|
1505
|
+
const modeSelect = createSelect(
|
|
1506
|
+
document2,
|
|
1507
|
+
[
|
|
1508
|
+
{ value: "replace", label: "Replace body" },
|
|
1509
|
+
{ value: "regex", label: "Regex rewrite" }
|
|
1510
|
+
],
|
|
1511
|
+
current.mode,
|
|
1512
|
+
(mode) => {
|
|
1513
|
+
if (mode === "replace") {
|
|
1514
|
+
context.setField("responseBody", { mode: "replace", body: "" });
|
|
1515
|
+
} else {
|
|
1516
|
+
context.setField("responseBody", {
|
|
1517
|
+
mode: "regex",
|
|
1518
|
+
replacements: [{ pattern: "", replacement: "" }]
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
render();
|
|
1522
|
+
}
|
|
1523
|
+
);
|
|
1524
|
+
context.registerControl("/responseBody/mode", modeSelect);
|
|
1525
|
+
container.append(modeSelect);
|
|
1526
|
+
if (current.mode === "replace") {
|
|
1527
|
+
const bodyInput = document2.createElement("textarea");
|
|
1528
|
+
bodyInput.value = current.body ?? "";
|
|
1529
|
+
bodyInput.placeholder = "Replacement body";
|
|
1530
|
+
bodyInput.addEventListener("input", () => {
|
|
1531
|
+
context.setField("responseBody", {
|
|
1532
|
+
mode: "replace",
|
|
1533
|
+
body: bodyInput.value
|
|
1534
|
+
});
|
|
1535
|
+
});
|
|
1536
|
+
context.registerControl("/responseBody/body", bodyInput);
|
|
1537
|
+
container.append(bodyInput);
|
|
1538
|
+
} else {
|
|
1539
|
+
const stored = context.getField("responseBody");
|
|
1540
|
+
const writeRegex = (replacements) => {
|
|
1541
|
+
if (isRecord3(stored) && !("mode" in stored) && Array.isArray(stored.replacements)) {
|
|
1542
|
+
context.setField("responseBody", { replacements });
|
|
1543
|
+
} else {
|
|
1544
|
+
context.setField("responseBody", {
|
|
1545
|
+
mode: "regex",
|
|
1546
|
+
replacements
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
renderRegexRows(writeRegex);
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1376
1553
|
render();
|
|
1377
1554
|
return { destroy() {
|
|
1378
1555
|
} };
|
|
1379
1556
|
},
|
|
1380
1557
|
defaultAction() {
|
|
1381
|
-
return {
|
|
1558
|
+
return { mode: "replace", body: "" };
|
|
1382
1559
|
}
|
|
1383
1560
|
};
|
|
1384
1561
|
}
|
|
1385
1562
|
|
|
1386
1563
|
// packages/editor/src/rule-types/index.ts
|
|
1387
1564
|
var builtInRuleTypes = Object.freeze([
|
|
1565
|
+
createHeaderRuleType(),
|
|
1566
|
+
createRedirectRuleType(),
|
|
1388
1567
|
queryRuleType,
|
|
1389
|
-
createMockRuleType(),
|
|
1390
1568
|
createResponseBodyRuleType(),
|
|
1391
1569
|
createRequestBodyRuleType()
|
|
1392
1570
|
]);
|
|
@@ -1472,7 +1650,7 @@ var FORBIDDEN_EXTENSION_FIELDS = /* @__PURE__ */ new Set([
|
|
|
1472
1650
|
var MAX_SNAPSHOT_ARRAY_LENGTH = 4096;
|
|
1473
1651
|
var F2_MAX_URL_REGEX_LENGTH2 = 2048;
|
|
1474
1652
|
var editorInstanceCount = 0;
|
|
1475
|
-
function
|
|
1653
|
+
function isRecord4(value) {
|
|
1476
1654
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1477
1655
|
}
|
|
1478
1656
|
function snapshotOwnData(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
@@ -1551,11 +1729,11 @@ function freezeSnapshot(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
1551
1729
|
return value;
|
|
1552
1730
|
}
|
|
1553
1731
|
function asDraftProject(value) {
|
|
1554
|
-
if (!
|
|
1732
|
+
if (!isRecord4(value) || !Array.isArray(value.groups)) return void 0;
|
|
1555
1733
|
for (const group of value.groups) {
|
|
1556
|
-
if (!
|
|
1734
|
+
if (!isRecord4(group) || !Array.isArray(group.rules)) return void 0;
|
|
1557
1735
|
for (const rule of group.rules) {
|
|
1558
|
-
if (!
|
|
1736
|
+
if (!isRecord4(rule)) return void 0;
|
|
1559
1737
|
}
|
|
1560
1738
|
}
|
|
1561
1739
|
return value;
|
|
@@ -1586,7 +1764,7 @@ function valueAtPath(root, path) {
|
|
|
1586
1764
|
if (index === void 0 || !Object.hasOwn(current, index))
|
|
1587
1765
|
return void 0;
|
|
1588
1766
|
current = current[index];
|
|
1589
|
-
} else if (
|
|
1767
|
+
} else if (isRecord4(current) && Object.hasOwn(current, segment)) {
|
|
1590
1768
|
current = current[segment];
|
|
1591
1769
|
} else {
|
|
1592
1770
|
return void 0;
|
|
@@ -1594,6 +1772,25 @@ function valueAtPath(root, path) {
|
|
|
1594
1772
|
}
|
|
1595
1773
|
return current;
|
|
1596
1774
|
}
|
|
1775
|
+
var MATCHER_CREATABLE_FIELDS = ["description", "method", "type"];
|
|
1776
|
+
var ACTION_PAYLOAD_FIELDS = [
|
|
1777
|
+
"redirect",
|
|
1778
|
+
"action",
|
|
1779
|
+
"requestBody",
|
|
1780
|
+
"responseBody",
|
|
1781
|
+
"headerDirection",
|
|
1782
|
+
"headerOperation",
|
|
1783
|
+
"headerName",
|
|
1784
|
+
"headerValue"
|
|
1785
|
+
];
|
|
1786
|
+
var CREATABLE_RULE_FIELDS = /* @__PURE__ */ new Set([
|
|
1787
|
+
...MATCHER_CREATABLE_FIELDS,
|
|
1788
|
+
...ACTION_PAYLOAD_FIELDS,
|
|
1789
|
+
"redactSensitiveInLogs"
|
|
1790
|
+
]);
|
|
1791
|
+
function isBodyRuleType(type) {
|
|
1792
|
+
return type === "request-body" || type === "response-body";
|
|
1793
|
+
}
|
|
1597
1794
|
function setValueAtPath(root, path, value) {
|
|
1598
1795
|
const segments = decodePointer(path);
|
|
1599
1796
|
if (!segments || segments.length === 0) return false;
|
|
@@ -1606,7 +1803,7 @@ function setValueAtPath(root, path, value) {
|
|
|
1606
1803
|
return false;
|
|
1607
1804
|
}
|
|
1608
1805
|
current = current[childIndex];
|
|
1609
|
-
} else if (
|
|
1806
|
+
} else if (isRecord4(current) && Object.hasOwn(current, segment)) {
|
|
1610
1807
|
current = current[segment];
|
|
1611
1808
|
} else {
|
|
1612
1809
|
return false;
|
|
@@ -1620,8 +1817,8 @@ function setValueAtPath(root, path, value) {
|
|
|
1620
1817
|
current[index] = value;
|
|
1621
1818
|
return true;
|
|
1622
1819
|
}
|
|
1623
|
-
if (!
|
|
1624
|
-
if (!Object.hasOwn(current, finalSegment) && finalSegment
|
|
1820
|
+
if (!isRecord4(current)) return false;
|
|
1821
|
+
if (!Object.hasOwn(current, finalSegment) && !CREATABLE_RULE_FIELDS.has(finalSegment)) {
|
|
1625
1822
|
return false;
|
|
1626
1823
|
}
|
|
1627
1824
|
if (Object.is(current[finalSegment], value)) return false;
|
|
@@ -1639,7 +1836,7 @@ function deleteValueAtPath(root, path) {
|
|
|
1639
1836
|
const parentPath = pointer(...segments.slice(0, -1));
|
|
1640
1837
|
const parent = valueAtPath(root, parentPath);
|
|
1641
1838
|
const key = segments[segments.length - 1];
|
|
1642
|
-
if (!
|
|
1839
|
+
if (!isRecord4(parent) || !Object.hasOwn(parent, key)) return false;
|
|
1643
1840
|
return delete parent[key];
|
|
1644
1841
|
}
|
|
1645
1842
|
function safeText(value, fallback = "") {
|
|
@@ -1675,7 +1872,7 @@ function normalizeDiagnostics(value) {
|
|
|
1675
1872
|
}
|
|
1676
1873
|
const diagnostics = [];
|
|
1677
1874
|
for (const item of value) {
|
|
1678
|
-
if (!
|
|
1875
|
+
if (!isRecord4(item)) continue;
|
|
1679
1876
|
const code = item.code;
|
|
1680
1877
|
const path = item.path;
|
|
1681
1878
|
const message = item.message;
|
|
@@ -1688,12 +1885,11 @@ function normalizeDiagnostics(value) {
|
|
|
1688
1885
|
function isValidExtensionName(name) {
|
|
1689
1886
|
return name.length > 0 && !COMMON_RULE_FIELDS.has(name) && !FORBIDDEN_EXTENSION_FIELDS.has(name) && !hasControl(name);
|
|
1690
1887
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
if (!isRecord5(rule)) return false;
|
|
1888
|
+
function clearActionFields(rule, keep, keepFields) {
|
|
1889
|
+
if (!isRecord4(rule)) return false;
|
|
1694
1890
|
let changed = false;
|
|
1695
|
-
for (const field of
|
|
1696
|
-
if (field === keep) continue;
|
|
1891
|
+
for (const field of ACTION_PAYLOAD_FIELDS) {
|
|
1892
|
+
if (field === keep || keepFields?.has(field)) continue;
|
|
1697
1893
|
if (Object.hasOwn(rule, field)) {
|
|
1698
1894
|
delete rule[field];
|
|
1699
1895
|
changed = true;
|
|
@@ -1706,9 +1902,9 @@ function extensionFieldParent(rule, name) {
|
|
|
1706
1902
|
if (segments.length === 0) return void 0;
|
|
1707
1903
|
let current = rule;
|
|
1708
1904
|
for (let index = 0; index < segments.length - 1; index += 1) {
|
|
1709
|
-
if (!
|
|
1905
|
+
if (!isRecord4(current)) return void 0;
|
|
1710
1906
|
const next = current[segments[index]];
|
|
1711
|
-
if (!
|
|
1907
|
+
if (!isRecord4(next)) {
|
|
1712
1908
|
const created = {};
|
|
1713
1909
|
Object.defineProperty(current, segments[index], {
|
|
1714
1910
|
configurable: true,
|
|
@@ -1721,7 +1917,7 @@ function extensionFieldParent(rule, name) {
|
|
|
1721
1917
|
current = next;
|
|
1722
1918
|
}
|
|
1723
1919
|
}
|
|
1724
|
-
if (!
|
|
1920
|
+
if (!isRecord4(current)) return void 0;
|
|
1725
1921
|
return { parent: current, key: segments[segments.length - 1] };
|
|
1726
1922
|
}
|
|
1727
1923
|
function toSearchText(value) {
|
|
@@ -2009,6 +2205,14 @@ var EditorControllerImpl = class {
|
|
|
2009
2205
|
return;
|
|
2010
2206
|
}
|
|
2011
2207
|
const path = target.dataset.path;
|
|
2208
|
+
if (path && target instanceof HTMLInputElement && target.type === "checkbox" && !this.extensionControls.has(path)) {
|
|
2209
|
+
const changed = setValueAtPath(this.draft, path, target.checked);
|
|
2210
|
+
if (changed) {
|
|
2211
|
+
this.markChanged();
|
|
2212
|
+
this.render();
|
|
2213
|
+
}
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2012
2216
|
if (!path || this.extensionControls.has(path)) return;
|
|
2013
2217
|
if (this.updateCommonField(path, target.value)) this.render();
|
|
2014
2218
|
};
|
|
@@ -2283,7 +2487,7 @@ var EditorControllerImpl = class {
|
|
|
2283
2487
|
const ruleContainer = valueAtPath(this.draft, ruleContainerPath);
|
|
2284
2488
|
clearActionFields(
|
|
2285
2489
|
ruleContainer,
|
|
2286
|
-
rawValue === "redirect"
|
|
2490
|
+
rawValue === "redirect" ? rawValue : void 0
|
|
2287
2491
|
);
|
|
2288
2492
|
this.markChanged();
|
|
2289
2493
|
}
|
|
@@ -2319,16 +2523,30 @@ var EditorControllerImpl = class {
|
|
|
2319
2523
|
return;
|
|
2320
2524
|
}
|
|
2321
2525
|
const extension = this.extensions.find((entry) => entry.id === typeId);
|
|
2322
|
-
if (!extension?.defaultAction) return;
|
|
2323
|
-
const actionField = extension.actionField ?? "action";
|
|
2526
|
+
if (!extension?.defaultAction && !extension?.defaultFields) return;
|
|
2324
2527
|
const changedType = setValueAtPath(this.draft, `${rulePath}/type`, typeId);
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2528
|
+
let changedPayload = false;
|
|
2529
|
+
let keep;
|
|
2530
|
+
let keepFields;
|
|
2531
|
+
if (extension.defaultFields) {
|
|
2532
|
+
const fields = extension.defaultFields();
|
|
2533
|
+
keepFields = new Set(Object.keys(fields));
|
|
2534
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
2535
|
+
if (setValueAtPath(this.draft, `${rulePath}/${key}`, value)) {
|
|
2536
|
+
changedPayload = true;
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
} else {
|
|
2540
|
+
const defaultAction = extension.defaultAction;
|
|
2541
|
+
if (defaultAction) {
|
|
2542
|
+
keep = extension.actionField ?? "action";
|
|
2543
|
+
if (setValueAtPath(this.draft, `${rulePath}/${keep}`, defaultAction())) {
|
|
2544
|
+
changedPayload = true;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
const cleared = clearActionFields(ruleContainer, keep, keepFields);
|
|
2549
|
+
if (changedType || changedPayload || cleared) {
|
|
2332
2550
|
this.markChanged();
|
|
2333
2551
|
this.render();
|
|
2334
2552
|
}
|
|
@@ -2580,7 +2798,7 @@ var EditorControllerImpl = class {
|
|
|
2580
2798
|
if (ruleProposal.method) {
|
|
2581
2799
|
rule.method = ruleProposal.method;
|
|
2582
2800
|
}
|
|
2583
|
-
const actionField = ruleProposal.kind === "redirect"
|
|
2801
|
+
const actionField = ruleProposal.kind === "redirect" ? ruleProposal.kind : "action";
|
|
2584
2802
|
rule[actionField] = ruleProposal.action;
|
|
2585
2803
|
group.rules.push(rule);
|
|
2586
2804
|
this.markChanged();
|
|
@@ -3048,31 +3266,25 @@ var EditorControllerImpl = class {
|
|
|
3048
3266
|
const group = this.groupById(this.route.groupId);
|
|
3049
3267
|
if (!group) return;
|
|
3050
3268
|
const index = this.groupIndex(this.route.groupId);
|
|
3051
|
-
const name = displayName(group.name, "Unnamed group");
|
|
3052
3269
|
this.commandBar.append(
|
|
3053
3270
|
this.createCommandButton("Add rule", "add-rule", this.saving, {
|
|
3054
3271
|
groupId: this.route.groupId
|
|
3055
3272
|
}),
|
|
3056
3273
|
this.createCommandButton(
|
|
3057
|
-
|
|
3274
|
+
"Move group up",
|
|
3058
3275
|
"move-group-up",
|
|
3059
3276
|
this.saving || index <= 0,
|
|
3060
3277
|
{ groupId: this.route.groupId }
|
|
3061
3278
|
),
|
|
3062
3279
|
this.createCommandButton(
|
|
3063
|
-
|
|
3280
|
+
"Move group down",
|
|
3064
3281
|
"move-group-down",
|
|
3065
3282
|
this.saving || index >= this.draft.groups.length - 1,
|
|
3066
3283
|
{ groupId: this.route.groupId }
|
|
3067
3284
|
),
|
|
3068
|
-
this.createCommandButton(
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
this.saving,
|
|
3072
|
-
{
|
|
3073
|
-
groupId: this.route.groupId
|
|
3074
|
-
}
|
|
3075
|
-
)
|
|
3285
|
+
this.createCommandButton("Remove group", "remove-group", this.saving, {
|
|
3286
|
+
groupId: this.route.groupId
|
|
3287
|
+
})
|
|
3076
3288
|
);
|
|
3077
3289
|
}
|
|
3078
3290
|
renderProject() {
|
|
@@ -3306,7 +3518,9 @@ var EditorControllerImpl = class {
|
|
|
3306
3518
|
card.id = `rogatio-rule-${groupId}-${ruleId}`;
|
|
3307
3519
|
card.tabIndex = -1;
|
|
3308
3520
|
const heading = this.document.createElement("h3");
|
|
3521
|
+
heading.id = `${this.instanceId}-rule-title-${groupIndex}-${ruleIndex}`;
|
|
3309
3522
|
heading.textContent = ruleName;
|
|
3523
|
+
card.setAttribute("aria-labelledby", heading.id);
|
|
3310
3524
|
card.append(heading);
|
|
3311
3525
|
const fields = this.document.createElement("fieldset");
|
|
3312
3526
|
const legend = this.document.createElement("legend");
|
|
@@ -3318,23 +3532,18 @@ var EditorControllerImpl = class {
|
|
|
3318
3532
|
id.type = "text";
|
|
3319
3533
|
id.maxLength = 64;
|
|
3320
3534
|
id.value = safeText(rule.id);
|
|
3321
|
-
this.renderField(grid,
|
|
3535
|
+
this.renderField(grid, "Rule ID", `${rulePath}/id`, id);
|
|
3322
3536
|
const name = this.document.createElement("input");
|
|
3323
3537
|
name.type = "text";
|
|
3324
3538
|
name.maxLength = 100;
|
|
3325
3539
|
name.value = safeText(rule.name);
|
|
3326
|
-
this.renderField(
|
|
3327
|
-
grid,
|
|
3328
|
-
`Rule name for ${ruleName}`,
|
|
3329
|
-
`${rulePath}/name`,
|
|
3330
|
-
name
|
|
3331
|
-
);
|
|
3540
|
+
this.renderField(grid, "Rule name", `${rulePath}/name`, name);
|
|
3332
3541
|
const regex = this.document.createElement("textarea");
|
|
3333
3542
|
regex.maxLength = F2_MAX_URL_REGEX_LENGTH2;
|
|
3334
3543
|
regex.value = safeText(rule.urlRegex);
|
|
3335
3544
|
this.renderField(
|
|
3336
3545
|
grid,
|
|
3337
|
-
|
|
3546
|
+
"URL regular expression",
|
|
3338
3547
|
`${rulePath}/urlRegex`,
|
|
3339
3548
|
regex
|
|
3340
3549
|
);
|
|
@@ -3357,7 +3566,7 @@ var EditorControllerImpl = class {
|
|
|
3357
3566
|
groupIndex,
|
|
3358
3567
|
ruleIndex
|
|
3359
3568
|
);
|
|
3360
|
-
this.renderResourceTypes(card, rule, rulePath
|
|
3569
|
+
this.renderResourceTypes(card, rule, rulePath);
|
|
3361
3570
|
const matcherFields = this.document.createElement("fieldset");
|
|
3362
3571
|
const matcherLegend = this.document.createElement("legend");
|
|
3363
3572
|
matcherLegend.textContent = "Request constraints";
|
|
@@ -3370,12 +3579,7 @@ var EditorControllerImpl = class {
|
|
|
3370
3579
|
priority.max = "1000";
|
|
3371
3580
|
priority.step = "1";
|
|
3372
3581
|
priority.value = typeof rule.priority === "number" && Number.isFinite(rule.priority) ? String(rule.priority) : safeText(rule.priority);
|
|
3373
|
-
this.renderField(
|
|
3374
|
-
matcherGrid,
|
|
3375
|
-
`Priority for ${ruleName}`,
|
|
3376
|
-
`${rulePath}/priority`,
|
|
3377
|
-
priority
|
|
3378
|
-
);
|
|
3582
|
+
this.renderField(matcherGrid, "Priority", `${rulePath}/priority`, priority);
|
|
3379
3583
|
const method = this.document.createElement("select");
|
|
3380
3584
|
const anyMethod = this.document.createElement("option");
|
|
3381
3585
|
anyMethod.value = "";
|
|
@@ -3388,21 +3592,35 @@ var EditorControllerImpl = class {
|
|
|
3388
3592
|
method.append(option);
|
|
3389
3593
|
}
|
|
3390
3594
|
method.value = safeText(rule.method);
|
|
3391
|
-
this.renderField(
|
|
3392
|
-
matcherGrid,
|
|
3393
|
-
`Method for ${ruleName}`,
|
|
3394
|
-
`${rulePath}/method`,
|
|
3395
|
-
method
|
|
3396
|
-
);
|
|
3595
|
+
this.renderField(matcherGrid, "Method", `${rulePath}/method`, method);
|
|
3397
3596
|
matcherFields.append(matcherGrid);
|
|
3398
3597
|
card.append(matcherFields);
|
|
3598
|
+
if (!isBodyRuleType(rule.type)) {
|
|
3599
|
+
const redactFieldset = this.document.createElement("fieldset");
|
|
3600
|
+
const redactLegend = this.document.createElement("legend");
|
|
3601
|
+
redactLegend.textContent = "Match logging";
|
|
3602
|
+
redactFieldset.append(redactLegend);
|
|
3603
|
+
const redactGrid = this.document.createElement("div");
|
|
3604
|
+
redactGrid.dataset.editorFields = "true";
|
|
3605
|
+
const redactCheckbox = this.document.createElement("input");
|
|
3606
|
+
redactCheckbox.type = "checkbox";
|
|
3607
|
+
redactCheckbox.checked = rule.redactSensitiveInLogs === true;
|
|
3608
|
+
this.renderField(
|
|
3609
|
+
redactGrid,
|
|
3610
|
+
"Redact sensitive fields in logs",
|
|
3611
|
+
`${rulePath}/redactSensitiveInLogs`,
|
|
3612
|
+
redactCheckbox
|
|
3613
|
+
);
|
|
3614
|
+
redactFieldset.append(redactGrid);
|
|
3615
|
+
card.append(redactFieldset);
|
|
3616
|
+
}
|
|
3399
3617
|
if (this.extensions.length > 0) {
|
|
3400
3618
|
const currentType = this.extensions.find(
|
|
3401
3619
|
(e) => e.matches(rule)
|
|
3402
3620
|
)?.id ?? safeText(rule.type);
|
|
3403
3621
|
const typeFieldset = this.document.createElement("fieldset");
|
|
3404
3622
|
const typeLegend = this.document.createElement("legend");
|
|
3405
|
-
typeLegend.textContent =
|
|
3623
|
+
typeLegend.textContent = "Rule type";
|
|
3406
3624
|
const typeField = this.document.createElement("div");
|
|
3407
3625
|
typeField.dataset.editorField = "true";
|
|
3408
3626
|
const typeLabel = this.document.createElement("label");
|
|
@@ -3440,26 +3658,21 @@ var EditorControllerImpl = class {
|
|
|
3440
3658
|
actions.dataset.ruleActions = "true";
|
|
3441
3659
|
actions.append(
|
|
3442
3660
|
this.createCommandButton(
|
|
3443
|
-
|
|
3661
|
+
"Move rule up",
|
|
3444
3662
|
"move-rule-up",
|
|
3445
3663
|
this.saving || ruleIndex <= 0,
|
|
3446
3664
|
{ groupId, ruleId }
|
|
3447
3665
|
),
|
|
3448
3666
|
this.createCommandButton(
|
|
3449
|
-
|
|
3667
|
+
"Move rule down",
|
|
3450
3668
|
"move-rule-down",
|
|
3451
3669
|
this.saving || ruleIndex >= group.rules.length - 1,
|
|
3452
3670
|
{ groupId, ruleId }
|
|
3453
3671
|
),
|
|
3454
|
-
this.createCommandButton(
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
{
|
|
3459
|
-
groupId,
|
|
3460
|
-
ruleId
|
|
3461
|
-
}
|
|
3462
|
-
)
|
|
3672
|
+
this.createCommandButton("Remove rule", "remove-rule", this.saving, {
|
|
3673
|
+
groupId,
|
|
3674
|
+
ruleId
|
|
3675
|
+
})
|
|
3463
3676
|
);
|
|
3464
3677
|
card.append(actions);
|
|
3465
3678
|
return card;
|
|
@@ -3505,10 +3718,10 @@ var EditorControllerImpl = class {
|
|
|
3505
3718
|
);
|
|
3506
3719
|
parent.append(fieldset);
|
|
3507
3720
|
}
|
|
3508
|
-
renderResourceTypes(parent, rule, rulePath
|
|
3721
|
+
renderResourceTypes(parent, rule, rulePath) {
|
|
3509
3722
|
const fieldset = this.document.createElement("fieldset");
|
|
3510
3723
|
const legend = this.document.createElement("legend");
|
|
3511
|
-
legend.textContent =
|
|
3724
|
+
legend.textContent = "Resource types";
|
|
3512
3725
|
fieldset.append(legend);
|
|
3513
3726
|
const checks = this.document.createElement("div");
|
|
3514
3727
|
checks.dataset.editorChecks = "true";
|
|
@@ -3886,10 +4099,10 @@ var EditorControllerImpl = class {
|
|
|
3886
4099
|
}
|
|
3887
4100
|
};
|
|
3888
4101
|
function isSaveSuccess(value) {
|
|
3889
|
-
return
|
|
4102
|
+
return isRecord4(value) && value.ok === true;
|
|
3890
4103
|
}
|
|
3891
4104
|
function saveFailureDiagnostic(value) {
|
|
3892
|
-
if (!
|
|
4105
|
+
if (!isRecord4(value)) {
|
|
3893
4106
|
return diagnostic(
|
|
3894
4107
|
"editor.save-failed",
|
|
3895
4108
|
"",
|
|
@@ -3936,282 +4149,11 @@ function createEditor(options) {
|
|
|
3936
4149
|
const extensions = normalizeExtensions(options.ruleTypes);
|
|
3937
4150
|
return new EditorControllerImpl(options, project, extensions);
|
|
3938
4151
|
}
|
|
3939
|
-
|
|
3940
|
-
// packages/editor/src/rule-types/header.ts
|
|
3941
|
-
var FORBIDDEN_REQUEST_HEADERS2 = Object.freeze([
|
|
3942
|
-
"accept-charset",
|
|
3943
|
-
"accept-encoding",
|
|
3944
|
-
"access-control-request-headers",
|
|
3945
|
-
"access-control-request-method",
|
|
3946
|
-
"connection",
|
|
3947
|
-
"content-length",
|
|
3948
|
-
"cookie",
|
|
3949
|
-
"cookie2",
|
|
3950
|
-
"date",
|
|
3951
|
-
"dnt",
|
|
3952
|
-
"expect",
|
|
3953
|
-
"host",
|
|
3954
|
-
"keep-alive",
|
|
3955
|
-
"origin",
|
|
3956
|
-
"proxy-authenticate",
|
|
3957
|
-
"proxy-authorization",
|
|
3958
|
-
"te",
|
|
3959
|
-
"trailer",
|
|
3960
|
-
"transfer-encoding",
|
|
3961
|
-
"upgrade",
|
|
3962
|
-
"via"
|
|
3963
|
-
]);
|
|
3964
|
-
var FORBIDDEN_RESPONSE_HEADERS2 = Object.freeze([
|
|
3965
|
-
"connection",
|
|
3966
|
-
"content-encoding",
|
|
3967
|
-
"content-length",
|
|
3968
|
-
"date",
|
|
3969
|
-
"keep-alive",
|
|
3970
|
-
"proxy-authenticate",
|
|
3971
|
-
"proxy-authorization",
|
|
3972
|
-
"set-cookie",
|
|
3973
|
-
"set-cookie2",
|
|
3974
|
-
"te",
|
|
3975
|
-
"trailer",
|
|
3976
|
-
"transfer-encoding",
|
|
3977
|
-
"upgrade",
|
|
3978
|
-
"via"
|
|
3979
|
-
]);
|
|
3980
|
-
var FORBIDDEN_REQUEST_PREFIXES2 = Object.freeze(["proxy-", "sec-"]);
|
|
3981
|
-
function isForbiddenHeader(name, direction) {
|
|
3982
|
-
const normalized = name.toLowerCase();
|
|
3983
|
-
const forbidden = direction === "request" ? FORBIDDEN_REQUEST_HEADERS2 : FORBIDDEN_RESPONSE_HEADERS2;
|
|
3984
|
-
return forbidden.includes(normalized) || direction === "request" && FORBIDDEN_REQUEST_PREFIXES2.some(
|
|
3985
|
-
(prefix) => normalized.startsWith(prefix)
|
|
3986
|
-
);
|
|
3987
|
-
}
|
|
3988
|
-
var HEADER_DIRECTIONS = [
|
|
3989
|
-
"request",
|
|
3990
|
-
"response"
|
|
3991
|
-
];
|
|
3992
|
-
var HEADER_OPERATIONS = [
|
|
3993
|
-
"set",
|
|
3994
|
-
"append",
|
|
3995
|
-
"remove"
|
|
3996
|
-
];
|
|
3997
|
-
function createSelect(document2, options, value, onChange) {
|
|
3998
|
-
const select = document2.createElement("select");
|
|
3999
|
-
for (const option of options) {
|
|
4000
|
-
const opt = document2.createElement("option");
|
|
4001
|
-
opt.value = option;
|
|
4002
|
-
opt.textContent = option;
|
|
4003
|
-
if (option === value) opt.selected = true;
|
|
4004
|
-
select.append(opt);
|
|
4005
|
-
}
|
|
4006
|
-
select.addEventListener("change", () => onChange(select.value));
|
|
4007
|
-
return select;
|
|
4008
|
-
}
|
|
4009
|
-
function createInput(document2, value, onChange, maxLength) {
|
|
4010
|
-
const input = document2.createElement("input");
|
|
4011
|
-
input.type = "text";
|
|
4012
|
-
input.value = value;
|
|
4013
|
-
if (maxLength !== void 0) input.maxLength = maxLength;
|
|
4014
|
-
input.addEventListener("input", () => onChange(input.value));
|
|
4015
|
-
return input;
|
|
4016
|
-
}
|
|
4017
|
-
function createHeaderRuleType() {
|
|
4018
|
-
return {
|
|
4019
|
-
id: "header",
|
|
4020
|
-
label: "Header",
|
|
4021
|
-
matches(rule) {
|
|
4022
|
-
return rule.type === "header";
|
|
4023
|
-
},
|
|
4024
|
-
mount(context) {
|
|
4025
|
-
const document2 = context.document;
|
|
4026
|
-
const fieldset = document2.createElement("fieldset");
|
|
4027
|
-
const legend = document2.createElement("legend");
|
|
4028
|
-
legend.textContent = "Header rule";
|
|
4029
|
-
fieldset.append(legend);
|
|
4030
|
-
const directionLabel = document2.createElement("label");
|
|
4031
|
-
directionLabel.textContent = "Direction";
|
|
4032
|
-
const directionValue = context.getField("headerDirection") ?? "request";
|
|
4033
|
-
const directionSelect = createSelect(
|
|
4034
|
-
document2,
|
|
4035
|
-
HEADER_DIRECTIONS,
|
|
4036
|
-
directionValue,
|
|
4037
|
-
(value) => {
|
|
4038
|
-
context.setField("headerDirection", value);
|
|
4039
|
-
}
|
|
4040
|
-
);
|
|
4041
|
-
context.registerControl("/headerDirection", directionSelect);
|
|
4042
|
-
directionLabel.append(directionSelect);
|
|
4043
|
-
fieldset.append(directionLabel);
|
|
4044
|
-
const operationLabel = document2.createElement("label");
|
|
4045
|
-
operationLabel.textContent = "Operation";
|
|
4046
|
-
const operationValue = context.getField("headerOperation") ?? "set";
|
|
4047
|
-
const operationSelect = createSelect(
|
|
4048
|
-
document2,
|
|
4049
|
-
HEADER_OPERATIONS,
|
|
4050
|
-
operationValue,
|
|
4051
|
-
(value) => {
|
|
4052
|
-
context.setField("headerOperation", value);
|
|
4053
|
-
}
|
|
4054
|
-
);
|
|
4055
|
-
context.registerControl("/headerOperation", operationSelect);
|
|
4056
|
-
operationLabel.append(operationSelect);
|
|
4057
|
-
fieldset.append(operationLabel);
|
|
4058
|
-
const nameLabel = document2.createElement("label");
|
|
4059
|
-
nameLabel.textContent = "Header name";
|
|
4060
|
-
const nameValue = context.getField("headerName") ?? "";
|
|
4061
|
-
const nameInput = createInput(
|
|
4062
|
-
document2,
|
|
4063
|
-
nameValue,
|
|
4064
|
-
(value) => {
|
|
4065
|
-
context.setField("headerName", value);
|
|
4066
|
-
},
|
|
4067
|
-
256
|
|
4068
|
-
);
|
|
4069
|
-
context.registerControl("/headerName", nameInput);
|
|
4070
|
-
nameLabel.append(nameInput);
|
|
4071
|
-
fieldset.append(nameLabel);
|
|
4072
|
-
const valueLabel = document2.createElement("label");
|
|
4073
|
-
valueLabel.textContent = "Header value";
|
|
4074
|
-
const valueValue = context.getField("headerValue") ?? "";
|
|
4075
|
-
const valueInput = createInput(
|
|
4076
|
-
document2,
|
|
4077
|
-
valueValue,
|
|
4078
|
-
(value) => {
|
|
4079
|
-
context.setField("headerValue", value);
|
|
4080
|
-
},
|
|
4081
|
-
4096
|
|
4082
|
-
);
|
|
4083
|
-
context.registerControl("/headerValue", valueInput);
|
|
4084
|
-
valueLabel.append(valueInput);
|
|
4085
|
-
fieldset.append(valueLabel);
|
|
4086
|
-
context.container.append(fieldset);
|
|
4087
|
-
return {
|
|
4088
|
-
destroy() {
|
|
4089
|
-
directionSelect.removeEventListener("change", () => {
|
|
4090
|
-
});
|
|
4091
|
-
operationSelect.removeEventListener("change", () => {
|
|
4092
|
-
});
|
|
4093
|
-
nameInput.removeEventListener("input", () => {
|
|
4094
|
-
});
|
|
4095
|
-
valueInput.removeEventListener("input", () => {
|
|
4096
|
-
});
|
|
4097
|
-
}
|
|
4098
|
-
};
|
|
4099
|
-
},
|
|
4100
|
-
validate(rule, rulePath) {
|
|
4101
|
-
const diagnostics = [];
|
|
4102
|
-
const direction = rule.headerDirection;
|
|
4103
|
-
const operation = rule.headerOperation;
|
|
4104
|
-
const headerName = rule.headerName;
|
|
4105
|
-
const headerValue = rule.headerValue;
|
|
4106
|
-
if (!direction || !HEADER_DIRECTIONS.includes(direction)) {
|
|
4107
|
-
diagnostics.push({
|
|
4108
|
-
code: "schema.invalid-value",
|
|
4109
|
-
severity: "error",
|
|
4110
|
-
path: `${rulePath}/headerDirection`,
|
|
4111
|
-
message: 'Direction must be "request" or "response".'
|
|
4112
|
-
});
|
|
4113
|
-
}
|
|
4114
|
-
if (!operation || !HEADER_OPERATIONS.includes(operation)) {
|
|
4115
|
-
diagnostics.push({
|
|
4116
|
-
code: "schema.invalid-value",
|
|
4117
|
-
severity: "error",
|
|
4118
|
-
path: `${rulePath}/headerOperation`,
|
|
4119
|
-
message: 'Operation must be "set", "append", or "remove".'
|
|
4120
|
-
});
|
|
4121
|
-
}
|
|
4122
|
-
if (typeof headerName !== "string" || headerName.length === 0 || headerName.length > 256) {
|
|
4123
|
-
diagnostics.push({
|
|
4124
|
-
code: "schema.out-of-range",
|
|
4125
|
-
severity: "error",
|
|
4126
|
-
path: `${rulePath}/headerName`,
|
|
4127
|
-
message: "Header name must be 1-256 characters."
|
|
4128
|
-
});
|
|
4129
|
-
} else if (direction && isForbiddenHeader(headerName, direction)) {
|
|
4130
|
-
diagnostics.push({
|
|
4131
|
-
code: "extension.forbidden-header",
|
|
4132
|
-
severity: "error",
|
|
4133
|
-
path: `${rulePath}/headerName`,
|
|
4134
|
-
message: `Header "${headerName}" is forbidden for ${direction} headers.`
|
|
4135
|
-
});
|
|
4136
|
-
}
|
|
4137
|
-
if ((operation === "set" || operation === "append") && typeof headerValue !== "string") {
|
|
4138
|
-
diagnostics.push({
|
|
4139
|
-
code: "schema.required",
|
|
4140
|
-
severity: "error",
|
|
4141
|
-
path: `${rulePath}/headerValue`,
|
|
4142
|
-
message: "Header value is required for set and append operations."
|
|
4143
|
-
});
|
|
4144
|
-
} else if ((operation === "set" || operation === "append") && typeof headerValue === "string" && headerValue.length > 4096) {
|
|
4145
|
-
diagnostics.push({
|
|
4146
|
-
code: "schema.out-of-range",
|
|
4147
|
-
severity: "error",
|
|
4148
|
-
path: `${rulePath}/headerValue`,
|
|
4149
|
-
message: "Header value must be at most 4096 characters."
|
|
4150
|
-
});
|
|
4151
|
-
}
|
|
4152
|
-
if (operation === "remove" && headerValue !== void 0) {
|
|
4153
|
-
diagnostics.push({
|
|
4154
|
-
code: "schema.unexpected",
|
|
4155
|
-
severity: "error",
|
|
4156
|
-
path: `${rulePath}/headerValue`,
|
|
4157
|
-
message: "Header value must not be provided for remove operation."
|
|
4158
|
-
});
|
|
4159
|
-
}
|
|
4160
|
-
return diagnostics;
|
|
4161
|
-
}
|
|
4162
|
-
};
|
|
4163
|
-
}
|
|
4164
|
-
|
|
4165
|
-
// packages/editor/src/rule-types/redirect.ts
|
|
4166
|
-
function createRedirectRuleType() {
|
|
4167
|
-
return {
|
|
4168
|
-
id: "redirect",
|
|
4169
|
-
label: "Redirect",
|
|
4170
|
-
matches(rule) {
|
|
4171
|
-
return rule.type === "redirect";
|
|
4172
|
-
},
|
|
4173
|
-
mount(context) {
|
|
4174
|
-
const document2 = context.document;
|
|
4175
|
-
const label = document2.createElement("label");
|
|
4176
|
-
label.textContent = "Redirect destination URL";
|
|
4177
|
-
const input = document2.createElement("input");
|
|
4178
|
-
input.type = "text";
|
|
4179
|
-
input.maxLength = 2048;
|
|
4180
|
-
const existing = context.getField("redirect.destination");
|
|
4181
|
-
input.value = typeof existing === "string" ? existing : "";
|
|
4182
|
-
input.addEventListener("input", () => {
|
|
4183
|
-
context.setField("redirect.destination", input.value);
|
|
4184
|
-
});
|
|
4185
|
-
context.registerControl("/redirect/destination", input);
|
|
4186
|
-
label.append(input);
|
|
4187
|
-
context.container.append(label);
|
|
4188
|
-
return {
|
|
4189
|
-
destroy() {
|
|
4190
|
-
input.removeEventListener("input", () => {
|
|
4191
|
-
});
|
|
4192
|
-
}
|
|
4193
|
-
};
|
|
4194
|
-
},
|
|
4195
|
-
validate(rule, rulePath) {
|
|
4196
|
-
const redirect = rule.redirect;
|
|
4197
|
-
const destination = redirect !== null && typeof redirect === "object" && typeof redirect.destination === "string" ? redirect.destination : "";
|
|
4198
|
-
const urlRegex = typeof rule.urlRegex === "string" ? rule.urlRegex : "";
|
|
4199
|
-
const issues = validateRedirectDestination(destination, urlRegex);
|
|
4200
|
-
return issues.map((issue) => ({
|
|
4201
|
-
code: `schema.${issue.code}`,
|
|
4202
|
-
severity: "error",
|
|
4203
|
-
path: `${rulePath}/redirect/destination`,
|
|
4204
|
-
message: issue.message
|
|
4205
|
-
}));
|
|
4206
|
-
}
|
|
4207
|
-
};
|
|
4208
|
-
}
|
|
4209
4152
|
export {
|
|
4210
4153
|
EditorInitializationError,
|
|
4211
4154
|
builtInRuleTypes,
|
|
4212
4155
|
createEditor,
|
|
4213
4156
|
createHeaderRuleType,
|
|
4214
|
-
createMockRuleType,
|
|
4215
4157
|
createRedirectRuleType,
|
|
4216
4158
|
createRequestBodyRuleType,
|
|
4217
4159
|
createResponseBodyRuleType,
|