@jaredwray/mockhttp 1.2.0 → 1.3.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/dist/index.d.ts +6 -0
- package/dist/index.js +856 -15
- package/package.json +16 -11
package/dist/index.d.ts
CHANGED
|
@@ -132,6 +132,7 @@ type HttpBinOptions = {
|
|
|
132
132
|
anything?: boolean;
|
|
133
133
|
auth?: boolean;
|
|
134
134
|
images?: boolean;
|
|
135
|
+
dynamicData?: boolean;
|
|
135
136
|
};
|
|
136
137
|
type MockHttpOptions = {
|
|
137
138
|
/**
|
|
@@ -347,6 +348,11 @@ declare class MockHttp extends Hookified {
|
|
|
347
348
|
* @param fastifyInstance - the server instance to register the routes on.
|
|
348
349
|
*/
|
|
349
350
|
registerImageRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
|
|
351
|
+
/**
|
|
352
|
+
* Register the dynamic data routes.
|
|
353
|
+
* @param fastifyInstance - the server instance to register the routes on.
|
|
354
|
+
*/
|
|
355
|
+
registerDynamicDataRoutes(fastifyInstance?: FastifyInstance): Promise<void>;
|
|
350
356
|
}
|
|
351
357
|
|
|
352
358
|
declare const start: () => Promise<MockHttp>;
|
package/dist/index.js
CHANGED
|
@@ -712,6 +712,808 @@ var postCookieRoute = /* @__PURE__ */ __name((fastify) => {
|
|
|
712
712
|
});
|
|
713
713
|
}, "postCookieRoute");
|
|
714
714
|
|
|
715
|
+
// src/routes/dynamic-data/base64.ts
|
|
716
|
+
var base64Schema = {
|
|
717
|
+
description: "Decodes base64url-encoded string",
|
|
718
|
+
tags: [
|
|
719
|
+
"Dynamic Data"
|
|
720
|
+
],
|
|
721
|
+
params: {
|
|
722
|
+
type: "object",
|
|
723
|
+
properties: {
|
|
724
|
+
value: {
|
|
725
|
+
type: "string",
|
|
726
|
+
description: "Base64 encoded value to decode"
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
required: [
|
|
730
|
+
"value"
|
|
731
|
+
]
|
|
732
|
+
},
|
|
733
|
+
response: {
|
|
734
|
+
200: {
|
|
735
|
+
type: "string",
|
|
736
|
+
description: "Decoded value"
|
|
737
|
+
},
|
|
738
|
+
400: {
|
|
739
|
+
type: "object",
|
|
740
|
+
properties: {
|
|
741
|
+
error: {
|
|
742
|
+
type: "string"
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
function isValidBase64(str) {
|
|
749
|
+
const base64Regex = /^[A-Za-z0-9+/\-_]*={0,2}$/;
|
|
750
|
+
if (!base64Regex.test(str)) {
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
const strippedLength = str.replace(/=+$/, "").length;
|
|
754
|
+
return strippedLength % 4 !== 1;
|
|
755
|
+
}
|
|
756
|
+
__name(isValidBase64, "isValidBase64");
|
|
757
|
+
var base64Route = /* @__PURE__ */ __name((fastify) => {
|
|
758
|
+
fastify.get("/base64/:value", {
|
|
759
|
+
schema: base64Schema
|
|
760
|
+
}, async (request, reply) => {
|
|
761
|
+
const { value } = request.params;
|
|
762
|
+
if (!isValidBase64(value)) {
|
|
763
|
+
return reply.code(400).send({
|
|
764
|
+
error: "Incorrect Base64 data"
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
const normalizedValue = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
768
|
+
const decoded = Buffer.from(normalizedValue, "base64").toString("utf-8");
|
|
769
|
+
return reply.header("Content-Type", "text/html; charset=utf-8").send(decoded);
|
|
770
|
+
});
|
|
771
|
+
}, "base64Route");
|
|
772
|
+
|
|
773
|
+
// src/routes/dynamic-data/bytes.ts
|
|
774
|
+
import { randomBytes } from "crypto";
|
|
775
|
+
var bytesSchema = {
|
|
776
|
+
description: "Returns n random bytes generated with given seed",
|
|
777
|
+
tags: [
|
|
778
|
+
"Dynamic Data"
|
|
779
|
+
],
|
|
780
|
+
params: {
|
|
781
|
+
type: "object",
|
|
782
|
+
properties: {
|
|
783
|
+
n: {
|
|
784
|
+
type: "string",
|
|
785
|
+
description: "Number of bytes to generate"
|
|
786
|
+
}
|
|
787
|
+
},
|
|
788
|
+
required: [
|
|
789
|
+
"n"
|
|
790
|
+
]
|
|
791
|
+
},
|
|
792
|
+
querystring: {
|
|
793
|
+
type: "object",
|
|
794
|
+
properties: {
|
|
795
|
+
seed: {
|
|
796
|
+
type: "string",
|
|
797
|
+
description: "Seed for random number generation"
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
response: {
|
|
802
|
+
200: {
|
|
803
|
+
type: "string",
|
|
804
|
+
description: "Random binary data"
|
|
805
|
+
},
|
|
806
|
+
400: {
|
|
807
|
+
type: "object",
|
|
808
|
+
properties: {
|
|
809
|
+
error: {
|
|
810
|
+
type: "string"
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
function seededRandom(seed) {
|
|
817
|
+
let state = seed;
|
|
818
|
+
return () => {
|
|
819
|
+
state = state * 1103515245 + 12345 & 2147483647;
|
|
820
|
+
return state / 2147483647;
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
__name(seededRandom, "seededRandom");
|
|
824
|
+
function generateSeededBytes(n, seed) {
|
|
825
|
+
const random = seededRandom(seed);
|
|
826
|
+
const bytes = Buffer.alloc(n);
|
|
827
|
+
for (let i = 0; i < n; i++) {
|
|
828
|
+
bytes[i] = Math.floor(random() * 256);
|
|
829
|
+
}
|
|
830
|
+
return bytes;
|
|
831
|
+
}
|
|
832
|
+
__name(generateSeededBytes, "generateSeededBytes");
|
|
833
|
+
var MAX_BYTES = 100 * 1024;
|
|
834
|
+
var bytesRoute = /* @__PURE__ */ __name((fastify) => {
|
|
835
|
+
fastify.get("/bytes/:n", {
|
|
836
|
+
schema: bytesSchema
|
|
837
|
+
}, async (request, reply) => {
|
|
838
|
+
const n = Number.parseInt(request.params.n, 10);
|
|
839
|
+
if (Number.isNaN(n) || n < 0) {
|
|
840
|
+
return reply.code(400).send({
|
|
841
|
+
error: "n must be a non-negative integer"
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
const limitedN = Math.min(n, MAX_BYTES);
|
|
845
|
+
const seedParam = request.query.seed;
|
|
846
|
+
let bytes;
|
|
847
|
+
if (seedParam !== void 0) {
|
|
848
|
+
const seed = Number.parseInt(seedParam, 10);
|
|
849
|
+
if (Number.isNaN(seed)) {
|
|
850
|
+
return reply.code(400).send({
|
|
851
|
+
error: "seed must be an integer"
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
bytes = generateSeededBytes(limitedN, seed);
|
|
855
|
+
} else {
|
|
856
|
+
bytes = randomBytes(limitedN);
|
|
857
|
+
}
|
|
858
|
+
return reply.header("Content-Type", "application/octet-stream").header("Content-Length", bytes.length).send(bytes);
|
|
859
|
+
});
|
|
860
|
+
}, "bytesRoute");
|
|
861
|
+
|
|
862
|
+
// src/routes/dynamic-data/delay.ts
|
|
863
|
+
var delaySchema = {
|
|
864
|
+
description: "Returns a delayed response (max of 10 seconds)",
|
|
865
|
+
tags: [
|
|
866
|
+
"Dynamic Data"
|
|
867
|
+
],
|
|
868
|
+
params: {
|
|
869
|
+
type: "object",
|
|
870
|
+
properties: {
|
|
871
|
+
delay: {
|
|
872
|
+
type: "string",
|
|
873
|
+
description: "Delay in seconds (max 10)"
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
required: [
|
|
877
|
+
"delay"
|
|
878
|
+
]
|
|
879
|
+
},
|
|
880
|
+
querystring: {
|
|
881
|
+
type: "object",
|
|
882
|
+
additionalProperties: true
|
|
883
|
+
},
|
|
884
|
+
response: {
|
|
885
|
+
200: {
|
|
886
|
+
type: "object",
|
|
887
|
+
properties: {
|
|
888
|
+
args: {
|
|
889
|
+
type: "object"
|
|
890
|
+
},
|
|
891
|
+
data: {
|
|
892
|
+
type: "string"
|
|
893
|
+
},
|
|
894
|
+
files: {
|
|
895
|
+
type: "object"
|
|
896
|
+
},
|
|
897
|
+
form: {
|
|
898
|
+
type: "object"
|
|
899
|
+
},
|
|
900
|
+
headers: {
|
|
901
|
+
type: "object"
|
|
902
|
+
},
|
|
903
|
+
origin: {
|
|
904
|
+
type: "string"
|
|
905
|
+
},
|
|
906
|
+
url: {
|
|
907
|
+
type: "string"
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
400: {
|
|
912
|
+
type: "object",
|
|
913
|
+
properties: {
|
|
914
|
+
error: {
|
|
915
|
+
type: "string"
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
var MAX_DELAY = 10;
|
|
922
|
+
function sleep(ms) {
|
|
923
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
924
|
+
}
|
|
925
|
+
__name(sleep, "sleep");
|
|
926
|
+
var delayRoute = /* @__PURE__ */ __name((fastify) => {
|
|
927
|
+
const handler = /* @__PURE__ */ __name(async (request, reply) => {
|
|
928
|
+
const delay = Number.parseFloat(request.params.delay);
|
|
929
|
+
if (Number.isNaN(delay) || delay < 0) {
|
|
930
|
+
return reply.code(400).send({
|
|
931
|
+
error: "delay must be a non-negative number"
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
const actualDelay = Math.min(delay, MAX_DELAY);
|
|
935
|
+
await sleep(actualDelay * 1e3);
|
|
936
|
+
const { protocol } = request;
|
|
937
|
+
const host = request.headers.host || "localhost";
|
|
938
|
+
return {
|
|
939
|
+
/* v8 ignore next -- @preserve */
|
|
940
|
+
args: request.query || {},
|
|
941
|
+
data: "",
|
|
942
|
+
files: {},
|
|
943
|
+
form: {},
|
|
944
|
+
headers: request.headers,
|
|
945
|
+
origin: request.ip,
|
|
946
|
+
url: `${protocol}://${host}${request.url}`
|
|
947
|
+
};
|
|
948
|
+
}, "handler");
|
|
949
|
+
fastify.get("/delay/:delay", {
|
|
950
|
+
schema: delaySchema
|
|
951
|
+
}, handler);
|
|
952
|
+
fastify.post("/delay/:delay", {
|
|
953
|
+
schema: delaySchema
|
|
954
|
+
}, handler);
|
|
955
|
+
fastify.put("/delay/:delay", {
|
|
956
|
+
schema: delaySchema
|
|
957
|
+
}, handler);
|
|
958
|
+
fastify.patch("/delay/:delay", {
|
|
959
|
+
schema: delaySchema
|
|
960
|
+
}, handler);
|
|
961
|
+
fastify.delete("/delay/:delay", {
|
|
962
|
+
schema: delaySchema
|
|
963
|
+
}, handler);
|
|
964
|
+
}, "delayRoute");
|
|
965
|
+
|
|
966
|
+
// src/routes/dynamic-data/drip.ts
|
|
967
|
+
var dripSchema = {
|
|
968
|
+
description: "Drips data over a duration after an optional initial delay, then (optionally) returns with the given status code",
|
|
969
|
+
tags: [
|
|
970
|
+
"Dynamic Data"
|
|
971
|
+
],
|
|
972
|
+
querystring: {
|
|
973
|
+
type: "object",
|
|
974
|
+
properties: {
|
|
975
|
+
duration: {
|
|
976
|
+
type: "string",
|
|
977
|
+
description: "Duration in seconds over which to drip the data (default: 2)"
|
|
978
|
+
},
|
|
979
|
+
numbytes: {
|
|
980
|
+
type: "string",
|
|
981
|
+
description: "Number of bytes to send (default: 10)"
|
|
982
|
+
},
|
|
983
|
+
code: {
|
|
984
|
+
type: "string",
|
|
985
|
+
description: "HTTP status code to return (default: 200)"
|
|
986
|
+
},
|
|
987
|
+
delay: {
|
|
988
|
+
type: "string",
|
|
989
|
+
description: "Initial delay in seconds before starting (default: 2)"
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
response: {
|
|
994
|
+
200: {
|
|
995
|
+
type: "string",
|
|
996
|
+
description: "Dripped data"
|
|
997
|
+
},
|
|
998
|
+
400: {
|
|
999
|
+
type: "object",
|
|
1000
|
+
properties: {
|
|
1001
|
+
error: {
|
|
1002
|
+
type: "string"
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
function sleep2(ms) {
|
|
1009
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1010
|
+
}
|
|
1011
|
+
__name(sleep2, "sleep");
|
|
1012
|
+
var DEFAULT_DURATION = 2;
|
|
1013
|
+
var DEFAULT_NUMBYTES = 10;
|
|
1014
|
+
var DEFAULT_CODE = 200;
|
|
1015
|
+
var DEFAULT_DELAY = 2;
|
|
1016
|
+
var MAX_DURATION = 10;
|
|
1017
|
+
var MAX_DELAY2 = 10;
|
|
1018
|
+
var MAX_BYTES2 = 10 * 1024 * 1024;
|
|
1019
|
+
var dripRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1020
|
+
fastify.get("/drip", {
|
|
1021
|
+
schema: dripSchema
|
|
1022
|
+
}, async (request, reply) => {
|
|
1023
|
+
const duration = request.query.duration ? Number.parseFloat(request.query.duration) : DEFAULT_DURATION;
|
|
1024
|
+
const numbytes = request.query.numbytes ? Number.parseInt(request.query.numbytes, 10) : DEFAULT_NUMBYTES;
|
|
1025
|
+
const code = request.query.code ? Number.parseInt(request.query.code, 10) : DEFAULT_CODE;
|
|
1026
|
+
const delay = request.query.delay ? Number.parseFloat(request.query.delay) : DEFAULT_DELAY;
|
|
1027
|
+
if (Number.isNaN(duration) || duration < 0) {
|
|
1028
|
+
return reply.code(400).send({
|
|
1029
|
+
error: "duration must be a non-negative number"
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
if (Number.isNaN(numbytes) || numbytes < 0) {
|
|
1033
|
+
return reply.code(400).send({
|
|
1034
|
+
error: "numbytes must be a non-negative integer"
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
if (Number.isNaN(code) || code < 100 || code > 599) {
|
|
1038
|
+
return reply.code(400).send({
|
|
1039
|
+
error: "code must be a valid HTTP status code"
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
if (Number.isNaN(delay) || delay < 0) {
|
|
1043
|
+
return reply.code(400).send({
|
|
1044
|
+
error: "delay must be a non-negative number"
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
const actualDuration = Math.min(duration, MAX_DURATION);
|
|
1048
|
+
const actualDelay = Math.min(delay, MAX_DELAY2);
|
|
1049
|
+
const actualBytes = Math.min(numbytes, MAX_BYTES2);
|
|
1050
|
+
if (actualDelay > 0) {
|
|
1051
|
+
await sleep2(actualDelay * 1e3);
|
|
1052
|
+
}
|
|
1053
|
+
reply.raw.writeHead(code, {
|
|
1054
|
+
"Content-Type": "application/octet-stream",
|
|
1055
|
+
"Content-Length": actualBytes.toString()
|
|
1056
|
+
});
|
|
1057
|
+
if (actualBytes === 0) {
|
|
1058
|
+
reply.raw.end();
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const intervalMs = actualBytes > 1 ? actualDuration * 1e3 / (actualBytes - 1) : 0;
|
|
1062
|
+
const byte = Buffer.from("*");
|
|
1063
|
+
for (let i = 0; i < actualBytes; i++) {
|
|
1064
|
+
reply.raw.write(byte);
|
|
1065
|
+
if (i < actualBytes - 1 && intervalMs > 0) {
|
|
1066
|
+
await sleep2(intervalMs);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
reply.raw.end();
|
|
1070
|
+
});
|
|
1071
|
+
}, "dripRoute");
|
|
1072
|
+
|
|
1073
|
+
// src/routes/dynamic-data/links.ts
|
|
1074
|
+
var linksSchema = {
|
|
1075
|
+
description: "Generate a page containing n links to other pages which do the same",
|
|
1076
|
+
tags: [
|
|
1077
|
+
"Dynamic Data"
|
|
1078
|
+
],
|
|
1079
|
+
params: {
|
|
1080
|
+
type: "object",
|
|
1081
|
+
properties: {
|
|
1082
|
+
n: {
|
|
1083
|
+
type: "string",
|
|
1084
|
+
description: "Number of links to generate"
|
|
1085
|
+
},
|
|
1086
|
+
offset: {
|
|
1087
|
+
type: "string",
|
|
1088
|
+
description: "Starting offset for pagination (default: 0)"
|
|
1089
|
+
}
|
|
1090
|
+
},
|
|
1091
|
+
required: [
|
|
1092
|
+
"n"
|
|
1093
|
+
]
|
|
1094
|
+
},
|
|
1095
|
+
response: {
|
|
1096
|
+
200: {
|
|
1097
|
+
type: "string",
|
|
1098
|
+
description: "HTML page with links"
|
|
1099
|
+
},
|
|
1100
|
+
400: {
|
|
1101
|
+
type: "object",
|
|
1102
|
+
properties: {
|
|
1103
|
+
error: {
|
|
1104
|
+
type: "string"
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
var MAX_LINKS = 200;
|
|
1111
|
+
var linksRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1112
|
+
fastify.get("/links/:n/:offset?", {
|
|
1113
|
+
schema: linksSchema
|
|
1114
|
+
}, async (request, reply) => {
|
|
1115
|
+
const n = Number.parseInt(request.params.n, 10);
|
|
1116
|
+
const offset = request.params.offset ? Number.parseInt(request.params.offset, 10) : 0;
|
|
1117
|
+
if (Number.isNaN(n) || n < 0) {
|
|
1118
|
+
return reply.code(400).send({
|
|
1119
|
+
error: "n must be a non-negative integer"
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
if (Number.isNaN(offset) || offset < 0) {
|
|
1123
|
+
return reply.code(400).send({
|
|
1124
|
+
error: "offset must be a non-negative integer"
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
const limitedN = Math.min(n, MAX_LINKS);
|
|
1128
|
+
const links = [];
|
|
1129
|
+
for (let i = 0; i < limitedN; i++) {
|
|
1130
|
+
if (i === offset) {
|
|
1131
|
+
links.push(`${i} `);
|
|
1132
|
+
} else {
|
|
1133
|
+
links.push(`<a href="/links/${limitedN}/${i}">${i}</a> `);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
const html = `<html>
|
|
1137
|
+
<head>
|
|
1138
|
+
<title>Links</title>
|
|
1139
|
+
</head>
|
|
1140
|
+
<body>
|
|
1141
|
+
${links.join("")}
|
|
1142
|
+
</body>
|
|
1143
|
+
</html>`;
|
|
1144
|
+
return reply.header("Content-Type", "text/html; charset=utf-8").send(html);
|
|
1145
|
+
});
|
|
1146
|
+
}, "linksRoute");
|
|
1147
|
+
|
|
1148
|
+
// src/routes/dynamic-data/range.ts
|
|
1149
|
+
var rangeSchema = {
|
|
1150
|
+
description: "Streams n random bytes generated, and supports HTTP Range requests",
|
|
1151
|
+
tags: [
|
|
1152
|
+
"Dynamic Data"
|
|
1153
|
+
],
|
|
1154
|
+
params: {
|
|
1155
|
+
type: "object",
|
|
1156
|
+
properties: {
|
|
1157
|
+
numbytes: {
|
|
1158
|
+
type: "string",
|
|
1159
|
+
description: "Number of bytes available"
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
required: [
|
|
1163
|
+
"numbytes"
|
|
1164
|
+
]
|
|
1165
|
+
},
|
|
1166
|
+
querystring: {
|
|
1167
|
+
type: "object",
|
|
1168
|
+
properties: {
|
|
1169
|
+
duration: {
|
|
1170
|
+
type: "string",
|
|
1171
|
+
description: "Delay before sending response in seconds"
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
},
|
|
1175
|
+
response: {
|
|
1176
|
+
200: {
|
|
1177
|
+
type: "string",
|
|
1178
|
+
description: "Binary data"
|
|
1179
|
+
},
|
|
1180
|
+
206: {
|
|
1181
|
+
type: "string",
|
|
1182
|
+
description: "Partial binary data"
|
|
1183
|
+
},
|
|
1184
|
+
400: {
|
|
1185
|
+
type: "object",
|
|
1186
|
+
properties: {
|
|
1187
|
+
error: {
|
|
1188
|
+
type: "string"
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
},
|
|
1192
|
+
416: {
|
|
1193
|
+
type: "object",
|
|
1194
|
+
properties: {
|
|
1195
|
+
error: {
|
|
1196
|
+
type: "string"
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
var MAX_BYTES3 = 100 * 1024;
|
|
1203
|
+
function sleep3(ms) {
|
|
1204
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1205
|
+
}
|
|
1206
|
+
__name(sleep3, "sleep");
|
|
1207
|
+
function generateBytes(n) {
|
|
1208
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyz";
|
|
1209
|
+
const bytes = Buffer.alloc(n);
|
|
1210
|
+
for (let i = 0; i < n; i++) {
|
|
1211
|
+
bytes[i] = alphabet.charCodeAt(i % alphabet.length);
|
|
1212
|
+
}
|
|
1213
|
+
return bytes;
|
|
1214
|
+
}
|
|
1215
|
+
__name(generateBytes, "generateBytes");
|
|
1216
|
+
function parseRangeHeader(rangeHeader, totalSize) {
|
|
1217
|
+
const match = rangeHeader.match(/^bytes=(.+)$/);
|
|
1218
|
+
if (!match) return null;
|
|
1219
|
+
const ranges = [];
|
|
1220
|
+
const parts = match[1].split(",");
|
|
1221
|
+
for (const part of parts) {
|
|
1222
|
+
const trimmedPart = part.trim();
|
|
1223
|
+
if (trimmedPart.startsWith("-")) {
|
|
1224
|
+
const suffix = Number.parseInt(trimmedPart.slice(1), 10);
|
|
1225
|
+
if (Number.isNaN(suffix)) return null;
|
|
1226
|
+
ranges.push({
|
|
1227
|
+
start: Math.max(0, totalSize - suffix),
|
|
1228
|
+
end: totalSize - 1
|
|
1229
|
+
});
|
|
1230
|
+
} else if (trimmedPart.endsWith("-")) {
|
|
1231
|
+
const start2 = Number.parseInt(trimmedPart.slice(0, -1), 10);
|
|
1232
|
+
if (Number.isNaN(start2)) return null;
|
|
1233
|
+
ranges.push({
|
|
1234
|
+
start: start2,
|
|
1235
|
+
end: totalSize - 1
|
|
1236
|
+
});
|
|
1237
|
+
} else {
|
|
1238
|
+
const [startStr, endStr] = trimmedPart.split("-");
|
|
1239
|
+
const start2 = Number.parseInt(startStr, 10);
|
|
1240
|
+
const end = Number.parseInt(endStr, 10);
|
|
1241
|
+
if (Number.isNaN(start2) || Number.isNaN(end)) return null;
|
|
1242
|
+
ranges.push({
|
|
1243
|
+
start: start2,
|
|
1244
|
+
end: Math.min(end, totalSize - 1)
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
for (const range of ranges) {
|
|
1249
|
+
if (range.start > range.end || range.start >= totalSize) {
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return ranges;
|
|
1254
|
+
}
|
|
1255
|
+
__name(parseRangeHeader, "parseRangeHeader");
|
|
1256
|
+
var rangeRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1257
|
+
fastify.get("/range/:numbytes", {
|
|
1258
|
+
schema: rangeSchema
|
|
1259
|
+
}, async (request, reply) => {
|
|
1260
|
+
const numbytes = Number.parseInt(request.params.numbytes, 10);
|
|
1261
|
+
if (Number.isNaN(numbytes) || numbytes < 0) {
|
|
1262
|
+
return reply.code(400).send({
|
|
1263
|
+
error: "numbytes must be a non-negative integer"
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
const limitedBytes = Math.min(numbytes, MAX_BYTES3);
|
|
1267
|
+
if (request.query.duration) {
|
|
1268
|
+
const duration = Number.parseFloat(request.query.duration);
|
|
1269
|
+
if (!Number.isNaN(duration) && duration > 0) {
|
|
1270
|
+
await sleep3(Math.min(duration, 10) * 1e3);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
const allBytes = generateBytes(limitedBytes);
|
|
1274
|
+
const rangeHeader = request.headers.range;
|
|
1275
|
+
if (!rangeHeader) {
|
|
1276
|
+
return reply.header("Content-Type", "application/octet-stream").header("Content-Length", allBytes.length).header("Accept-Ranges", "bytes").send(allBytes);
|
|
1277
|
+
}
|
|
1278
|
+
const ranges = parseRangeHeader(rangeHeader, limitedBytes);
|
|
1279
|
+
if (!ranges || ranges.length === 0) {
|
|
1280
|
+
return reply.code(416).header("Content-Range", `bytes */${limitedBytes}`).send({
|
|
1281
|
+
error: "Range Not Satisfiable"
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
if (ranges.length === 1) {
|
|
1285
|
+
const range = ranges[0];
|
|
1286
|
+
const chunk = allBytes.subarray(range.start, range.end + 1);
|
|
1287
|
+
return reply.code(206).header("Content-Type", "application/octet-stream").header("Content-Length", chunk.length).header("Accept-Ranges", "bytes").header("Content-Range", `bytes ${range.start}-${range.end}/${limitedBytes}`).send(chunk);
|
|
1288
|
+
}
|
|
1289
|
+
const boundary = "3d6b6a416f9b5";
|
|
1290
|
+
const parts = [];
|
|
1291
|
+
for (const range of ranges) {
|
|
1292
|
+
const chunk = allBytes.subarray(range.start, range.end + 1);
|
|
1293
|
+
const header = `\r
|
|
1294
|
+
--${boundary}\r
|
|
1295
|
+
Content-Type: application/octet-stream\r
|
|
1296
|
+
Content-Range: bytes ${range.start}-${range.end}/${limitedBytes}\r
|
|
1297
|
+
\r
|
|
1298
|
+
`;
|
|
1299
|
+
parts.push(Buffer.from(header));
|
|
1300
|
+
parts.push(chunk);
|
|
1301
|
+
}
|
|
1302
|
+
parts.push(Buffer.from(`\r
|
|
1303
|
+
--${boundary}--\r
|
|
1304
|
+
`));
|
|
1305
|
+
const multipartBody = Buffer.concat(parts);
|
|
1306
|
+
return reply.code(206).header("Content-Type", `multipart/byteranges; boundary=${boundary}`).header("Content-Length", multipartBody.length).send(multipartBody);
|
|
1307
|
+
});
|
|
1308
|
+
}, "rangeRoute");
|
|
1309
|
+
|
|
1310
|
+
// src/routes/dynamic-data/stream.ts
|
|
1311
|
+
var streamSchema = {
|
|
1312
|
+
description: "Stream n JSON responses",
|
|
1313
|
+
tags: [
|
|
1314
|
+
"Dynamic Data"
|
|
1315
|
+
],
|
|
1316
|
+
params: {
|
|
1317
|
+
type: "object",
|
|
1318
|
+
properties: {
|
|
1319
|
+
n: {
|
|
1320
|
+
type: "string",
|
|
1321
|
+
description: "Number of JSON responses to stream (max 100)"
|
|
1322
|
+
}
|
|
1323
|
+
},
|
|
1324
|
+
required: [
|
|
1325
|
+
"n"
|
|
1326
|
+
]
|
|
1327
|
+
},
|
|
1328
|
+
response: {
|
|
1329
|
+
200: {
|
|
1330
|
+
type: "string",
|
|
1331
|
+
description: "Newline-delimited JSON responses"
|
|
1332
|
+
},
|
|
1333
|
+
400: {
|
|
1334
|
+
type: "object",
|
|
1335
|
+
properties: {
|
|
1336
|
+
error: {
|
|
1337
|
+
type: "string"
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
var MAX_LINES = 100;
|
|
1344
|
+
var streamRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1345
|
+
fastify.get("/stream/:n", {
|
|
1346
|
+
schema: streamSchema
|
|
1347
|
+
}, async (request, reply) => {
|
|
1348
|
+
const n = Number.parseInt(request.params.n, 10);
|
|
1349
|
+
if (Number.isNaN(n) || n < 0) {
|
|
1350
|
+
return reply.code(400).send({
|
|
1351
|
+
error: "n must be a non-negative integer"
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
const lines = Math.min(n, MAX_LINES);
|
|
1355
|
+
const { protocol } = request;
|
|
1356
|
+
const host = request.headers.host || "localhost";
|
|
1357
|
+
reply.raw.writeHead(200, {
|
|
1358
|
+
"Content-Type": "application/json",
|
|
1359
|
+
"Transfer-Encoding": "chunked"
|
|
1360
|
+
});
|
|
1361
|
+
for (let i = 0; i < lines; i++) {
|
|
1362
|
+
const data = {
|
|
1363
|
+
id: i,
|
|
1364
|
+
/* v8 ignore next -- @preserve */
|
|
1365
|
+
args: request.query || {},
|
|
1366
|
+
headers: request.headers,
|
|
1367
|
+
origin: request.ip,
|
|
1368
|
+
url: `${protocol}://${host}${request.url}`
|
|
1369
|
+
};
|
|
1370
|
+
reply.raw.write(`${JSON.stringify(data)}
|
|
1371
|
+
`);
|
|
1372
|
+
}
|
|
1373
|
+
reply.raw.end();
|
|
1374
|
+
});
|
|
1375
|
+
}, "streamRoute");
|
|
1376
|
+
|
|
1377
|
+
// src/routes/dynamic-data/stream-bytes.ts
|
|
1378
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
1379
|
+
var streamBytesSchema = {
|
|
1380
|
+
description: "Streams n random bytes generated with given seed, in chunks of chunk_size per packet",
|
|
1381
|
+
tags: [
|
|
1382
|
+
"Dynamic Data"
|
|
1383
|
+
],
|
|
1384
|
+
params: {
|
|
1385
|
+
type: "object",
|
|
1386
|
+
properties: {
|
|
1387
|
+
n: {
|
|
1388
|
+
type: "string",
|
|
1389
|
+
description: "Number of bytes to stream"
|
|
1390
|
+
}
|
|
1391
|
+
},
|
|
1392
|
+
required: [
|
|
1393
|
+
"n"
|
|
1394
|
+
]
|
|
1395
|
+
},
|
|
1396
|
+
querystring: {
|
|
1397
|
+
type: "object",
|
|
1398
|
+
properties: {
|
|
1399
|
+
seed: {
|
|
1400
|
+
type: "string",
|
|
1401
|
+
description: "Seed for random number generation"
|
|
1402
|
+
},
|
|
1403
|
+
chunk_size: {
|
|
1404
|
+
type: "string",
|
|
1405
|
+
description: "Size of each chunk (default: 10240)"
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
},
|
|
1409
|
+
response: {
|
|
1410
|
+
200: {
|
|
1411
|
+
type: "string",
|
|
1412
|
+
description: "Streamed random binary data"
|
|
1413
|
+
},
|
|
1414
|
+
400: {
|
|
1415
|
+
type: "object",
|
|
1416
|
+
properties: {
|
|
1417
|
+
error: {
|
|
1418
|
+
type: "string"
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
function seededRandom2(seed) {
|
|
1425
|
+
let state = seed;
|
|
1426
|
+
return () => {
|
|
1427
|
+
state = state * 1103515245 + 12345 & 2147483647;
|
|
1428
|
+
return state / 2147483647;
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
__name(seededRandom2, "seededRandom");
|
|
1432
|
+
function generateSeededBytes2(n, seed) {
|
|
1433
|
+
const random = seededRandom2(seed);
|
|
1434
|
+
const bytes = Buffer.alloc(n);
|
|
1435
|
+
for (let i = 0; i < n; i++) {
|
|
1436
|
+
bytes[i] = Math.floor(random() * 256);
|
|
1437
|
+
}
|
|
1438
|
+
return bytes;
|
|
1439
|
+
}
|
|
1440
|
+
__name(generateSeededBytes2, "generateSeededBytes");
|
|
1441
|
+
var MAX_BYTES4 = 100 * 1024;
|
|
1442
|
+
var DEFAULT_CHUNK_SIZE = 10240;
|
|
1443
|
+
var streamBytesRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1444
|
+
fastify.get("/stream-bytes/:n", {
|
|
1445
|
+
schema: streamBytesSchema
|
|
1446
|
+
}, async (request, reply) => {
|
|
1447
|
+
const n = Number.parseInt(request.params.n, 10);
|
|
1448
|
+
if (Number.isNaN(n) || n < 0) {
|
|
1449
|
+
return reply.code(400).send({
|
|
1450
|
+
error: "n must be a non-negative integer"
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
const limitedN = Math.min(n, MAX_BYTES4);
|
|
1454
|
+
const chunkSize = request.query.chunk_size ? Number.parseInt(request.query.chunk_size, 10) : DEFAULT_CHUNK_SIZE;
|
|
1455
|
+
if (Number.isNaN(chunkSize) || chunkSize <= 0) {
|
|
1456
|
+
return reply.code(400).send({
|
|
1457
|
+
error: "chunk_size must be a positive integer"
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
const seedParam = request.query.seed;
|
|
1461
|
+
let allBytes;
|
|
1462
|
+
if (seedParam !== void 0) {
|
|
1463
|
+
const seed = Number.parseInt(seedParam, 10);
|
|
1464
|
+
if (Number.isNaN(seed)) {
|
|
1465
|
+
return reply.code(400).send({
|
|
1466
|
+
error: "seed must be an integer"
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
allBytes = generateSeededBytes2(limitedN, seed);
|
|
1470
|
+
} else {
|
|
1471
|
+
allBytes = randomBytes2(limitedN);
|
|
1472
|
+
}
|
|
1473
|
+
reply.raw.writeHead(200, {
|
|
1474
|
+
"Content-Type": "application/octet-stream",
|
|
1475
|
+
"Transfer-Encoding": "chunked"
|
|
1476
|
+
});
|
|
1477
|
+
let offset = 0;
|
|
1478
|
+
while (offset < allBytes.length) {
|
|
1479
|
+
const chunk = allBytes.subarray(offset, offset + chunkSize);
|
|
1480
|
+
reply.raw.write(chunk);
|
|
1481
|
+
offset += chunkSize;
|
|
1482
|
+
}
|
|
1483
|
+
reply.raw.end();
|
|
1484
|
+
});
|
|
1485
|
+
}, "streamBytesRoute");
|
|
1486
|
+
|
|
1487
|
+
// src/routes/dynamic-data/uuid.ts
|
|
1488
|
+
import { randomUUID } from "crypto";
|
|
1489
|
+
var uuidSchema = {
|
|
1490
|
+
description: "Return a UUID4",
|
|
1491
|
+
tags: [
|
|
1492
|
+
"Dynamic Data"
|
|
1493
|
+
],
|
|
1494
|
+
response: {
|
|
1495
|
+
200: {
|
|
1496
|
+
type: "object",
|
|
1497
|
+
properties: {
|
|
1498
|
+
uuid: {
|
|
1499
|
+
type: "string",
|
|
1500
|
+
format: "uuid"
|
|
1501
|
+
}
|
|
1502
|
+
},
|
|
1503
|
+
required: [
|
|
1504
|
+
"uuid"
|
|
1505
|
+
]
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
var uuidRoute = /* @__PURE__ */ __name((fastify) => {
|
|
1510
|
+
fastify.get("/uuid", {
|
|
1511
|
+
schema: uuidSchema
|
|
1512
|
+
}, async () => ({
|
|
1513
|
+
uuid: randomUUID()
|
|
1514
|
+
}));
|
|
1515
|
+
}, "uuidRoute");
|
|
1516
|
+
|
|
715
1517
|
// src/routes/http-methods/delete.ts
|
|
716
1518
|
var deleteSchema = {
|
|
717
1519
|
description: "Handles a DELETE request and returns request information",
|
|
@@ -1148,6 +1950,18 @@ var indexRoute = /* @__PURE__ */ __name((fastify) => {
|
|
|
1148
1950
|
<meta
|
|
1149
1951
|
name="viewport"
|
|
1150
1952
|
content="width=device-width, initial-scale=1" />
|
|
1953
|
+
<meta name="description" content="MockHttp - A powerful HTTP mock server and httpbin replacement for API testing. Built with Fastify, TypeScript, and Docker support." />
|
|
1954
|
+
<meta name="keywords" content="mock, http, httpbin, testing, api, fastify, docker, typescript, rest, mock-server, api-testing, http-testing, httpbin-alternative" />
|
|
1955
|
+
<meta property="og:title" content="MockHttp - HTTP Mock Server" />
|
|
1956
|
+
<meta property="og:description" content="A powerful HTTP mock server and httpbin replacement for API testing. Built with Fastify, TypeScript, and Docker support." />
|
|
1957
|
+
<meta property="og:type" content="website" />
|
|
1958
|
+
<meta property="og:url" content="https://mockhttp.org" />
|
|
1959
|
+
<meta property="og:image" content="https://mockhttp.org/logo.png" />
|
|
1960
|
+
<meta property="og:site_name" content="MockHttp" />
|
|
1961
|
+
<meta name="twitter:card" content="summary" />
|
|
1962
|
+
<meta name="twitter:title" content="MockHttp - HTTP Mock Server" />
|
|
1963
|
+
<meta name="twitter:description" content="A powerful HTTP mock server and httpbin replacement for API testing." />
|
|
1964
|
+
<meta name="twitter:image" content="https://mockhttp.org/logo.png" />
|
|
1151
1965
|
<style>
|
|
1152
1966
|
:root {
|
|
1153
1967
|
--scalar-custom-header-height: 70px;
|
|
@@ -2716,7 +3530,7 @@ import { fastifySwaggerUi } from "@fastify/swagger-ui";
|
|
|
2716
3530
|
// package.json
|
|
2717
3531
|
var package_default = {
|
|
2718
3532
|
name: "@jaredwray/mockhttp",
|
|
2719
|
-
version: "1.
|
|
3533
|
+
version: "1.3.0",
|
|
2720
3534
|
description: "Mock Http - Easy way to mock http with httpbin replacement",
|
|
2721
3535
|
type: "module",
|
|
2722
3536
|
main: "dist/index.js",
|
|
@@ -2767,36 +3581,41 @@ var package_default = {
|
|
|
2767
3581
|
},
|
|
2768
3582
|
homepage: "https://github.com/jaredwray/mockhttp#readme",
|
|
2769
3583
|
devDependencies: {
|
|
2770
|
-
"@biomejs/biome": "^2.3.
|
|
2771
|
-
"@swc/core": "^1.15.
|
|
3584
|
+
"@biomejs/biome": "^2.3.11",
|
|
3585
|
+
"@swc/core": "^1.15.8",
|
|
2772
3586
|
"@types/html-escaper": "^3.0.4",
|
|
2773
|
-
"@vitest/coverage-v8": "^4.0.
|
|
3587
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
2774
3588
|
rimraf: "^6.1.2",
|
|
2775
3589
|
tsup: "^8.5.1",
|
|
2776
3590
|
tsx: "^4.21.0",
|
|
2777
3591
|
typescript: "^5.9.3",
|
|
2778
|
-
vitest: "^4.0.
|
|
3592
|
+
vitest: "^4.0.16"
|
|
2779
3593
|
},
|
|
2780
3594
|
dependencies: {
|
|
2781
3595
|
"@fastify/cookie": "^11.0.2",
|
|
2782
3596
|
"@fastify/helmet": "^13.0.2",
|
|
2783
3597
|
"@fastify/rate-limit": "^10.3.0",
|
|
2784
|
-
"@fastify/static": "^
|
|
3598
|
+
"@fastify/static": "^9.0.0",
|
|
2785
3599
|
"@fastify/swagger": "^9.6.1",
|
|
2786
|
-
"@fastify/swagger-ui": "^5.2.
|
|
2787
|
-
"@scalar/api-reference": "^1.
|
|
3600
|
+
"@fastify/swagger-ui": "^5.2.4",
|
|
3601
|
+
"@scalar/api-reference": "^1.43.1",
|
|
2788
3602
|
"detect-port": "^2.1.0",
|
|
2789
3603
|
fastify: "^5.6.2",
|
|
2790
|
-
hookified: "^1.
|
|
3604
|
+
hookified: "^1.15.0",
|
|
2791
3605
|
"html-escaper": "^3.0.3",
|
|
2792
|
-
pino: "^10.1.
|
|
3606
|
+
pino: "^10.1.1",
|
|
2793
3607
|
"pino-pretty": "^13.1.3"
|
|
2794
3608
|
},
|
|
2795
3609
|
files: [
|
|
2796
3610
|
"dist",
|
|
2797
3611
|
"public",
|
|
2798
3612
|
"LICENCE"
|
|
2799
|
-
]
|
|
3613
|
+
],
|
|
3614
|
+
pnpm: {
|
|
3615
|
+
overrides: {
|
|
3616
|
+
"set-cookie-parser": "2.7.2"
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
2800
3619
|
};
|
|
2801
3620
|
|
|
2802
3621
|
// src/swagger.ts
|
|
@@ -2851,7 +3670,7 @@ var registerSwaggerUi = /* @__PURE__ */ __name(async (fastify) => {
|
|
|
2851
3670
|
}, "registerSwaggerUi");
|
|
2852
3671
|
|
|
2853
3672
|
// src/tap-manager.ts
|
|
2854
|
-
import { randomUUID } from "crypto";
|
|
3673
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2855
3674
|
var TapManager = class {
|
|
2856
3675
|
static {
|
|
2857
3676
|
__name(this, "TapManager");
|
|
@@ -2879,7 +3698,7 @@ var TapManager = class {
|
|
|
2879
3698
|
* @returns A tap object that can be used to remove the injection
|
|
2880
3699
|
*/
|
|
2881
3700
|
inject(response, matcher) {
|
|
2882
|
-
const id =
|
|
3701
|
+
const id = randomUUID2();
|
|
2883
3702
|
const tap = {
|
|
2884
3703
|
id,
|
|
2885
3704
|
response,
|
|
@@ -2992,7 +3811,8 @@ var MockHttp = class extends Hookified {
|
|
|
2992
3811
|
cookies: true,
|
|
2993
3812
|
anything: true,
|
|
2994
3813
|
auth: true,
|
|
2995
|
-
images: true
|
|
3814
|
+
images: true,
|
|
3815
|
+
dynamicData: true
|
|
2996
3816
|
};
|
|
2997
3817
|
_rateLimit = {
|
|
2998
3818
|
max: 1e3,
|
|
@@ -3247,7 +4067,7 @@ var MockHttp = class extends Hookified {
|
|
|
3247
4067
|
if (this._apiDocs) {
|
|
3248
4068
|
await this.registerApiDocs();
|
|
3249
4069
|
}
|
|
3250
|
-
const { httpMethods, redirects, requestInspection, responseInspection, responseFormats, statusCodes: statusCodes2, cookies, anything, auth, images } = this._httpBin;
|
|
4070
|
+
const { httpMethods, redirects, requestInspection, responseInspection, responseFormats, statusCodes: statusCodes2, cookies, anything, auth, images, dynamicData } = this._httpBin;
|
|
3251
4071
|
if (httpMethods) {
|
|
3252
4072
|
await this.registerHttpMethods();
|
|
3253
4073
|
}
|
|
@@ -3278,6 +4098,9 @@ var MockHttp = class extends Hookified {
|
|
|
3278
4098
|
if (images) {
|
|
3279
4099
|
await this.registerImageRoutes();
|
|
3280
4100
|
}
|
|
4101
|
+
if (dynamicData) {
|
|
4102
|
+
await this.registerDynamicDataRoutes();
|
|
4103
|
+
}
|
|
3281
4104
|
if (this._autoDetectPort) {
|
|
3282
4105
|
const originalPort = this._port;
|
|
3283
4106
|
this._port = await this.detectPort();
|
|
@@ -3409,6 +4232,22 @@ var MockHttp = class extends Hookified {
|
|
|
3409
4232
|
const fastify = fastifyInstance ?? this._server;
|
|
3410
4233
|
await fastify.register(imageRoutes);
|
|
3411
4234
|
}
|
|
4235
|
+
/**
|
|
4236
|
+
* Register the dynamic data routes.
|
|
4237
|
+
* @param fastifyInstance - the server instance to register the routes on.
|
|
4238
|
+
*/
|
|
4239
|
+
async registerDynamicDataRoutes(fastifyInstance) {
|
|
4240
|
+
const fastify = fastifyInstance ?? this._server;
|
|
4241
|
+
await fastify.register(uuidRoute);
|
|
4242
|
+
await fastify.register(bytesRoute);
|
|
4243
|
+
await fastify.register(streamBytesRoute);
|
|
4244
|
+
await fastify.register(delayRoute);
|
|
4245
|
+
await fastify.register(base64Route);
|
|
4246
|
+
await fastify.register(streamRoute);
|
|
4247
|
+
await fastify.register(rangeRoute);
|
|
4248
|
+
await fastify.register(dripRoute);
|
|
4249
|
+
await fastify.register(linksRoute);
|
|
4250
|
+
}
|
|
3412
4251
|
};
|
|
3413
4252
|
|
|
3414
4253
|
// src/index.ts
|
|
@@ -3435,3 +4274,5 @@ export {
|
|
|
3435
4274
|
start
|
|
3436
4275
|
};
|
|
3437
4276
|
/* v8 ignore next -- @preserve */
|
|
4277
|
+
/* v8 ignore next 2 -- @preserve */
|
|
4278
|
+
/* v8 ignore next 3 -- @preserve */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaredwray/mockhttp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Mock Http - Easy way to mock http with httpbin replacement",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,34 +51,39 @@
|
|
|
51
51
|
},
|
|
52
52
|
"homepage": "https://github.com/jaredwray/mockhttp#readme",
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@biomejs/biome": "^2.3.
|
|
55
|
-
"@swc/core": "^1.15.
|
|
54
|
+
"@biomejs/biome": "^2.3.11",
|
|
55
|
+
"@swc/core": "^1.15.8",
|
|
56
56
|
"@types/html-escaper": "^3.0.4",
|
|
57
|
-
"@vitest/coverage-v8": "^4.0.
|
|
57
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
58
58
|
"rimraf": "^6.1.2",
|
|
59
59
|
"tsup": "^8.5.1",
|
|
60
60
|
"tsx": "^4.21.0",
|
|
61
61
|
"typescript": "^5.9.3",
|
|
62
|
-
"vitest": "^4.0.
|
|
62
|
+
"vitest": "^4.0.16"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@fastify/cookie": "^11.0.2",
|
|
66
66
|
"@fastify/helmet": "^13.0.2",
|
|
67
67
|
"@fastify/rate-limit": "^10.3.0",
|
|
68
|
-
"@fastify/static": "^
|
|
68
|
+
"@fastify/static": "^9.0.0",
|
|
69
69
|
"@fastify/swagger": "^9.6.1",
|
|
70
|
-
"@fastify/swagger-ui": "^5.2.
|
|
71
|
-
"@scalar/api-reference": "^1.
|
|
70
|
+
"@fastify/swagger-ui": "^5.2.4",
|
|
71
|
+
"@scalar/api-reference": "^1.43.1",
|
|
72
72
|
"detect-port": "^2.1.0",
|
|
73
73
|
"fastify": "^5.6.2",
|
|
74
|
-
"hookified": "^1.
|
|
74
|
+
"hookified": "^1.15.0",
|
|
75
75
|
"html-escaper": "^3.0.3",
|
|
76
|
-
"pino": "^10.1.
|
|
76
|
+
"pino": "^10.1.1",
|
|
77
77
|
"pino-pretty": "^13.1.3"
|
|
78
78
|
},
|
|
79
79
|
"files": [
|
|
80
80
|
"dist",
|
|
81
81
|
"public",
|
|
82
82
|
"LICENCE"
|
|
83
|
-
]
|
|
83
|
+
],
|
|
84
|
+
"pnpm": {
|
|
85
|
+
"overrides": {
|
|
86
|
+
"set-cookie-parser": "2.7.2"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
84
89
|
}
|