[{"data":1,"prerenderedAt":29331},["ShallowReactive",2],{"article_list_series_":3},[4,352,1296,2797,10498,10980,14341,15136,16606,19852,23619,24997,25511,25872,26002,27560],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"excerpt":11,"image":12,"publishDate":13,"tags":14,"body":17,"_type":343,"_id":344,"_source":345,"_file":346,"_stem":347,"_extension":348,"author":349},"/alalande/2023-2/heuristics","2023-2",false,"","Thinking Like a Programmer: Heuristics","We want to help you turbo-charge your decision making.","Hiring good people is hard.","/alalande/2023-2/img/heuristics.png","2024-03-15",[15,16],"series","educational",{"type":18,"children":19,"toc":332},"root",[20,27,40,45,50,55,62,74,79,86,91,96,101,106,111,117,122,127,132,137,149,154,160,165,177,182,202,207,212,218,223,228,233,238,250,263,268,273,282,295,300,306,311,316],{"type":21,"tag":22,"props":23,"children":24},"element","p",{},[25],{"type":26,"value":11},"text",{"type":21,"tag":22,"props":28,"children":29},{},[30,32,38],{"type":26,"value":31},"No, actually, hiring is easy. It's ",{"type":21,"tag":33,"props":34,"children":35},"em",{},[36],{"type":26,"value":37},"finding",{"type":26,"value":39}," the right people for the job that's hard.",{"type":21,"tag":22,"props":41,"children":42},{},[43],{"type":26,"value":44},"Suppose you are the hiring manager in charge of filling an open position. You have written an exciting and enticing job ad, posted it in all of the right places, and now, you sit in front of a stack of 50 resumes. In the early stage of the hiring process, your aim was to cast as wide a net as possible, in hopes of finding the absolute best fit. You've done that, and now you find yourself at the inflection point, where you need to work in the opposite direction, and narrow the field of consideration down to one.",{"type":21,"tag":22,"props":46,"children":47},{},[48],{"type":26,"value":49},"In an ideal world, you would hire each of these applicants for a year, and see who does the job best, like some reality show competition. Realistically, however, you don't have time to interview 50 people, let alone coach them through a first year on the job.",{"type":21,"tag":22,"props":51,"children":52},{},[53],{"type":26,"value":54},"What you -- and most people -- would do to speed up the process is to define some criteria that would let you start sorting the applicants into \"will\" and \"won't\" interview piles. What you need are heuristics.",{"type":21,"tag":56,"props":57,"children":59},"h2",{"id":58},"two-kinds-of-heuristics",[60],{"type":26,"value":61},"Two Kinds of Heuristics",{"type":21,"tag":22,"props":63,"children":64},{},[65,67,72],{"type":26,"value":66},"The word ",{"type":21,"tag":33,"props":68,"children":69},{},[70],{"type":26,"value":71},"heuristics",{"type":26,"value":73}," (hyoo-RIS-tics) refers to a kind of filter that comes before a difficult or laborious decision, and which reduces the options by quickly disqualifying those that seem like a waste of time or energy. Using a heuristic, you can efficiently narrow down the field of choices, and spend more time meeting with and getting to know the candidates who seem most promising.",{"type":21,"tag":22,"props":75,"children":76},{},[77],{"type":26,"value":78},"How can you tell which applicants to immediately discard? That question lies at the heart of this discussion, and to address it we need to talk about two different kinds of heuristics.",{"type":21,"tag":80,"props":81,"children":83},"h3",{"id":82},"loosecasual-heuristics",[84],{"type":26,"value":85},"Loose/Casual Heuristics",{"type":21,"tag":22,"props":87,"children":88},{},[89],{"type":26,"value":90},"Think of all of the decisions that consumers make at the grocery store. Few consumers make decisions based solely on objective product details (e.g. price, expiration dates, quantities, nutritional details, etc.). Most consumers also include subjective criteria, and might therefore opt for the brand of eggs with the cuddly mascot on the carton, or pay for the more expensive brand of razors because of the catchy jingle in their ads.",{"type":21,"tag":22,"props":92,"children":93},{},[94],{"type":26,"value":95},"Most of the time, humans make decisions using fuzzy, ill-defined emotional judgments, not clear, strictly-defined objective criteria. For that reason, sorting \"good\" choices from \"bad\" choices tends to be highly subjective, and that's why we talk first about casual heuristics.",{"type":21,"tag":22,"props":97,"children":98},{},[99],{"type":26,"value":100},"Returning to the interviewing scenario, there are countless heuristics you could use to quickly discard the \"bad\" candidates. You could start by tossing the obviously-unprofessional resumes: those with spelling mistakes, those with bad grammar or swearing, those written in fanciful fonts, or those written by hand, in pink, on the back of a Big Mac wrapper.",{"type":21,"tag":22,"props":102,"children":103},{},[104],{"type":26,"value":105},"This is your first heuristic, call it the Unprofessional Heuristic. You are effectively saying that the ideal candidate for the job will already be fully versed in the codes of professionalism that you expect from job-seekers.",{"type":21,"tag":22,"props":107,"children":108},{},[109],{"type":26,"value":110},"This is a \"loose\" heuristic, because you can see it as a way to cut corners. You may be right 99.9% of the time, that an unprofessional-looking resume is indicative of a candidate who would not fit the corporate culture, but you can probably also picture a Hollywood-worthy scenario where a prodigious but eccentric savant is disqualified from a job he could do in his sleep, because of the Unprofessional Heuristic. Hollywood notwithstanding, the Unprofessional Heuristic is a useful way of reducing the candidate pool to a manageable size.",{"type":21,"tag":80,"props":112,"children":114},{"id":113},"stricttechnical-heuristics",[115],{"type":26,"value":116},"Strict/Technical Heuristics",{"type":21,"tag":22,"props":118,"children":119},{},[120],{"type":26,"value":121},"On the other hand, certain problems -- especially in the quantitative and scientific disciplines -- come with an \"objective\" definition of what makes a bad or a good candidate.",{"type":21,"tag":22,"props":123,"children":124},{},[125],{"type":26,"value":126},"To illustrate this, consider the problem of verifying if a number is prime or not. Determining whether a small number is prime is easy, but as numbers get increasingly long, the difficulty of the problem increases. In cryptography, for instance, knowing whether very large numbers (containing hundreds of digits) are prime or not is critical to keeping information secure. Finding ways to make the prime/composite determination about large numbers faster and more efficient is essential to the widespread use of cryptography.",{"type":21,"tag":22,"props":128,"children":129},{},[130],{"type":26,"value":131},"Heuristics can help greatly accelerate the process. If you spot a number greater than 2 that ends in an even digit (0, 2, 4, 6, or 8), you can quickly tell that it is divisible by 2, and therefore could not possibly be prime. The beauty of this approach is that no matter how many digits a number has, looking to see if the last digit is even is always going to take the exact same amount of time.",{"type":21,"tag":22,"props":133,"children":134},{},[135],{"type":26,"value":136},"We'll call this the Obviously-Even Heuristic. It alone saves you from checking a whopping 50 percent of candidates!",{"type":21,"tag":22,"props":138,"children":139},{},[140,142,147],{"type":26,"value":141},"Unlike the Unprofessionalism Heuristic above (which was a casual/loose type of heuristic), this one will never be wrong. This is the key difference between the two types of heuristics: technical heuristics must never, ",{"type":21,"tag":33,"props":143,"children":144},{},[145],{"type":26,"value":146},"ever",{"type":26,"value":148}," disqualify a good candidate.",{"type":21,"tag":22,"props":150,"children":151},{},[152],{"type":26,"value":153},"Whereas a \"loose\" heuristic is like a \"rule of thumb\", a \"strict\" heuristic is more like a hint in a puzzle: the hint will never be wrong, but it won't tell you the answer either. A hint will narrow down your range of choices and provide a condition which must necessarily be true in order to find the answer.",{"type":21,"tag":80,"props":155,"children":157},{"id":156},"necessary-not-sufficient",[158],{"type":26,"value":159},"Necessary, not Sufficient",{"type":21,"tag":22,"props":161,"children":162},{},[163],{"type":26,"value":164},"Technical heuristics ensure that they never disqualify a good candidate by isolating a necessary but not solely-sufficient condition that can be easily tested (like the presence of an even number as the last digit for numbers greater than 2).",{"type":21,"tag":22,"props":166,"children":167},{},[168,170,175],{"type":26,"value":169},"Notice that a number ",{"type":21,"tag":33,"props":171,"children":172},{},[173],{"type":26,"value":174},"not",{"type":26,"value":176}," ending in an even digit does not guarantee it to be prime. A number like 25 does not end in an even digit, but is not prime, either.",{"type":21,"tag":22,"props":178,"children":179},{},[180],{"type":26,"value":181},"Like sifting pans with holes that get progressively smaller down the stack, you could pile heuristics on top of one another to reduce the number of candidates even further, e.g.:",{"type":21,"tag":183,"props":184,"children":185},"ol",{},[186,192,197],{"type":21,"tag":187,"props":188,"children":189},"li",{},[190],{"type":26,"value":191},"First, we apply our Obviously-Even Heuristic,",{"type":21,"tag":187,"props":193,"children":194},{},[195],{"type":26,"value":196},"Then, we apply the Obviously-Divisible-by-Five Heuristic, which will filter out any candidate greater than 5 that ends in 5 or 0.",{"type":21,"tag":187,"props":198,"children":199},{},[200],{"type":26,"value":201},"Finally, we'll apply the Obviously-Divisible-by-Three Heuristic, which will filter out any candidate greater than 3 whose digits add up to a multiple of 3.",{"type":21,"tag":22,"props":203,"children":204},{},[205],{"type":26,"value":206},"A number like 8 will get filtered out by the first heuristic, a number like 15 will be filtered out by the second one, and a number like 63 will be filtered out by the third.",{"type":21,"tag":22,"props":208,"children":209},{},[210],{"type":26,"value":211},"If a number manages to get through all three filters, it has managed to satisfy three necessary conditions of primacy, but that is still not enough, not sufficient. The number still needs to be tested for being prime or not. Heuristics don't help speed up the final test, they just ensure that you don't waste time running it on numbers like 9,375,292,394, which are clearly not prime.",{"type":21,"tag":56,"props":213,"children":215},{"id":214},"programming-with-costs",[216],{"type":26,"value":217},"Programming with Costs",{"type":21,"tag":22,"props":219,"children":220},{},[221],{"type":26,"value":222},"This entire discussion has been about the cost of making decisions. These costs come in the form of time, money, effort, or any other scarce resource (e.g. computing power, hard disk space, bandwidth).",{"type":21,"tag":22,"props":224,"children":225},{},[226],{"type":26,"value":227},"Casual Heuristics sacrifice some of the accuracy of the decision-making process to gain time. Technical Heuristics find necessary but insufficient conditions that can be tested at low cost.",{"type":21,"tag":22,"props":229,"children":230},{},[231],{"type":26,"value":232},"A skillful programmer should always bear the question of costs in mind while writing code. Sending a request out over the internet may take a lot of time, and should be considered much more expensive than retrieving the same information from the hard drive, or, even better, from an in-memory cache.",{"type":21,"tag":22,"props":234,"children":235},{},[236],{"type":26,"value":237},"When combined with a language feature called \"short circuiting\", it becomes very easy to implement technical heuristics in code.",{"type":21,"tag":239,"props":240,"children":244},"pre",{"className":241,"code":243,"language":26},[242],"language-text","#!/usr/bin/env python3\n\nfrom .heuristics import (\n   # Runs quickly, only checks the last digit\n   obviously_even_heuristic,\n\n   # Runs quickly, only checks the last digit\n   obviously_divisible_by_five_heuristic,\n\n   # Runs a tiny bit slower, requires summing up\n   # all of the digits in a number recursively\n   obviously_divisible_by_three_heuristic,\n)\n\nfrom .deciders import (\n   # Very expensive to run on large numbers\n   is_number_prime\n)\n\n\ndef determine_if_prime(n):\n   return (\n      not obviously_even_heuristic(n) and\n      not obviously_divisible_by_five_heuristic(n) and\n      not obviously_divisible_by_three_heuristic(n) and\n      is_number_prime(n)\n   )\n",[245],{"type":21,"tag":246,"props":247,"children":248},"code",{"__ignoreMap":8},[249],{"type":26,"value":243},{"type":21,"tag":22,"props":251,"children":252},{},[253,255,261],{"type":26,"value":254},"\"Short circuiting\" means that in the ",{"type":21,"tag":246,"props":256,"children":258},{"className":257},[],[259],{"type":26,"value":260},"return",{"type":26,"value":262}," statement at the end of the code above, the second condition will not be evaluated if the first one is false, the third will not be evaluated if the second one is false, and so on. In essence, with the \"and\" operator, Python is smart enough to know that it can stop evaluating the expression if the end result cannot possibly be true.",{"type":21,"tag":22,"props":264,"children":265},{},[266],{"type":26,"value":267},"(Short-circuiting is essentially a technical heuristic built into the programming language itself!)",{"type":21,"tag":22,"props":269,"children":270},{},[271],{"type":26,"value":272},"This behavior could also be used with unrelated tests using the \"or\" operator, as in this excerpt:",{"type":21,"tag":239,"props":274,"children":277},{"className":275,"code":276,"language":26},[242],"def transfer_money(amount, from_account, to_account):\n   if (\n      amount > 10000 or\n      from_account.is_frozen() or\n      to_account.is_frozen() or\n      account_is_on_government_watchlist(from_account) or\n      account_is_on_government_watchlist(to_account)\n    ):\n      raise TransferDeniedException\n    \n    # proceed with the rest of the transfer code here\n    ...\n",[278],{"type":21,"tag":246,"props":279,"children":280},{"__ignoreMap":8},[281],{"type":26,"value":276},{"type":21,"tag":22,"props":283,"children":284},{},[285,287,293],{"type":26,"value":286},"Before we proceed to the bulk of the work inside of this ",{"type":21,"tag":246,"props":288,"children":290},{"className":289},[],[291],{"type":26,"value":292},"transfer_money()",{"type":26,"value":294}," function, we first test whether this transfer raises any red flags. We do this with the \"or\" operator, which means that as soon as any one of these conditions is true, we stop testing the rest.",{"type":21,"tag":22,"props":296,"children":297},{},[298],{"type":26,"value":299},"This is why we start with a very inexpensive test (whether the amount being transferred exceeds $10,000), then proceed with a bit more expensive tests (we query our own database to see if the accounts are frozen), and finally, we perform the most expensive tests (querying a government system).",{"type":21,"tag":56,"props":301,"children":303},{"id":302},"conclusion",[304],{"type":26,"value":305},"Conclusion",{"type":21,"tag":22,"props":307,"children":308},{},[309],{"type":26,"value":310},"Technical heuristics are a very useful tool for any programmer who wishes to build at scale. A well-crafted technical heuristic may permit the same program to run faster, on smaller devices, across more users simultaneously, etc.",{"type":21,"tag":22,"props":312,"children":313},{},[314],{"type":26,"value":315},"More broadly, programming while keeping in mind the costs associated with each statement is a good habit to adopt. Higher resource usage implies higher fragility as well, as there is more that can go wrong. All other things being equal, code that uses fewer resources is necessarily less fragile.",{"type":21,"tag":22,"props":317,"children":318},{},[319,321,330],{"type":26,"value":320},"If you'd like to get started experimenting with heuristics in a hands-on way, there is no better place to start than ",{"type":21,"tag":322,"props":323,"children":327},"a",{"href":324,"rel":325},"https://projecteuler.net/",[326],"nofollow",[328],{"type":26,"value":329},"Project Euler",{"type":26,"value":331},". There, you will find hundreds of puzzles involving number theory and geometry. For each one, you must write a piece of code that, when run, will find the answer in under one minute. You will quickly discover that each puzzle has a \"naive\" solution that will require hours, days, or even months of computing time to find. The art in solving these puzzles lies in finding the right set of heuristics to speed up your code.",{"title":8,"searchDepth":333,"depth":333,"links":334},3,[335,341,342],{"id":58,"depth":336,"text":61,"children":337},2,[338,339,340],{"id":82,"depth":333,"text":85},{"id":113,"depth":333,"text":116},{"id":156,"depth":333,"text":159},{"id":214,"depth":336,"text":217},{"id":302,"depth":336,"text":305},"markdown","content:alalande:2023-2:heuristics.md","content","alalande/2023-2/heuristics.md","alalande/2023-2/heuristics","md",{"user":350,"name":351},"alalande","Anthony Lalande",{"_path":353,"_dir":354,"_draft":7,"_partial":7,"_locale":8,"title":355,"description":356,"excerpt":357,"image":358,"publishDate":359,"tags":360,"body":361,"_type":343,"_id":1292,"_source":345,"_file":1293,"_stem":1294,"_extension":348,"author":1295},"/alalande/2023-1/escaping_text","2023-1","Thinking Like a Programmer: Escaping Text","In this progressive and vivid explanation, we explore the HOW and WHY of quoting, nesting, and escaping text.","Spend enough time around a software engineer, and you might hear something about escaping text. This is not a wish to avoid text, or replace it with other media (images, animated GIFs, movies, etc.).","/alalande/2023-1/img/header.png","2024-02-15",[15,16],{"type":18,"children":362,"toc":1284},[363,375,394,400,419,440,445,501,506,514,540,552,574,587,599,604,626,632,637,642,653,658,663,668,674,686,698,710,729,734,743,748,766,771,776,781,787,792,815,820,832,841,846,893,905,917,929,934,941,946,952,962,967,977,995,1014,1019,1079,1084,1089,1096,1101,1117,1122,1141,1146,1155,1160,1220,1225,1231,1236,1254,1259,1273,1278],{"type":21,"tag":22,"props":364,"children":365},{},[366,368,373],{"type":26,"value":367},"Spend enough time around a software engineer, and you might hear something about ",{"type":21,"tag":33,"props":369,"children":370},{},[371],{"type":26,"value":372},"escaping text",{"type":26,"value":374},". This is not a wish to avoid text, or replace it with other media (images, animated GIFs, movies, etc.).",{"type":21,"tag":22,"props":376,"children":377},{},[378,380,385,387,392],{"type":26,"value":379},"\"Escaping text\", in the context of software engineering, refers to the practice of switching between the ",{"type":21,"tag":33,"props":381,"children":382},{},[383],{"type":26,"value":384},"literal",{"type":26,"value":386}," and ",{"type":21,"tag":33,"props":388,"children":389},{},[390],{"type":26,"value":391},"pragmatic",{"type":26,"value":393}," modes of interpreting text. This can be a daunting idea to wrap your head around, so allow me to illustrate it progressively.",{"type":21,"tag":56,"props":395,"children":397},{"id":396},"lets-start-with-strings",[398],{"type":26,"value":399},"Let's start with strings",{"type":21,"tag":22,"props":401,"children":402},{},[403,405,410,412,417],{"type":26,"value":404},"When a programmer writes code, he or she is usually making a list of statements in a text file. This text file gets transformed (either ",{"type":21,"tag":33,"props":406,"children":407},{},[408],{"type":26,"value":409},"compiled",{"type":26,"value":411}," or ",{"type":21,"tag":33,"props":413,"children":414},{},[415],{"type":26,"value":416},"interpreted",{"type":26,"value":418},") into language that the computer itself can read and carry out.",{"type":21,"tag":22,"props":420,"children":421},{},[422,424,431,433,438],{"type":26,"value":423},"Conventionally, when learning a new language, a programmer will be exposed to a program called ",{"type":21,"tag":322,"props":425,"children":428},{"href":426,"rel":427},"https://en.wikipedia.org/wiki/%22Hello,_World!%22_program",[326],[429],{"type":26,"value":430},"Hello World",{"type":26,"value":432},". This program -- no matter the language it is programmed in -- displays the phrase \"Hello, World!\" somewhere on the screen. (Confusingly, programmers refer to this as ",{"type":21,"tag":33,"props":434,"children":435},{},[436],{"type":26,"value":437},"printing",{"type":26,"value":439}," text to the screen).",{"type":21,"tag":22,"props":441,"children":442},{},[443],{"type":26,"value":444},"In Python, the \"Hello World\" program looks like this:",{"type":21,"tag":239,"props":446,"children":450},{"className":447,"code":448,"language":449,"meta":8,"style":8},"language-python shiki shiki-themes github-light github-dark","#!/usr/bin/python3\n\nprint(\"Hello World!\")\n","python",[451],{"type":21,"tag":246,"props":452,"children":453},{"__ignoreMap":8},[454,466,475],{"type":21,"tag":455,"props":456,"children":459},"span",{"class":457,"line":458},"line",1,[460],{"type":21,"tag":455,"props":461,"children":463},{"style":462},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[464],{"type":26,"value":465},"#!/usr/bin/python3\n",{"type":21,"tag":455,"props":467,"children":468},{"class":457,"line":336},[469],{"type":21,"tag":455,"props":470,"children":472},{"emptyLinePlaceholder":471},true,[473],{"type":26,"value":474},"\n",{"type":21,"tag":455,"props":476,"children":477},{"class":457,"line":333},[478,484,490,496],{"type":21,"tag":455,"props":479,"children":481},{"style":480},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[482],{"type":26,"value":483},"print",{"type":21,"tag":455,"props":485,"children":487},{"style":486},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[488],{"type":26,"value":489},"(",{"type":21,"tag":455,"props":491,"children":493},{"style":492},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[494],{"type":26,"value":495},"\"Hello World!\"",{"type":21,"tag":455,"props":497,"children":498},{"style":486},[499],{"type":26,"value":500},")\n",{"type":21,"tag":22,"props":502,"children":503},{},[504],{"type":26,"value":505},"As expected, running it produces the text \"Hello World!\" on screen.",{"type":21,"tag":22,"props":507,"children":508},{},[509],{"type":21,"tag":510,"props":511,"children":513},"img",{"alt":8,"src":512},"/alalande/2023-1/img/01_hello_world.gif",[],{"type":21,"tag":22,"props":515,"children":516},{},[517,519,524,526,531,533,538],{"type":26,"value":518},"The first important thing to notice, for our purposes here, is that the ",{"type":21,"tag":33,"props":520,"children":521},{},[522],{"type":26,"value":523},"text file",{"type":26,"value":525}," that we used to define the program is different than the ",{"type":21,"tag":33,"props":527,"children":528},{},[529],{"type":26,"value":530},"result",{"type":26,"value":532}," of running that program -- which, of course, is the whole fun of programming -- you make the computer actually ",{"type":21,"tag":33,"props":534,"children":535},{},[536],{"type":26,"value":537},"do",{"type":26,"value":539}," things!",{"type":21,"tag":22,"props":541,"children":542},{},[543,545,550],{"type":26,"value":544},"In particular, you'll notice that the text \"/usr/bin/python3\" and \"print\" do not appear in the program's output; only the phrase \"Hello World!\" appears in the output. This is because \"Hello World!\" is defined in the program as a ",{"type":21,"tag":33,"props":546,"children":547},{},[548],{"type":26,"value":549},"string",{"type":26,"value":551},".",{"type":21,"tag":22,"props":553,"children":554},{},[555,560,562,566,568,572],{"type":21,"tag":33,"props":556,"children":557},{},[558],{"type":26,"value":559},"Strings",{"type":26,"value":561}," are a special type of ",{"type":21,"tag":33,"props":563,"children":564},{},[565],{"type":26,"value":26},{"type":26,"value":567}," (or ",{"type":21,"tag":33,"props":569,"children":570},{},[571],{"type":26,"value":246},{"type":26,"value":573},") that gets passed around by computer programs. Strings can be sent by programs to a variety of places -- to a spreadsheet on your hard drive, to your monitor, to your printer, to a text-to-speech synthesizer, and so on.",{"type":21,"tag":22,"props":575,"children":576},{},[577,579,585],{"type":26,"value":578},"Strings generally contain text designed for a human to read, but not always. Website addresses, for instance (e.g. \"",{"type":21,"tag":322,"props":580,"children":583},{"href":581,"rel":582},"https://www.artandlogic.com/",[326],[584],{"type":26,"value":581},{"type":26,"value":586},"\") are usually treated as strings within computer code, even if these are meant to be interpretable by web browsers rather than people.",{"type":21,"tag":22,"props":588,"children":589},{},[590,592,597],{"type":26,"value":591},"The important take-away here is that strings are not code. They are used ",{"type":21,"tag":33,"props":593,"children":594},{},[595],{"type":26,"value":596},"by",{"type":26,"value":598}," computer code, but they are pieces of text that are not transformed into computer code. If programs are airports, strings are luggage. Strings get shuttled around by the airport workers, and although they contain an assortment of objects inside, they are treated as self-contained units.",{"type":21,"tag":22,"props":600,"children":601},{},[602],{"type":26,"value":603},"This analogy is actually quite useful, because it lets us make a further comparison: the quotes around the string \"Hello World!\" are the suitcase: the opening quotation mark says \"what follows are the contents of my string\", while the closing quotation mark says \"now we're going back to the code\". This is why quotes come in pairs. One opens the string, the other closes it.",{"type":21,"tag":22,"props":605,"children":606},{},[607,609,613,614,618,620,624],{"type":26,"value":608},"This is what I meant earlier by switching between the ",{"type":21,"tag":33,"props":610,"children":611},{},[612],{"type":26,"value":384},{"type":26,"value":386},{"type":21,"tag":33,"props":615,"children":616},{},[617],{"type":26,"value":391},{"type":26,"value":619}," modes of interpreting text. All the pieces of text in our program that make the computer ",{"type":21,"tag":33,"props":621,"children":622},{},[623],{"type":26,"value":537},{"type":26,"value":625}," things are pragmatic. This is the text that defines what the airport looks like and how it operates. All of the pieces of text in our program that get passed around like luggage are literal. Quotation marks let us switch back-and-forth between designing the airport and describing the luggage.",{"type":21,"tag":56,"props":627,"children":629},{"id":628},"what-does-charlie-say",[630],{"type":26,"value":631},"What does Charlie say?",{"type":21,"tag":22,"props":633,"children":634},{},[635],{"type":26,"value":636},"I started this discussion using double quotes (\"), because readers of English will recognize them from newspaper articles and books, where double quotes are often used to encapsulate what someone has said.",{"type":21,"tag":22,"props":638,"children":639},{},[640],{"type":26,"value":641},"Avid fiction readers may be intuitively familiar with one of the tricky aspects of quotes, which is reporting on what one person says another person says. If Alice asks Bob what Charlie said, the writer needs to find a way to communicate to the reader that within Bob's response are Charlie's words.",{"type":21,"tag":22,"props":643,"children":644},{},[645,647,651],{"type":26,"value":646},"\"What did Charlie say?\", asked Alice.",{"type":21,"tag":648,"props":649,"children":650},"br",{},[],{"type":26,"value":652},"\n\"He stared off into the distance and waxed poetically, 'If things were different they wouldn't be the same'\", replied Bob.",{"type":21,"tag":22,"props":654,"children":655},{},[656],{"type":26,"value":657},"Bob has just presented the reader with a suitcase within a suitcase. The writer has used two types of quotation marks to differentiate them: Bob's words (the outer suitcase) are encapsulated using double quotes (\"). Charlie's words (the inner suitcase) are encapsulated using single quotes (').",{"type":21,"tag":22,"props":659,"children":660},{},[661],{"type":26,"value":662},"This can quickly get messy. What would Bob's response be if Charlie were quoting Dennis?",{"type":21,"tag":22,"props":664,"children":665},{},[666],{"type":26,"value":667},"English punctuation doesn't comfortably handle multiple-layered scenarios, and so a resourceful author might prefer to find an alternative way to describe the interaction, perhaps using more context or even a literary device like a flashback or a parenthetical remark.",{"type":21,"tag":56,"props":669,"children":671},{"id":670},"poetry-and-code",[672],{"type":26,"value":673},"Poetry and code",{"type":21,"tag":22,"props":675,"children":676},{},[677,679,684],{"type":26,"value":678},"While literary authors have the luxury of being able to play with the conventions and form of the language (they have ",{"type":21,"tag":33,"props":680,"children":681},{},[682],{"type":26,"value":683},"poetic license",{"type":26,"value":685}," to bend the rules), that kind of freedom is much more restricted in programming. There are a lot of creative choices you can make when writing a computer program, but these don't extend to the syntax of the language.",{"type":21,"tag":22,"props":687,"children":688},{},[689,691,696],{"type":26,"value":690},"In other words, computer code has to be unambiguous -- it can only mean one thing and one thing only. The rules of ",{"type":21,"tag":33,"props":692,"children":693},{},[694],{"type":26,"value":695},"syntax",{"type":26,"value":697}," for any given programming language are generally very strict.",{"type":21,"tag":22,"props":699,"children":700},{},[701,703,708],{"type":26,"value":702},"(Even though we talk about computer languages being \"interpreted\", this is another term of art which -- unlike its everyday usage -- doesn't imply any choice on the part of the reader, i.e. the computer. My \"Hello World\" Python program above can be interpreted (i.e. ",{"type":21,"tag":33,"props":704,"children":705},{},[706],{"type":26,"value":707},"run",{"type":26,"value":709},") millions of times, and it will function the exact same way every time.)",{"type":21,"tag":22,"props":711,"children":712},{},[713,715,720,722,727],{"type":26,"value":714},"Programming, as a discipline, draws a lot of inspiration from mathematical notation. While math formulas don't report what people say, they do make heavy use of the \"suitcase inside a suitcase\" phenomenon, more generally called ",{"type":21,"tag":33,"props":716,"children":717},{},[718],{"type":26,"value":719},"nesting",{"type":26,"value":721},". The inner suitcase from our analogy can be said to be ",{"type":21,"tag":33,"props":723,"children":724},{},[725],{"type":26,"value":726},"nested",{"type":26,"value":728}," within the outer suitcase.",{"type":21,"tag":22,"props":730,"children":731},{},[732],{"type":26,"value":733},"Parentheses accomplish the same thing in math:",{"type":21,"tag":239,"props":735,"children":738},{"className":736,"code":737,"language":26},[242],"a = 12 × (b + 2.4 × (g + 2))\n",[739],{"type":21,"tag":246,"props":740,"children":741},{"__ignoreMap":8},[742],{"type":26,"value":737},{"type":21,"tag":22,"props":744,"children":745},{},[746],{"type":26,"value":747},"You can see here that we have three levels of nesting:",{"type":21,"tag":183,"props":749,"children":750},{},[751,756,761],{"type":21,"tag":187,"props":752,"children":753},{},[754],{"type":26,"value":755},"The entire right-hand-side of the equation provides the outermost suitcase,",{"type":21,"tag":187,"props":757,"children":758},{},[759],{"type":26,"value":760},"The first open parenthesis declares the start of the first inner suitcase, and",{"type":21,"tag":187,"props":762,"children":763},{},[764],{"type":26,"value":765},"The second open parenthesis declares the start of the innermost suitcase.",{"type":21,"tag":22,"props":767,"children":768},{},[769],{"type":26,"value":770},"The nesting you see here is quite easy to follow, because it is accomplished using just one pair of symbols: the open and closed parentheses.",{"type":21,"tag":22,"props":772,"children":773},{},[774],{"type":26,"value":775},"Nesting within text strings is a bit more complicated because of a key difference: parentheses in math formulas are always pragmatic, never literal.",{"type":21,"tag":22,"props":777,"children":778},{},[779],{"type":26,"value":780},"Quotation marks in the context of programming, on the other hand, may be either literal or pragmatic.",{"type":21,"tag":56,"props":782,"children":784},{"id":783},"bringing-it-all-together",[785],{"type":26,"value":786},"Bringing it all together",{"type":21,"tag":22,"props":788,"children":789},{},[790],{"type":26,"value":791},"Reviewing what we know so far, we've discussed three really important ideas:",{"type":21,"tag":183,"props":793,"children":794},{},[795,800,805],{"type":21,"tag":187,"props":796,"children":797},{},[798],{"type":26,"value":799},"Strings are text that computer programs treat as whole and distinct entities. They are like suitcases.",{"type":21,"tag":187,"props":801,"children":802},{},[803],{"type":26,"value":804},"The start of a string is declared when a double quote (\") is encountered within the computer code. The end of the string is declared when the exact same symbol (\") is encountered again. (Other symbols such as single quotes, backticks or slashes are sometimes used, but for simplicity, let's stick with double quotes).",{"type":21,"tag":187,"props":806,"children":807},{},[808,810,814],{"type":26,"value":809},"Strings, like suitcases, and like math formulas, may contain sub-objects. This is called ",{"type":21,"tag":33,"props":811,"children":812},{},[813],{"type":26,"value":719},{"type":26,"value":551},{"type":21,"tag":22,"props":816,"children":817},{},[818],{"type":26,"value":819},"Reflecting and meditating on these three ideas and their implications raises a difficulty: How can points #2 and #3 above co-exist?",{"type":21,"tag":22,"props":821,"children":822},{},[823,825,830],{"type":26,"value":824},"For instance, what if I wanted to modify my Hello World program above so that I have a nested string? Instead of \"Hello World\", suppose I wanted my program to give me a piece of advice inspired by the TV show ",{"type":21,"tag":33,"props":826,"children":827},{},[828],{"type":26,"value":829},"Arrested Development",{"type":26,"value":831},":",{"type":21,"tag":239,"props":833,"children":836},{"className":834,"code":835,"language":26},[242],"Just remember: \"There's always money in the banana stand!\"\n",[837],{"type":21,"tag":246,"props":838,"children":839},{"__ignoreMap":8},[840],{"type":26,"value":835},{"type":21,"tag":22,"props":842,"children":843},{},[844],{"type":26,"value":845},"I would have to replace the contents of the \"Hello World\" string with the above, to give me something like this:",{"type":21,"tag":239,"props":847,"children":849},{"className":447,"code":848,"language":449,"meta":8,"style":8},"#!/usr/bin/python3\n\nprint(\"Just remember: \"There's always money in the banana stand!\"\")\n",[850],{"type":21,"tag":246,"props":851,"children":852},{"__ignoreMap":8},[853,860,867],{"type":21,"tag":455,"props":854,"children":855},{"class":457,"line":458},[856],{"type":21,"tag":455,"props":857,"children":858},{"style":462},[859],{"type":26,"value":465},{"type":21,"tag":455,"props":861,"children":862},{"class":457,"line":336},[863],{"type":21,"tag":455,"props":864,"children":865},{"emptyLinePlaceholder":471},[866],{"type":26,"value":474},{"type":21,"tag":455,"props":868,"children":869},{"class":457,"line":333},[870,874,878,883,888],{"type":21,"tag":455,"props":871,"children":872},{"style":480},[873],{"type":26,"value":483},{"type":21,"tag":455,"props":875,"children":876},{"style":486},[877],{"type":26,"value":489},{"type":21,"tag":455,"props":879,"children":880},{"style":492},[881],{"type":26,"value":882},"\"Just remember: \"",{"type":21,"tag":455,"props":884,"children":885},{"style":486},[886],{"type":26,"value":887},"There",{"type":21,"tag":455,"props":889,"children":890},{"style":492},[891],{"type":26,"value":892},"'s always money in the banana stand!\"\")\n",{"type":21,"tag":22,"props":894,"children":895},{},[896,898,903],{"type":26,"value":897},"However, pairing up the quotes from left to right as dictated by principle #2 above, disappointingly results in two ",{"type":21,"tag":33,"props":899,"children":900},{},[901],{"type":26,"value":902},"non",{"type":26,"value":904},"-nested strings with some gibberish between them.",{"type":21,"tag":22,"props":906,"children":907},{},[908,910,915],{"type":26,"value":909},"Scanning left to right, the first string (between the first two double quotes) is ",{"type":21,"tag":246,"props":911,"children":913},{"className":912},[],[914],{"type":26,"value":882},{"type":26,"value":916},". Of course, we meant for both of these to be opening quotation marks (which would be unambiguous if we were using parentheses), but because we're forced to use the same character for both opening and closing quotes, we have to rely on left-to-right scanning to pair them up and decide which one is an opening quote and which one is a closing quote.",{"type":21,"tag":22,"props":918,"children":919},{},[920,922,928],{"type":26,"value":921},"The second string, not recognized until another pair of quotation marks is encountered, is empty, ",{"type":21,"tag":246,"props":923,"children":925},{"className":924},[],[926],{"type":26,"value":927},"\"\"",{"type":26,"value":551},{"type":21,"tag":22,"props":930,"children":931},{},[932],{"type":26,"value":933},"Between the two strings is the text (not a string!) \"There's always money in the banana stand!\", which the Python interpreter is not going to know how to deal with. We meant to put one suitcase inside another, but instead we've created two suitcases, one of which is empty, and between them, an armful of loose items which is just going to gum up the works of the baggage carousel.",{"type":21,"tag":22,"props":935,"children":936},{},[937],{"type":21,"tag":510,"props":938,"children":940},{"alt":8,"src":939},"/alalande/2023-1/img/02_unescaped.gif",[],{"type":21,"tag":22,"props":942,"children":943},{},[944],{"type":26,"value":945},"So, how can we have nesting (#3) when we must close one suitcase before creating a new one?",{"type":21,"tag":56,"props":947,"children":949},{"id":948},"escaping",[950],{"type":26,"value":951},"Escaping",{"type":21,"tag":22,"props":953,"children":954},{},[955,957,961],{"type":26,"value":956},"Now, finally, we arrive at the concept of ",{"type":21,"tag":33,"props":958,"children":959},{},[960],{"type":26,"value":948},{"type":26,"value":551},{"type":21,"tag":22,"props":963,"children":964},{},[965],{"type":26,"value":966},"When we use double quotes to mark the start and stop of a string, we are using quotes in their pragmatic sense. These quotes are telling Python something: while scanning text left to right, start then stop treating this text as a string.",{"type":21,"tag":22,"props":968,"children":969},{},[970,972,976],{"type":26,"value":971},"What we need, in order to reconcile points #2 and #3 above, is a method of turning a pragmatic double quote into a literal one. This is known as ",{"type":21,"tag":33,"props":973,"children":974},{},[975],{"type":26,"value":948},{"type":26,"value":551},{"type":21,"tag":22,"props":978,"children":979},{},[980,982,987,989,994],{"type":26,"value":981},"In our banana stand string, to accomplish nesting, we need to somehow tell Python to ignore the string-demarcating behaviour of the two inner double quotes, and to treat them like any other letter, number or character within a string. We need to switch those two quotes from ",{"type":21,"tag":33,"props":983,"children":984},{},[985],{"type":26,"value":986},"pragmatic quotes",{"type":26,"value":988}," to ",{"type":21,"tag":33,"props":990,"children":991},{},[992],{"type":26,"value":993},"literal quotes",{"type":26,"value":551},{"type":21,"tag":22,"props":996,"children":997},{},[998,1000,1005,1007,1013],{"type":26,"value":999},"We do this with an ",{"type":21,"tag":33,"props":1001,"children":1002},{},[1003],{"type":26,"value":1004},"escape character",{"type":26,"value":1006},". In Python, this is the backslash: ",{"type":21,"tag":246,"props":1008,"children":1010},{"className":1009},[],[1011],{"type":26,"value":1012},"\\",{"type":26,"value":551},{"type":21,"tag":22,"props":1015,"children":1016},{},[1017],{"type":26,"value":1018},"Our Python program, with the inner quotes escaped appropriately, now looks like this:",{"type":21,"tag":239,"props":1020,"children":1022},{"className":447,"code":1021,"language":449,"meta":8,"style":8},"#!/usr/bin/python3\n\nprint(\"Just remember: \\\"There's always money in the banana stand!\\\"\")\n",[1023],{"type":21,"tag":246,"props":1024,"children":1025},{"__ignoreMap":8},[1026,1033,1040],{"type":21,"tag":455,"props":1027,"children":1028},{"class":457,"line":458},[1029],{"type":21,"tag":455,"props":1030,"children":1031},{"style":462},[1032],{"type":26,"value":465},{"type":21,"tag":455,"props":1034,"children":1035},{"class":457,"line":336},[1036],{"type":21,"tag":455,"props":1037,"children":1038},{"emptyLinePlaceholder":471},[1039],{"type":26,"value":474},{"type":21,"tag":455,"props":1041,"children":1042},{"class":457,"line":333},[1043,1047,1051,1056,1061,1066,1070,1075],{"type":21,"tag":455,"props":1044,"children":1045},{"style":480},[1046],{"type":26,"value":483},{"type":21,"tag":455,"props":1048,"children":1049},{"style":486},[1050],{"type":26,"value":489},{"type":21,"tag":455,"props":1052,"children":1053},{"style":492},[1054],{"type":26,"value":1055},"\"Just remember: ",{"type":21,"tag":455,"props":1057,"children":1058},{"style":480},[1059],{"type":26,"value":1060},"\\\"",{"type":21,"tag":455,"props":1062,"children":1063},{"style":492},[1064],{"type":26,"value":1065},"There's always money in the banana stand!",{"type":21,"tag":455,"props":1067,"children":1068},{"style":480},[1069],{"type":26,"value":1060},{"type":21,"tag":455,"props":1071,"children":1072},{"style":492},[1073],{"type":26,"value":1074},"\"",{"type":21,"tag":455,"props":1076,"children":1077},{"style":486},[1078],{"type":26,"value":500},{"type":21,"tag":22,"props":1080,"children":1081},{},[1082],{"type":26,"value":1083},"While somewhat uglier and less readable to humans, this is unambiguous to the Python interpreter: the inner two quotes are part of the string, and you can safely ignore them when looking for the second double quote that closes the suitcase.",{"type":21,"tag":22,"props":1085,"children":1086},{},[1087],{"type":26,"value":1088},"Naturally, I don't want my final output to contain any ugly backslashes, which is why escape characters are removed before the string gets displayed, sent as a text message, read aloud, or otherwise.",{"type":21,"tag":22,"props":1090,"children":1091},{},[1092],{"type":21,"tag":510,"props":1093,"children":1095},{"alt":8,"src":1094},"/alalande/2023-1/img/03_escaped.gif",[],{"type":21,"tag":22,"props":1097,"children":1098},{},[1099],{"type":26,"value":1100},"Now that we understand what strings are, how strings are delimited in a left-to-right way, what nesting is, and how escape characters allow strings to be nested within one another, there's a final distinction to master:",{"type":21,"tag":22,"props":1102,"children":1103},{},[1104,1106,1110,1111,1115],{"type":26,"value":1105},"Are escape characters ",{"type":21,"tag":33,"props":1107,"children":1108},{},[1109],{"type":26,"value":391},{"type":26,"value":411},{"type":21,"tag":33,"props":1112,"children":1113},{},[1114],{"type":26,"value":384},{"type":26,"value":1116},"?",{"type":21,"tag":22,"props":1118,"children":1119},{},[1120],{"type":26,"value":1121},"Escape characters are pragmatic. We can see this in two ways:",{"type":21,"tag":183,"props":1123,"children":1124},{},[1125,1136],{"type":21,"tag":187,"props":1126,"children":1127},{},[1128,1130,1134],{"type":26,"value":1129},"They actually tell Python to ",{"type":21,"tag":33,"props":1131,"children":1132},{},[1133],{"type":26,"value":537},{"type":26,"value":1135}," something (in this case, \"ignore the quotation marks that directly follow\"), and",{"type":21,"tag":187,"props":1137,"children":1138},{},[1139],{"type":26,"value":1140},"They could not possibly be literal because they are removed from the string.",{"type":21,"tag":22,"props":1142,"children":1143},{},[1144],{"type":26,"value":1145},"So, what happens if I want my program to output something that contains a backslash?",{"type":21,"tag":239,"props":1147,"children":1150},{"className":1148,"code":1149,"language":26},[242],"The file you are looking for is found under C:\\Windows\\System32\n",[1151],{"type":21,"tag":246,"props":1152,"children":1153},{"__ignoreMap":8},[1154],{"type":26,"value":1149},{"type":21,"tag":22,"props":1156,"children":1157},{},[1158],{"type":26,"value":1159},"The answer is logical. Since the escape character tells Python to ignore the following character (treat it as a literal), we can use the escape character on itself, simply by doubling it!",{"type":21,"tag":239,"props":1161,"children":1163},{"className":447,"code":1162,"language":449,"meta":8,"style":8},"#!/usr/bin/python3\n\nprint(\"The file you are looking for is found under C:\\\\Windows\\\\System32\")\n",[1164],{"type":21,"tag":246,"props":1165,"children":1166},{"__ignoreMap":8},[1167,1174,1181],{"type":21,"tag":455,"props":1168,"children":1169},{"class":457,"line":458},[1170],{"type":21,"tag":455,"props":1171,"children":1172},{"style":462},[1173],{"type":26,"value":465},{"type":21,"tag":455,"props":1175,"children":1176},{"class":457,"line":336},[1177],{"type":21,"tag":455,"props":1178,"children":1179},{"emptyLinePlaceholder":471},[1180],{"type":26,"value":474},{"type":21,"tag":455,"props":1182,"children":1183},{"class":457,"line":333},[1184,1188,1192,1197,1202,1207,1211,1216],{"type":21,"tag":455,"props":1185,"children":1186},{"style":480},[1187],{"type":26,"value":483},{"type":21,"tag":455,"props":1189,"children":1190},{"style":486},[1191],{"type":26,"value":489},{"type":21,"tag":455,"props":1193,"children":1194},{"style":492},[1195],{"type":26,"value":1196},"\"The file you are looking for is found under C:",{"type":21,"tag":455,"props":1198,"children":1199},{"style":480},[1200],{"type":26,"value":1201},"\\\\",{"type":21,"tag":455,"props":1203,"children":1204},{"style":492},[1205],{"type":26,"value":1206},"Windows",{"type":21,"tag":455,"props":1208,"children":1209},{"style":480},[1210],{"type":26,"value":1201},{"type":21,"tag":455,"props":1212,"children":1213},{"style":492},[1214],{"type":26,"value":1215},"System32\"",{"type":21,"tag":455,"props":1217,"children":1218},{"style":486},[1219],{"type":26,"value":500},{"type":21,"tag":22,"props":1221,"children":1222},{},[1223],{"type":26,"value":1224},"When the backslashes are duplicated: the first in a pair tells Python to ignore the second in the pair, or at least to treat it literally.",{"type":21,"tag":56,"props":1226,"children":1228},{"id":1227},"closing-thoughts-why-this-is-important-to-programmers",[1229],{"type":26,"value":1230},"Closing thoughts: Why this is important to programmers",{"type":21,"tag":22,"props":1232,"children":1233},{},[1234],{"type":26,"value":1235},"Escaping text is important to understand because it is central to the art and craft of programming, just as luggage is central to the experience of traveling.",{"type":21,"tag":22,"props":1237,"children":1238},{},[1239,1241,1245,1247,1252],{"type":26,"value":1240},"More broadly, strings, suitcases, trunks, cans, enveloppes, cardboard boxes and many other types of containers are designed to shuttle contents through a system. Their outsides are meant to be handled ",{"type":21,"tag":33,"props":1242,"children":1243},{},[1244],{"type":26,"value":596},{"type":26,"value":1246}," the system, while their contents are meant to be kept separate ",{"type":21,"tag":33,"props":1248,"children":1249},{},[1250],{"type":26,"value":1251},"from",{"type":26,"value":1253}," the system. Both the system and the contents of the container benefit from a sturdy and leak-proof divider between them.",{"type":21,"tag":22,"props":1255,"children":1256},{},[1257],{"type":26,"value":1258},"Picture a malformed string as a suitcase missing one side, or a paint can with a leak, and it's easy to see the kind of havoc it could wreak making its way to where it needs to go.",{"type":21,"tag":22,"props":1260,"children":1261},{},[1262,1264,1271],{"type":26,"value":1263},"An improperly-escaped string can be just as bad. In fact, Apple's installer for iTunes 2 (released in 2001) contained a string which was not properly escaped, and this caused ",{"type":21,"tag":322,"props":1265,"children":1268},{"href":1266,"rel":1267},"https://macosx.com/threads/itunes-2-did-it-erase-your-hd-or-not.8733/post-42972",[326],[1269],{"type":26,"value":1270},"a bug which erased several users' hard drives",{"type":26,"value":1272},"!",{"type":21,"tag":22,"props":1274,"children":1275},{},[1276],{"type":26,"value":1277},"This is why escaping text matters. A programmer who doesn't craft strings carefully risks the introduction of bugs, errors, and even security vulnerabilities anywhere strings are used, which is to say almost anywhere.",{"type":21,"tag":1279,"props":1280,"children":1281},"style",{},[1282],{"type":26,"value":1283},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":8,"searchDepth":333,"depth":333,"links":1285},[1286,1287,1288,1289,1290,1291],{"id":396,"depth":336,"text":399},{"id":628,"depth":336,"text":631},{"id":670,"depth":336,"text":673},{"id":783,"depth":336,"text":786},{"id":948,"depth":336,"text":951},{"id":1227,"depth":336,"text":1230},"content:alalande:2023-1:escaping_text.md","alalande/2023-1/escaping_text.md","alalande/2023-1/escaping_text",{"user":350,"name":351},{"_path":1297,"_dir":1298,"_draft":7,"_partial":7,"_locale":8,"title":1299,"description":1300,"tags":1301,"image":1303,"publishDate":1304,"excerpt":1300,"body":1305,"_type":343,"_id":2791,"_source":345,"_file":2792,"_stem":2793,"_extension":348,"author":2794},"/phendry/2023-01-19/badcode","2023-01-19","\"Bad\" Code (Or, Why Software Development is Hard)","Recently, the Dutch government open-sourced the iOS application for their \"DigiD\" authentication service. A tweet with a snippet of that source code, presumably making fun of it, blew up into a debate about whether mocking it is even justified. The amount of debate over such a simple snippet of code highlights, in my mind, just how tricky software development can be.",[1302,15],"security","/phendry/2023-01-19/img/Bad Code.png","2024-01-15",{"type":18,"children":1306,"toc":2786},[1307,1330,1335,2003,2031,2065,2251,2256,2261,2275,2280,2285,2290,2296,2301,2354,2373,2385,2390,2396,2408,2420,2425,2720,2749,2755,2760,2782],{"type":21,"tag":22,"props":1308,"children":1309},{},[1310,1312,1319,1321,1328],{"type":26,"value":1311},"Recently, the Dutch government open-sourced the iOS application for their \"DigiD\" authentication service. A ",{"type":21,"tag":322,"props":1313,"children":1316},{"href":1314,"rel":1315},"https://twitter.com/JeroenFrijters/status/1615204074588180481",[326],[1317],{"type":26,"value":1318},"tweet",{"type":26,"value":1320}," with a snippet of that source code, presumably making fun of it, blew up into ",{"type":21,"tag":322,"props":1322,"children":1325},{"href":1323,"rel":1324},"https://old.reddit.com/r/ProgrammerHumor/comments/10dh6x1/very_efficient_code/",[326],[1326],{"type":26,"value":1327},"a debate",{"type":26,"value":1329}," about whether mocking it is even justified. The amount of debate over such a simple snippet of code highlights, in my mind, just how tricky software development can be.",{"type":21,"tag":22,"props":1331,"children":1332},{},[1333],{"type":26,"value":1334},"Here is the code in question, written in C#:",{"type":21,"tag":239,"props":1336,"children":1340},{"className":1337,"code":1338,"language":1339,"meta":8,"style":8},"language-csharp shiki shiki-themes github-light github-dark","private static string GetPercentageRounds(double percentage)\n{\n    if (percentage == 0)\n        return \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.0 && percentage \u003C= 0.1)\n        return \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.1 && percentage \u003C= 0.2)\n        return \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.2 && percentage \u003C= 0.3)\n        return \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.3 && percentage \u003C= 0.4)\n        return \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\";\n    if (percentage > 0.4 && percentage \u003C= 0.5)\n        return \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\";\n    if (percentage > 0.5 && percentage \u003C= 0.6)\n        return \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\";\n    if (percentage > 0.6 && percentage \u003C= 0.7)\n        return \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\";\n    if (percentage > 0.7 && percentage \u003C= 0.8)\n        return \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\";\n    if (percentage > 0.8 && percentage \u003C= 0.9)\n        return \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\";\n\n    return \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\";\n}\n","csharp",[1341],{"type":21,"tag":246,"props":1342,"children":1343},{"__ignoreMap":8},[1344,1387,1395,1422,1441,1487,1504,1545,1562,1603,1620,1661,1678,1719,1736,1777,1794,1835,1852,1893,1910,1951,1968,1976,1994],{"type":21,"tag":455,"props":1345,"children":1346},{"class":457,"line":458},[1347,1353,1358,1363,1369,1373,1378,1383],{"type":21,"tag":455,"props":1348,"children":1350},{"style":1349},"--shiki-default:#D73A49;--shiki-dark:#F97583",[1351],{"type":26,"value":1352},"private",{"type":21,"tag":455,"props":1354,"children":1355},{"style":1349},[1356],{"type":26,"value":1357}," static",{"type":21,"tag":455,"props":1359,"children":1360},{"style":1349},[1361],{"type":26,"value":1362}," string",{"type":21,"tag":455,"props":1364,"children":1366},{"style":1365},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[1367],{"type":26,"value":1368}," GetPercentageRounds",{"type":21,"tag":455,"props":1370,"children":1371},{"style":486},[1372],{"type":26,"value":489},{"type":21,"tag":455,"props":1374,"children":1375},{"style":1349},[1376],{"type":26,"value":1377},"double",{"type":21,"tag":455,"props":1379,"children":1380},{"style":1365},[1381],{"type":26,"value":1382}," percentage",{"type":21,"tag":455,"props":1384,"children":1385},{"style":486},[1386],{"type":26,"value":500},{"type":21,"tag":455,"props":1388,"children":1389},{"class":457,"line":336},[1390],{"type":21,"tag":455,"props":1391,"children":1392},{"style":486},[1393],{"type":26,"value":1394},"{\n",{"type":21,"tag":455,"props":1396,"children":1397},{"class":457,"line":333},[1398,1403,1408,1413,1418],{"type":21,"tag":455,"props":1399,"children":1400},{"style":1349},[1401],{"type":26,"value":1402},"    if",{"type":21,"tag":455,"props":1404,"children":1405},{"style":486},[1406],{"type":26,"value":1407}," (percentage ",{"type":21,"tag":455,"props":1409,"children":1410},{"style":1349},[1411],{"type":26,"value":1412},"==",{"type":21,"tag":455,"props":1414,"children":1415},{"style":480},[1416],{"type":26,"value":1417}," 0",{"type":21,"tag":455,"props":1419,"children":1420},{"style":486},[1421],{"type":26,"value":500},{"type":21,"tag":455,"props":1423,"children":1425},{"class":457,"line":1424},4,[1426,1431,1436],{"type":21,"tag":455,"props":1427,"children":1428},{"style":1349},[1429],{"type":26,"value":1430},"        return",{"type":21,"tag":455,"props":1432,"children":1433},{"style":492},[1434],{"type":26,"value":1435}," \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":1437,"children":1438},{"style":486},[1439],{"type":26,"value":1440},";\n",{"type":21,"tag":455,"props":1442,"children":1444},{"class":457,"line":1443},5,[1445,1449,1453,1458,1463,1468,1473,1478,1483],{"type":21,"tag":455,"props":1446,"children":1447},{"style":1349},[1448],{"type":26,"value":1402},{"type":21,"tag":455,"props":1450,"children":1451},{"style":486},[1452],{"type":26,"value":1407},{"type":21,"tag":455,"props":1454,"children":1455},{"style":1349},[1456],{"type":26,"value":1457},">",{"type":21,"tag":455,"props":1459,"children":1460},{"style":480},[1461],{"type":26,"value":1462}," 0.0",{"type":21,"tag":455,"props":1464,"children":1465},{"style":1349},[1466],{"type":26,"value":1467}," &&",{"type":21,"tag":455,"props":1469,"children":1470},{"style":486},[1471],{"type":26,"value":1472}," percentage ",{"type":21,"tag":455,"props":1474,"children":1475},{"style":1349},[1476],{"type":26,"value":1477},"\u003C=",{"type":21,"tag":455,"props":1479,"children":1480},{"style":480},[1481],{"type":26,"value":1482}," 0.1",{"type":21,"tag":455,"props":1484,"children":1485},{"style":486},[1486],{"type":26,"value":500},{"type":21,"tag":455,"props":1488,"children":1490},{"class":457,"line":1489},6,[1491,1495,1500],{"type":21,"tag":455,"props":1492,"children":1493},{"style":1349},[1494],{"type":26,"value":1430},{"type":21,"tag":455,"props":1496,"children":1497},{"style":492},[1498],{"type":26,"value":1499}," \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":1501,"children":1502},{"style":486},[1503],{"type":26,"value":1440},{"type":21,"tag":455,"props":1505,"children":1507},{"class":457,"line":1506},7,[1508,1512,1516,1520,1524,1528,1532,1536,1541],{"type":21,"tag":455,"props":1509,"children":1510},{"style":1349},[1511],{"type":26,"value":1402},{"type":21,"tag":455,"props":1513,"children":1514},{"style":486},[1515],{"type":26,"value":1407},{"type":21,"tag":455,"props":1517,"children":1518},{"style":1349},[1519],{"type":26,"value":1457},{"type":21,"tag":455,"props":1521,"children":1522},{"style":480},[1523],{"type":26,"value":1482},{"type":21,"tag":455,"props":1525,"children":1526},{"style":1349},[1527],{"type":26,"value":1467},{"type":21,"tag":455,"props":1529,"children":1530},{"style":486},[1531],{"type":26,"value":1472},{"type":21,"tag":455,"props":1533,"children":1534},{"style":1349},[1535],{"type":26,"value":1477},{"type":21,"tag":455,"props":1537,"children":1538},{"style":480},[1539],{"type":26,"value":1540}," 0.2",{"type":21,"tag":455,"props":1542,"children":1543},{"style":486},[1544],{"type":26,"value":500},{"type":21,"tag":455,"props":1546,"children":1548},{"class":457,"line":1547},8,[1549,1553,1558],{"type":21,"tag":455,"props":1550,"children":1551},{"style":1349},[1552],{"type":26,"value":1430},{"type":21,"tag":455,"props":1554,"children":1555},{"style":492},[1556],{"type":26,"value":1557}," \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":1559,"children":1560},{"style":486},[1561],{"type":26,"value":1440},{"type":21,"tag":455,"props":1563,"children":1565},{"class":457,"line":1564},9,[1566,1570,1574,1578,1582,1586,1590,1594,1599],{"type":21,"tag":455,"props":1567,"children":1568},{"style":1349},[1569],{"type":26,"value":1402},{"type":21,"tag":455,"props":1571,"children":1572},{"style":486},[1573],{"type":26,"value":1407},{"type":21,"tag":455,"props":1575,"children":1576},{"style":1349},[1577],{"type":26,"value":1457},{"type":21,"tag":455,"props":1579,"children":1580},{"style":480},[1581],{"type":26,"value":1540},{"type":21,"tag":455,"props":1583,"children":1584},{"style":1349},[1585],{"type":26,"value":1467},{"type":21,"tag":455,"props":1587,"children":1588},{"style":486},[1589],{"type":26,"value":1472},{"type":21,"tag":455,"props":1591,"children":1592},{"style":1349},[1593],{"type":26,"value":1477},{"type":21,"tag":455,"props":1595,"children":1596},{"style":480},[1597],{"type":26,"value":1598}," 0.3",{"type":21,"tag":455,"props":1600,"children":1601},{"style":486},[1602],{"type":26,"value":500},{"type":21,"tag":455,"props":1604,"children":1606},{"class":457,"line":1605},10,[1607,1611,1616],{"type":21,"tag":455,"props":1608,"children":1609},{"style":1349},[1610],{"type":26,"value":1430},{"type":21,"tag":455,"props":1612,"children":1613},{"style":492},[1614],{"type":26,"value":1615}," \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":1617,"children":1618},{"style":486},[1619],{"type":26,"value":1440},{"type":21,"tag":455,"props":1621,"children":1623},{"class":457,"line":1622},11,[1624,1628,1632,1636,1640,1644,1648,1652,1657],{"type":21,"tag":455,"props":1625,"children":1626},{"style":1349},[1627],{"type":26,"value":1402},{"type":21,"tag":455,"props":1629,"children":1630},{"style":486},[1631],{"type":26,"value":1407},{"type":21,"tag":455,"props":1633,"children":1634},{"style":1349},[1635],{"type":26,"value":1457},{"type":21,"tag":455,"props":1637,"children":1638},{"style":480},[1639],{"type":26,"value":1598},{"type":21,"tag":455,"props":1641,"children":1642},{"style":1349},[1643],{"type":26,"value":1467},{"type":21,"tag":455,"props":1645,"children":1646},{"style":486},[1647],{"type":26,"value":1472},{"type":21,"tag":455,"props":1649,"children":1650},{"style":1349},[1651],{"type":26,"value":1477},{"type":21,"tag":455,"props":1653,"children":1654},{"style":480},[1655],{"type":26,"value":1656}," 0.4",{"type":21,"tag":455,"props":1658,"children":1659},{"style":486},[1660],{"type":26,"value":500},{"type":21,"tag":455,"props":1662,"children":1664},{"class":457,"line":1663},12,[1665,1669,1674],{"type":21,"tag":455,"props":1666,"children":1667},{"style":1349},[1668],{"type":26,"value":1430},{"type":21,"tag":455,"props":1670,"children":1671},{"style":492},[1672],{"type":26,"value":1673}," \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":1675,"children":1676},{"style":486},[1677],{"type":26,"value":1440},{"type":21,"tag":455,"props":1679,"children":1681},{"class":457,"line":1680},13,[1682,1686,1690,1694,1698,1702,1706,1710,1715],{"type":21,"tag":455,"props":1683,"children":1684},{"style":1349},[1685],{"type":26,"value":1402},{"type":21,"tag":455,"props":1687,"children":1688},{"style":486},[1689],{"type":26,"value":1407},{"type":21,"tag":455,"props":1691,"children":1692},{"style":1349},[1693],{"type":26,"value":1457},{"type":21,"tag":455,"props":1695,"children":1696},{"style":480},[1697],{"type":26,"value":1656},{"type":21,"tag":455,"props":1699,"children":1700},{"style":1349},[1701],{"type":26,"value":1467},{"type":21,"tag":455,"props":1703,"children":1704},{"style":486},[1705],{"type":26,"value":1472},{"type":21,"tag":455,"props":1707,"children":1708},{"style":1349},[1709],{"type":26,"value":1477},{"type":21,"tag":455,"props":1711,"children":1712},{"style":480},[1713],{"type":26,"value":1714}," 0.5",{"type":21,"tag":455,"props":1716,"children":1717},{"style":486},[1718],{"type":26,"value":500},{"type":21,"tag":455,"props":1720,"children":1722},{"class":457,"line":1721},14,[1723,1727,1732],{"type":21,"tag":455,"props":1724,"children":1725},{"style":1349},[1726],{"type":26,"value":1430},{"type":21,"tag":455,"props":1728,"children":1729},{"style":492},[1730],{"type":26,"value":1731}," \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":1733,"children":1734},{"style":486},[1735],{"type":26,"value":1440},{"type":21,"tag":455,"props":1737,"children":1739},{"class":457,"line":1738},15,[1740,1744,1748,1752,1756,1760,1764,1768,1773],{"type":21,"tag":455,"props":1741,"children":1742},{"style":1349},[1743],{"type":26,"value":1402},{"type":21,"tag":455,"props":1745,"children":1746},{"style":486},[1747],{"type":26,"value":1407},{"type":21,"tag":455,"props":1749,"children":1750},{"style":1349},[1751],{"type":26,"value":1457},{"type":21,"tag":455,"props":1753,"children":1754},{"style":480},[1755],{"type":26,"value":1714},{"type":21,"tag":455,"props":1757,"children":1758},{"style":1349},[1759],{"type":26,"value":1467},{"type":21,"tag":455,"props":1761,"children":1762},{"style":486},[1763],{"type":26,"value":1472},{"type":21,"tag":455,"props":1765,"children":1766},{"style":1349},[1767],{"type":26,"value":1477},{"type":21,"tag":455,"props":1769,"children":1770},{"style":480},[1771],{"type":26,"value":1772}," 0.6",{"type":21,"tag":455,"props":1774,"children":1775},{"style":486},[1776],{"type":26,"value":500},{"type":21,"tag":455,"props":1778,"children":1780},{"class":457,"line":1779},16,[1781,1785,1790],{"type":21,"tag":455,"props":1782,"children":1783},{"style":1349},[1784],{"type":26,"value":1430},{"type":21,"tag":455,"props":1786,"children":1787},{"style":492},[1788],{"type":26,"value":1789}," \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":1791,"children":1792},{"style":486},[1793],{"type":26,"value":1440},{"type":21,"tag":455,"props":1795,"children":1797},{"class":457,"line":1796},17,[1798,1802,1806,1810,1814,1818,1822,1826,1831],{"type":21,"tag":455,"props":1799,"children":1800},{"style":1349},[1801],{"type":26,"value":1402},{"type":21,"tag":455,"props":1803,"children":1804},{"style":486},[1805],{"type":26,"value":1407},{"type":21,"tag":455,"props":1807,"children":1808},{"style":1349},[1809],{"type":26,"value":1457},{"type":21,"tag":455,"props":1811,"children":1812},{"style":480},[1813],{"type":26,"value":1772},{"type":21,"tag":455,"props":1815,"children":1816},{"style":1349},[1817],{"type":26,"value":1467},{"type":21,"tag":455,"props":1819,"children":1820},{"style":486},[1821],{"type":26,"value":1472},{"type":21,"tag":455,"props":1823,"children":1824},{"style":1349},[1825],{"type":26,"value":1477},{"type":21,"tag":455,"props":1827,"children":1828},{"style":480},[1829],{"type":26,"value":1830}," 0.7",{"type":21,"tag":455,"props":1832,"children":1833},{"style":486},[1834],{"type":26,"value":500},{"type":21,"tag":455,"props":1836,"children":1838},{"class":457,"line":1837},18,[1839,1843,1848],{"type":21,"tag":455,"props":1840,"children":1841},{"style":1349},[1842],{"type":26,"value":1430},{"type":21,"tag":455,"props":1844,"children":1845},{"style":492},[1846],{"type":26,"value":1847}," \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\"",{"type":21,"tag":455,"props":1849,"children":1850},{"style":486},[1851],{"type":26,"value":1440},{"type":21,"tag":455,"props":1853,"children":1855},{"class":457,"line":1854},19,[1856,1860,1864,1868,1872,1876,1880,1884,1889],{"type":21,"tag":455,"props":1857,"children":1858},{"style":1349},[1859],{"type":26,"value":1402},{"type":21,"tag":455,"props":1861,"children":1862},{"style":486},[1863],{"type":26,"value":1407},{"type":21,"tag":455,"props":1865,"children":1866},{"style":1349},[1867],{"type":26,"value":1457},{"type":21,"tag":455,"props":1869,"children":1870},{"style":480},[1871],{"type":26,"value":1830},{"type":21,"tag":455,"props":1873,"children":1874},{"style":1349},[1875],{"type":26,"value":1467},{"type":21,"tag":455,"props":1877,"children":1878},{"style":486},[1879],{"type":26,"value":1472},{"type":21,"tag":455,"props":1881,"children":1882},{"style":1349},[1883],{"type":26,"value":1477},{"type":21,"tag":455,"props":1885,"children":1886},{"style":480},[1887],{"type":26,"value":1888}," 0.8",{"type":21,"tag":455,"props":1890,"children":1891},{"style":486},[1892],{"type":26,"value":500},{"type":21,"tag":455,"props":1894,"children":1896},{"class":457,"line":1895},20,[1897,1901,1906],{"type":21,"tag":455,"props":1898,"children":1899},{"style":1349},[1900],{"type":26,"value":1430},{"type":21,"tag":455,"props":1902,"children":1903},{"style":492},[1904],{"type":26,"value":1905}," \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\"",{"type":21,"tag":455,"props":1907,"children":1908},{"style":486},[1909],{"type":26,"value":1440},{"type":21,"tag":455,"props":1911,"children":1913},{"class":457,"line":1912},21,[1914,1918,1922,1926,1930,1934,1938,1942,1947],{"type":21,"tag":455,"props":1915,"children":1916},{"style":1349},[1917],{"type":26,"value":1402},{"type":21,"tag":455,"props":1919,"children":1920},{"style":486},[1921],{"type":26,"value":1407},{"type":21,"tag":455,"props":1923,"children":1924},{"style":1349},[1925],{"type":26,"value":1457},{"type":21,"tag":455,"props":1927,"children":1928},{"style":480},[1929],{"type":26,"value":1888},{"type":21,"tag":455,"props":1931,"children":1932},{"style":1349},[1933],{"type":26,"value":1467},{"type":21,"tag":455,"props":1935,"children":1936},{"style":486},[1937],{"type":26,"value":1472},{"type":21,"tag":455,"props":1939,"children":1940},{"style":1349},[1941],{"type":26,"value":1477},{"type":21,"tag":455,"props":1943,"children":1944},{"style":480},[1945],{"type":26,"value":1946}," 0.9",{"type":21,"tag":455,"props":1948,"children":1949},{"style":486},[1950],{"type":26,"value":500},{"type":21,"tag":455,"props":1952,"children":1954},{"class":457,"line":1953},22,[1955,1959,1964],{"type":21,"tag":455,"props":1956,"children":1957},{"style":1349},[1958],{"type":26,"value":1430},{"type":21,"tag":455,"props":1960,"children":1961},{"style":492},[1962],{"type":26,"value":1963}," \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\"",{"type":21,"tag":455,"props":1965,"children":1966},{"style":486},[1967],{"type":26,"value":1440},{"type":21,"tag":455,"props":1969,"children":1971},{"class":457,"line":1970},23,[1972],{"type":21,"tag":455,"props":1973,"children":1974},{"emptyLinePlaceholder":471},[1975],{"type":26,"value":474},{"type":21,"tag":455,"props":1977,"children":1979},{"class":457,"line":1978},24,[1980,1985,1990],{"type":21,"tag":455,"props":1981,"children":1982},{"style":1349},[1983],{"type":26,"value":1984},"    return",{"type":21,"tag":455,"props":1986,"children":1987},{"style":492},[1988],{"type":26,"value":1989}," \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\"",{"type":21,"tag":455,"props":1991,"children":1992},{"style":486},[1993],{"type":26,"value":1440},{"type":21,"tag":455,"props":1995,"children":1997},{"class":457,"line":1996},25,[1998],{"type":21,"tag":455,"props":1999,"children":2000},{"style":486},[2001],{"type":26,"value":2002},"}\n",{"type":21,"tag":22,"props":2004,"children":2005},{},[2006,2008,2014,2016,2022,2024,2029],{"type":26,"value":2007},"Let's ignore the lack of parameter validation (namely of ensuring that ",{"type":21,"tag":246,"props":2009,"children":2011},{"className":2010},[],[2012],{"type":26,"value":2013},"percentage",{"type":26,"value":2015}," is not ",{"type":21,"tag":246,"props":2017,"children":2019},{"className":2018},[],[2020],{"type":26,"value":2021},"NaN",{"type":26,"value":2023},", negative, or infinity), since it would be the same regardless of solution, and it's not uncommon to omit that sort of rigorous error handling in a ",{"type":21,"tag":246,"props":2025,"children":2027},{"className":2026},[],[2028],{"type":26,"value":1352},{"type":26,"value":2030}," method.",{"type":21,"tag":22,"props":2032,"children":2033},{},[2034,2036,2042,2044,2049,2051,2056,2058,2063],{"type":26,"value":2035},"Aside from that, well... it omits curly brackets in the ",{"type":21,"tag":246,"props":2037,"children":2039},{"className":2038},[],[2040],{"type":26,"value":2041},"if",{"type":26,"value":2043}," blocks, which is a style thing many people dislike. It's ",{"type":21,"tag":33,"props":2045,"children":2046},{},[2047],{"type":26,"value":2048},"very",{"type":26,"value":2050}," repetitious. It has unnecessary greater-than conditions (since each prior ",{"type":21,"tag":246,"props":2052,"children":2054},{"className":2053},[],[2055],{"type":26,"value":2041},{"type":26,"value":2057},"'s less-than-equal condition guarantees it). It does several more ",{"type":21,"tag":246,"props":2059,"children":2061},{"className":2060},[],[2062],{"type":26,"value":2041},{"type":26,"value":2064}," comparisons than it technically needs to. In fact, given that the output is linearly related to the input, this can be a one-liner with just a little bit of math; let's call it the \"math solution\".",{"type":21,"tag":239,"props":2066,"children":2068},{"className":1337,"code":2067,"language":1339,"meta":8,"style":8},"private static string GetPercentageRounds(double percentage)\n{\n    int stage = (int)Math.Ceiling(percentage * 10);\n    return new string('🔵', stage) + new string('⚪', 10 - stage);\n}\n",[2069],{"type":21,"tag":246,"props":2070,"children":2071},{"__ignoreMap":8},[2072,2107,2114,2172,2244],{"type":21,"tag":455,"props":2073,"children":2074},{"class":457,"line":458},[2075,2079,2083,2087,2091,2095,2099,2103],{"type":21,"tag":455,"props":2076,"children":2077},{"style":1349},[2078],{"type":26,"value":1352},{"type":21,"tag":455,"props":2080,"children":2081},{"style":1349},[2082],{"type":26,"value":1357},{"type":21,"tag":455,"props":2084,"children":2085},{"style":1349},[2086],{"type":26,"value":1362},{"type":21,"tag":455,"props":2088,"children":2089},{"style":1365},[2090],{"type":26,"value":1368},{"type":21,"tag":455,"props":2092,"children":2093},{"style":486},[2094],{"type":26,"value":489},{"type":21,"tag":455,"props":2096,"children":2097},{"style":1349},[2098],{"type":26,"value":1377},{"type":21,"tag":455,"props":2100,"children":2101},{"style":1365},[2102],{"type":26,"value":1382},{"type":21,"tag":455,"props":2104,"children":2105},{"style":486},[2106],{"type":26,"value":500},{"type":21,"tag":455,"props":2108,"children":2109},{"class":457,"line":336},[2110],{"type":21,"tag":455,"props":2111,"children":2112},{"style":486},[2113],{"type":26,"value":1394},{"type":21,"tag":455,"props":2115,"children":2116},{"class":457,"line":333},[2117,2122,2127,2132,2137,2142,2147,2152,2157,2162,2167],{"type":21,"tag":455,"props":2118,"children":2119},{"style":1349},[2120],{"type":26,"value":2121},"    int",{"type":21,"tag":455,"props":2123,"children":2124},{"style":1365},[2125],{"type":26,"value":2126}," stage",{"type":21,"tag":455,"props":2128,"children":2129},{"style":1349},[2130],{"type":26,"value":2131}," =",{"type":21,"tag":455,"props":2133,"children":2134},{"style":486},[2135],{"type":26,"value":2136}," (",{"type":21,"tag":455,"props":2138,"children":2139},{"style":1349},[2140],{"type":26,"value":2141},"int",{"type":21,"tag":455,"props":2143,"children":2144},{"style":486},[2145],{"type":26,"value":2146},")Math.",{"type":21,"tag":455,"props":2148,"children":2149},{"style":1365},[2150],{"type":26,"value":2151},"Ceiling",{"type":21,"tag":455,"props":2153,"children":2154},{"style":486},[2155],{"type":26,"value":2156},"(percentage ",{"type":21,"tag":455,"props":2158,"children":2159},{"style":1349},[2160],{"type":26,"value":2161},"*",{"type":21,"tag":455,"props":2163,"children":2164},{"style":480},[2165],{"type":26,"value":2166}," 10",{"type":21,"tag":455,"props":2168,"children":2169},{"style":486},[2170],{"type":26,"value":2171},");\n",{"type":21,"tag":455,"props":2173,"children":2174},{"class":457,"line":1424},[2175,2179,2184,2188,2192,2197,2202,2207,2211,2215,2219,2224,2229,2234,2239],{"type":21,"tag":455,"props":2176,"children":2177},{"style":1349},[2178],{"type":26,"value":1984},{"type":21,"tag":455,"props":2180,"children":2181},{"style":1349},[2182],{"type":26,"value":2183}," new",{"type":21,"tag":455,"props":2185,"children":2186},{"style":1349},[2187],{"type":26,"value":1362},{"type":21,"tag":455,"props":2189,"children":2190},{"style":486},[2191],{"type":26,"value":489},{"type":21,"tag":455,"props":2193,"children":2194},{"style":492},[2195],{"type":26,"value":2196},"'🔵'",{"type":21,"tag":455,"props":2198,"children":2199},{"style":486},[2200],{"type":26,"value":2201},", stage) ",{"type":21,"tag":455,"props":2203,"children":2204},{"style":1349},[2205],{"type":26,"value":2206},"+",{"type":21,"tag":455,"props":2208,"children":2209},{"style":1349},[2210],{"type":26,"value":2183},{"type":21,"tag":455,"props":2212,"children":2213},{"style":1349},[2214],{"type":26,"value":1362},{"type":21,"tag":455,"props":2216,"children":2217},{"style":486},[2218],{"type":26,"value":489},{"type":21,"tag":455,"props":2220,"children":2221},{"style":492},[2222],{"type":26,"value":2223},"'⚪'",{"type":21,"tag":455,"props":2225,"children":2226},{"style":486},[2227],{"type":26,"value":2228},", ",{"type":21,"tag":455,"props":2230,"children":2231},{"style":480},[2232],{"type":26,"value":2233},"10",{"type":21,"tag":455,"props":2235,"children":2236},{"style":1349},[2237],{"type":26,"value":2238}," -",{"type":21,"tag":455,"props":2240,"children":2241},{"style":486},[2242],{"type":26,"value":2243}," stage);\n",{"type":21,"tag":455,"props":2245,"children":2246},{"class":457,"line":1443},[2247],{"type":21,"tag":455,"props":2248,"children":2249},{"style":486},[2250],{"type":26,"value":2002},{"type":21,"tag":22,"props":2252,"children":2253},{},[2254],{"type":26,"value":2255},"Repetition avoided; 21 lines reduced to 2. Problem solved...",{"type":21,"tag":22,"props":2257,"children":2258},{},[2259],{"type":26,"value":2260},"...although if I were come back to that code in a month's time, I'd definitely have to stare at it longer to figure out what it does...",{"type":21,"tag":22,"props":2262,"children":2263},{},[2264,2266,2273],{"type":26,"value":2265},"...and I'm not 100% confident that the multiplication won't cause ",{"type":21,"tag":322,"props":2267,"children":2270},{"href":2268,"rel":2269},"https://floating-point-gui.de/errors/comparison/",[326],[2271],{"type":26,"value":2272},"floating point edge cases",{"type":26,"value":2274},"...",{"type":21,"tag":22,"props":2276,"children":2277},{},[2278],{"type":26,"value":2279},"...and I guess it's less flexible now (e.g. to adjust the color of individual dots by stage)...",{"type":21,"tag":22,"props":2281,"children":2282},{},[2283],{"type":26,"value":2284},"...and it's doing three string allocations now instead of returning a constant, so maybe that has performance implications...",{"type":21,"tag":22,"props":2286,"children":2287},{},[2288],{"type":26,"value":2289},"...hmm. Maybe we need to remind ourselves of what \"good\" code is.",{"type":21,"tag":56,"props":2291,"children":2293},{"id":2292},"question-what-is-good-code-answer-code-that-makes-the-right-tradeoffs",[2294],{"type":26,"value":2295},"Question: What is Good Code? Answer: Code That Makes The Right Tradeoffs.",{"type":21,"tag":22,"props":2297,"children":2298},{},[2299],{"type":26,"value":2300},"Good code has a number of qualities most developers would agree on:",{"type":21,"tag":183,"props":2302,"children":2303},{},[2304,2317,2334,2344],{"type":21,"tag":187,"props":2305,"children":2306},{},[2307,2309,2315],{"type":26,"value":2308},"Arguably the most important, ",{"type":21,"tag":2310,"props":2311,"children":2312},"strong",{},[2313],{"type":26,"value":2314},"good code is readable",{"type":26,"value":2316},". Code is written once but it is read many times, and understanding code is key to maintaining and extending it, so a solution that is easy to read and understand is usually better than one that is more clever.",{"type":21,"tag":187,"props":2318,"children":2319},{},[2320,2325,2327,2332],{"type":21,"tag":2310,"props":2321,"children":2322},{},[2323],{"type":26,"value":2324},"Good code is correct",{"type":26,"value":2326},"; it does what it's supposed to do. OK, ",{"type":21,"tag":33,"props":2328,"children":2329},{},[2330],{"type":26,"value":2331},"maybe",{"type":26,"value":2333}," this is more important than readability, but then again, it's easy to fix a bug in readable code.",{"type":21,"tag":187,"props":2335,"children":2336},{},[2337,2342],{"type":21,"tag":2310,"props":2338,"children":2339},{},[2340],{"type":26,"value":2341},"Good code is performant",{"type":26,"value":2343},"; it should not needlessly waste system resources.",{"type":21,"tag":187,"props":2345,"children":2346},{},[2347,2352],{"type":21,"tag":2310,"props":2348,"children":2349},{},[2350],{"type":26,"value":2351},"Good code is easy to write",{"type":26,"value":2353},", since productivity is important.",{"type":21,"tag":22,"props":2355,"children":2356},{},[2357,2359,2364,2366,2371],{"type":26,"value":2358},"Often, even ",{"type":21,"tag":33,"props":2360,"children":2361},{},[2362],{"type":26,"value":2363},"usually",{"type":26,"value":2365},", some of these qualities are at odds with one another. Making code more readable often means not choosing the most performant possible solution, and similarly, optimizing a solution often makes it less readable. Code that's easy to write is often not readable, performant ",{"type":21,"tag":33,"props":2367,"children":2368},{},[2369],{"type":26,"value":2370},"or",{"type":26,"value":2372}," correct.",{"type":21,"tag":22,"props":2374,"children":2375},{},[2376,2378,2383],{"type":26,"value":2377},"What this means is that what makes for \"good\" code always depends on the circumstances of that code: its purpose, the code surrounding it, the current and future priorities of the end client or product manager, etc. We don't even ",{"type":21,"tag":33,"props":2379,"children":2380},{},[2381],{"type":26,"value":2382},"know",{"type":26,"value":2384}," all those circumstances at the time the code is written, so we're doomed to never truly know what the best solution is, and just come up with the best we can.",{"type":21,"tag":22,"props":2386,"children":2387},{},[2388],{"type":26,"value":2389},"With that in mind, let's have another go at comparing the qualities of these two solutions, along with yet another one added to the mix.",{"type":21,"tag":56,"props":2391,"children":2393},{"id":2392},"comparing-solutions",[2394],{"type":26,"value":2395},"Comparing Solutions",{"type":21,"tag":22,"props":2397,"children":2398},{},[2399,2401,2406],{"type":26,"value":2400},"The \"original\" solution is understandable at a glance, it doesn't use any fancy features a junior developer might not understand, it doesn't do any dynamic string allocations, and it's flexible for a few types of future changes (e.g. changing dot colors per stage or per position). On the other hand, it's many lines of code, it's repetitious, and its many ",{"type":21,"tag":246,"props":2402,"children":2404},{"className":2403},[],[2405],{"type":26,"value":2041},{"type":26,"value":2407}," comparisons could be a performance problem if called in a tight loop.",{"type":21,"tag":22,"props":2409,"children":2410},{},[2411,2413,2418],{"type":26,"value":2412},"The \"math\" solution is about as compact as a solution could be, and consequently it's quite elegant too. On the other hand, that makes it harder to read, it's not as flexible for potential future changes, and its multiple ",{"type":21,"tag":246,"props":2414,"children":2416},{"className":2415},[],[2417],{"type":26,"value":549},{"type":26,"value":2419}," allocations could be its own sort of performance problem.",{"type":21,"tag":22,"props":2421,"children":2422},{},[2423],{"type":26,"value":2424},"The third one is a blend that we can call the \"array\" solution:",{"type":21,"tag":239,"props":2426,"children":2428},{"className":1337,"code":2427,"language":1339,"meta":8,"style":8},"private static readonly string[] Rounds = {\n    \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\",\n    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\",\n};\n\nprivate static string GetPercentageRounds(double percentage)\n{\n    return Rounds[(int)Math.Ceiling(percentage * (Rounds.Length - 1))];\n}\n",[2429],{"type":21,"tag":246,"props":2430,"children":2431},{"__ignoreMap":8},[2432,2471,2484,2496,2508,2520,2532,2544,2556,2568,2580,2592,2604,2612,2619,2654,2661,2713],{"type":21,"tag":455,"props":2433,"children":2434},{"class":457,"line":458},[2435,2439,2443,2448,2452,2457,2462,2466],{"type":21,"tag":455,"props":2436,"children":2437},{"style":1349},[2438],{"type":26,"value":1352},{"type":21,"tag":455,"props":2440,"children":2441},{"style":1349},[2442],{"type":26,"value":1357},{"type":21,"tag":455,"props":2444,"children":2445},{"style":1349},[2446],{"type":26,"value":2447}," readonly",{"type":21,"tag":455,"props":2449,"children":2450},{"style":1349},[2451],{"type":26,"value":1362},{"type":21,"tag":455,"props":2453,"children":2454},{"style":486},[2455],{"type":26,"value":2456},"[] ",{"type":21,"tag":455,"props":2458,"children":2459},{"style":1365},[2460],{"type":26,"value":2461},"Rounds",{"type":21,"tag":455,"props":2463,"children":2464},{"style":1349},[2465],{"type":26,"value":2131},{"type":21,"tag":455,"props":2467,"children":2468},{"style":486},[2469],{"type":26,"value":2470}," {\n",{"type":21,"tag":455,"props":2472,"children":2473},{"class":457,"line":336},[2474,2479],{"type":21,"tag":455,"props":2475,"children":2476},{"style":492},[2477],{"type":26,"value":2478},"    \"⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":2480,"children":2481},{"style":486},[2482],{"type":26,"value":2483},",\n",{"type":21,"tag":455,"props":2485,"children":2486},{"class":457,"line":333},[2487,2492],{"type":21,"tag":455,"props":2488,"children":2489},{"style":492},[2490],{"type":26,"value":2491},"    \"🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":2493,"children":2494},{"style":486},[2495],{"type":26,"value":2483},{"type":21,"tag":455,"props":2497,"children":2498},{"class":457,"line":1424},[2499,2504],{"type":21,"tag":455,"props":2500,"children":2501},{"style":492},[2502],{"type":26,"value":2503},"    \"🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":2505,"children":2506},{"style":486},[2507],{"type":26,"value":2483},{"type":21,"tag":455,"props":2509,"children":2510},{"class":457,"line":1443},[2511,2516],{"type":21,"tag":455,"props":2512,"children":2513},{"style":492},[2514],{"type":26,"value":2515},"    \"🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":2517,"children":2518},{"style":486},[2519],{"type":26,"value":2483},{"type":21,"tag":455,"props":2521,"children":2522},{"class":457,"line":1489},[2523,2528],{"type":21,"tag":455,"props":2524,"children":2525},{"style":492},[2526],{"type":26,"value":2527},"    \"🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":2529,"children":2530},{"style":486},[2531],{"type":26,"value":2483},{"type":21,"tag":455,"props":2533,"children":2534},{"class":457,"line":1506},[2535,2540],{"type":21,"tag":455,"props":2536,"children":2537},{"style":492},[2538],{"type":26,"value":2539},"    \"🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":2541,"children":2542},{"style":486},[2543],{"type":26,"value":2483},{"type":21,"tag":455,"props":2545,"children":2546},{"class":457,"line":1547},[2547,2552],{"type":21,"tag":455,"props":2548,"children":2549},{"style":492},[2550],{"type":26,"value":2551},"    \"🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪\"",{"type":21,"tag":455,"props":2553,"children":2554},{"style":486},[2555],{"type":26,"value":2483},{"type":21,"tag":455,"props":2557,"children":2558},{"class":457,"line":1564},[2559,2564],{"type":21,"tag":455,"props":2560,"children":2561},{"style":492},[2562],{"type":26,"value":2563},"    \"🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪\"",{"type":21,"tag":455,"props":2565,"children":2566},{"style":486},[2567],{"type":26,"value":2483},{"type":21,"tag":455,"props":2569,"children":2570},{"class":457,"line":1605},[2571,2576],{"type":21,"tag":455,"props":2572,"children":2573},{"style":492},[2574],{"type":26,"value":2575},"    \"🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪\"",{"type":21,"tag":455,"props":2577,"children":2578},{"style":486},[2579],{"type":26,"value":2483},{"type":21,"tag":455,"props":2581,"children":2582},{"class":457,"line":1622},[2583,2588],{"type":21,"tag":455,"props":2584,"children":2585},{"style":492},[2586],{"type":26,"value":2587},"    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪\"",{"type":21,"tag":455,"props":2589,"children":2590},{"style":486},[2591],{"type":26,"value":2483},{"type":21,"tag":455,"props":2593,"children":2594},{"class":457,"line":1663},[2595,2600],{"type":21,"tag":455,"props":2596,"children":2597},{"style":492},[2598],{"type":26,"value":2599},"    \"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\"",{"type":21,"tag":455,"props":2601,"children":2602},{"style":486},[2603],{"type":26,"value":2483},{"type":21,"tag":455,"props":2605,"children":2606},{"class":457,"line":1680},[2607],{"type":21,"tag":455,"props":2608,"children":2609},{"style":486},[2610],{"type":26,"value":2611},"};\n",{"type":21,"tag":455,"props":2613,"children":2614},{"class":457,"line":1721},[2615],{"type":21,"tag":455,"props":2616,"children":2617},{"emptyLinePlaceholder":471},[2618],{"type":26,"value":474},{"type":21,"tag":455,"props":2620,"children":2621},{"class":457,"line":1738},[2622,2626,2630,2634,2638,2642,2646,2650],{"type":21,"tag":455,"props":2623,"children":2624},{"style":1349},[2625],{"type":26,"value":1352},{"type":21,"tag":455,"props":2627,"children":2628},{"style":1349},[2629],{"type":26,"value":1357},{"type":21,"tag":455,"props":2631,"children":2632},{"style":1349},[2633],{"type":26,"value":1362},{"type":21,"tag":455,"props":2635,"children":2636},{"style":1365},[2637],{"type":26,"value":1368},{"type":21,"tag":455,"props":2639,"children":2640},{"style":486},[2641],{"type":26,"value":489},{"type":21,"tag":455,"props":2643,"children":2644},{"style":1349},[2645],{"type":26,"value":1377},{"type":21,"tag":455,"props":2647,"children":2648},{"style":1365},[2649],{"type":26,"value":1382},{"type":21,"tag":455,"props":2651,"children":2652},{"style":486},[2653],{"type":26,"value":500},{"type":21,"tag":455,"props":2655,"children":2656},{"class":457,"line":1779},[2657],{"type":21,"tag":455,"props":2658,"children":2659},{"style":486},[2660],{"type":26,"value":1394},{"type":21,"tag":455,"props":2662,"children":2663},{"class":457,"line":1796},[2664,2668,2673,2677,2681,2685,2689,2693,2698,2703,2708],{"type":21,"tag":455,"props":2665,"children":2666},{"style":1349},[2667],{"type":26,"value":1984},{"type":21,"tag":455,"props":2669,"children":2670},{"style":486},[2671],{"type":26,"value":2672}," Rounds[(",{"type":21,"tag":455,"props":2674,"children":2675},{"style":1349},[2676],{"type":26,"value":2141},{"type":21,"tag":455,"props":2678,"children":2679},{"style":486},[2680],{"type":26,"value":2146},{"type":21,"tag":455,"props":2682,"children":2683},{"style":1365},[2684],{"type":26,"value":2151},{"type":21,"tag":455,"props":2686,"children":2687},{"style":486},[2688],{"type":26,"value":2156},{"type":21,"tag":455,"props":2690,"children":2691},{"style":1349},[2692],{"type":26,"value":2161},{"type":21,"tag":455,"props":2694,"children":2695},{"style":486},[2696],{"type":26,"value":2697}," (Rounds.Length ",{"type":21,"tag":455,"props":2699,"children":2700},{"style":1349},[2701],{"type":26,"value":2702},"-",{"type":21,"tag":455,"props":2704,"children":2705},{"style":480},[2706],{"type":26,"value":2707}," 1",{"type":21,"tag":455,"props":2709,"children":2710},{"style":486},[2711],{"type":26,"value":2712},"))];\n",{"type":21,"tag":455,"props":2714,"children":2715},{"class":457,"line":1837},[2716],{"type":21,"tag":455,"props":2717,"children":2718},{"style":486},[2719],{"type":26,"value":2002},{"type":21,"tag":22,"props":2721,"children":2722},{},[2723,2725,2730,2732,2738,2740,2747],{"type":26,"value":2724},"This one has the same at-a-glance readability of the original solution, it's even more flexible since it works with an arbitrary number of loading stages, and it makes no dynamic ",{"type":21,"tag":246,"props":2726,"children":2728},{"className":2727},[],[2729],{"type":26,"value":549},{"type":26,"value":2731}," allocations. On the other hand, it requires a separate ",{"type":21,"tag":246,"props":2733,"children":2735},{"className":2734},[],[2736],{"type":26,"value":2737},"static readonly string[]",{"type":26,"value":2739}," that's external to the method (hampering readability), and its main advantage over the simpler original or \"math\" solutions is just performance, something which is likely a ",{"type":21,"tag":322,"props":2741,"children":2744},{"href":2742,"rel":2743},"https://wiki.c2.com/?PrematureOptimization",[326],[2745],{"type":26,"value":2746},"premature optimization",{"type":26,"value":2748}," for a method like this (particularly since we haven't actually measured to confirm that it is faster).",{"type":21,"tag":56,"props":2750,"children":2752},{"id":2751},"so-which-solution-is-best",[2753],{"type":26,"value":2754},"So, Which Solution Is Best?",{"type":21,"tag":22,"props":2756,"children":2757},{},[2758],{"type":26,"value":2759},"None of them and all of them, I would argue. A developer might have their own preference, but none of the solutions are truly so bad that they ought to be condemned in a code review.",{"type":21,"tag":22,"props":2761,"children":2762},{},[2763,2765,2772,2774,2780],{"type":26,"value":2764},"The debate over this snippet of code reminds me how hard software development can be: here's a piece of code which is trivial enough that debating it is almost certainly ",{"type":21,"tag":322,"props":2766,"children":2769},{"href":2767,"rel":2768},"https://en.wikipedia.org/wiki/Law_of_triviality",[326],[2770],{"type":26,"value":2771},"bike-shedding",{"type":26,"value":2773},", but even so, that debate demonstrates just how many decisions a developer needs to make even during the easiest parts of their day. Best to accept that, for trivial code like ",{"type":21,"tag":246,"props":2775,"children":2777},{"className":2776},[],[2778],{"type":26,"value":2779},"GetPercentageRounds()",{"type":26,"value":2781},", maybe an embarrassingly simple and repetitious solution is just fine, and to save our energy for debating the solutions to the complex and mission-critical problems where the subtleties are more impactful.",{"type":21,"tag":1279,"props":2783,"children":2784},{},[2785],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":2787},[2788,2789,2790],{"id":2292,"depth":336,"text":2295},{"id":2392,"depth":336,"text":2395},{"id":2751,"depth":336,"text":2754},"content:phendry:2023-01-19:BadCode.md","phendry/2023-01-19/BadCode.md","phendry/2023-01-19/BadCode",{"user":2795,"name":2796},"phendry","Paul Hendry",{"_path":2798,"_dir":2799,"_draft":7,"_partial":7,"_locale":8,"title":2800,"description":2801,"image":2802,"tags":2803,"publishDate":2805,"excerpt":2801,"body":2806,"_type":343,"_id":10494,"_source":345,"_file":10495,"_stem":10496,"_extension":348,"author":10497},"/phendry/2022-07-28/migratingfromexpresstofastifypart2","2022-07-28","Migrating from Express to Fastify, Part 2","In Part 1, we looked at the features of the Fastify Node.js Web framework compared to Express.js. In Part 2, we'll work through migrating an example Express.js application to Fastify.","/phendry/2022-07-28/img/Migrating from Express to Fastify, Part 2.png",[2804,15],"js","2023-12-31",{"type":18,"children":2807,"toc":10481},[2808,2831,2858,2872,2878,2896,2901,2933,2939,2952,3048,3067,3073,3078,3087,3108,3143,3155,3168,3399,3404,3831,3836,3953,3974,3979,4007,4644,4657,5163,5189,5231,5244,5250,5256,5276,5305,5314,6148,6160,6290,6319,6325,6338,6874,6880,6900,6910,7008,7029,7038,7194,7215,7220,7229,7691,7696,7705,8046,8073,8109,8118,8387,8392,8637,8642,8654,8660,8702,8714,8720,8725,8730,9251,9272,9282,10443,10449,10461,10466,10477],{"type":21,"tag":22,"props":2809,"children":2810},{},[2811,2813,2820,2822,2829],{"type":26,"value":2812},"In Part 1, we looked at the features of the ",{"type":21,"tag":322,"props":2814,"children":2817},{"href":2815,"rel":2816},"https://www.fastify.io/",[326],[2818],{"type":26,"value":2819},"Fastify",{"type":26,"value":2821}," Node.js Web framework compared to ",{"type":21,"tag":322,"props":2823,"children":2826},{"href":2824,"rel":2825},"https://expressjs.com/",[326],[2827],{"type":26,"value":2828},"Express.js",{"type":26,"value":2830},". In Part 2, we'll work through migrating an example Express.js application to Fastify.",{"type":21,"tag":22,"props":2832,"children":2833},{},[2834,2836,2847,2849,2856],{"type":26,"value":2835},"The example application we'll migrate is called ",{"type":21,"tag":322,"props":2837,"children":2840},{"href":2838,"rel":2839},"https://github.com/gothinkster/node-express-realworld-example-app",[326],[2841],{"type":21,"tag":246,"props":2842,"children":2844},{"className":2843},[],[2845],{"type":26,"value":2846},"node-express-realworld-example-app",{"type":26,"value":2848},", which is an Express/MongoDB-based implementation of the ",{"type":21,"tag":322,"props":2850,"children":2853},{"href":2851,"rel":2852},"https://github.com/gothinkster/realworld",[326],[2854],{"type":26,"value":2855},"RealWorld example app",{"type":26,"value":2857}," backend. RealWorld is a specification of a simple blogging website, so the backend component involves managing users, articles, and comments; it's a good demonstration project since it's just large enough to be a decent representation of a production project.",{"type":21,"tag":22,"props":2859,"children":2860},{},[2861,2863,2870],{"type":26,"value":2862},"The code for this example can be found ",{"type":21,"tag":322,"props":2864,"children":2867},{"href":2865,"rel":2866},"https://github.com/artandlogic/node-express-to-fastify-example",[326],[2868],{"type":26,"value":2869},"here",{"type":26,"value":2871},"; at each step in the migration, there'll be a link to follow along with the complete set of changes.",{"type":21,"tag":56,"props":2873,"children":2875},{"id":2874},"incremental-migration-using-fastifyexpress",[2876],{"type":26,"value":2877},"Incremental migration using @fastify/express",{"type":21,"tag":22,"props":2879,"children":2880},{},[2881,2883,2894],{"type":26,"value":2882},"Any code refactor is best done incrementally, so that it can be tested every step along the way. Fastify provides a core plugin called ",{"type":21,"tag":322,"props":2884,"children":2887},{"href":2885,"rel":2886},"https://github.com/fastify/fastify-express",[326],[2888],{"type":21,"tag":246,"props":2889,"children":2891},{"className":2890},[],[2892],{"type":26,"value":2893},"@fastify/express",{"type":26,"value":2895}," which makes this possible during an Express-to-Fastify migration. It can be used in a few different ways, but the feature that's useful in this example is its ability to load an entire Express application inside of a Fastify application.",{"type":21,"tag":22,"props":2897,"children":2898},{},[2899],{"type":26,"value":2900},"With this capability, an Express application can be migrated to Fastify in a three-step process:",{"type":21,"tag":183,"props":2902,"children":2903},{},[2904,2916,2921],{"type":21,"tag":187,"props":2905,"children":2906},{},[2907,2909,2914],{"type":26,"value":2908},"Replace the Express server component with Fastify, using ",{"type":21,"tag":246,"props":2910,"children":2912},{"className":2911},[],[2913],{"type":26,"value":2893},{"type":26,"value":2915}," to load the existing Express router",{"type":21,"tag":187,"props":2917,"children":2918},{},[2919],{"type":26,"value":2920},"Migrate routes from Express to Fastify, one by one",{"type":21,"tag":187,"props":2922,"children":2923},{},[2924,2926,2931],{"type":26,"value":2925},"Remove Express, associated plugins, and ",{"type":21,"tag":246,"props":2927,"children":2929},{"className":2928},[],[2930],{"type":26,"value":2893},{"type":26,"value":2932}," from the project",{"type":21,"tag":56,"props":2934,"children":2936},{"id":2935},"an-overview-of-the-example-project",[2937],{"type":26,"value":2938},"An overview of the example project",{"type":21,"tag":22,"props":2940,"children":2941},{},[2942,2944,2950],{"type":26,"value":2943},"The initial state of the project can be browsed ",{"type":21,"tag":322,"props":2945,"children":2948},{"href":2946,"rel":2947},"https://github.com/artandlogic/node-express-to-fastify-example/tree/3614fd4aeeca2c863c79b9127ecf467ce8c80fe2",[326],[2949],{"type":26,"value":2869},{"type":26,"value":2951},". It's structured in a straightforward way:",{"type":21,"tag":2953,"props":2954,"children":2955},"ul",{},[2956,2975,2995,3006,3017,3037],{"type":21,"tag":187,"props":2957,"children":2958},{},[2959,2965,2967,2973],{"type":21,"tag":246,"props":2960,"children":2962},{"className":2961},[],[2963],{"type":26,"value":2964},"config/",{"type":26,"value":2966},": A few modules for configuring environment variables and the ",{"type":21,"tag":246,"props":2968,"children":2970},{"className":2969},[],[2971],{"type":26,"value":2972},"passport",{"type":26,"value":2974}," library used for authentication",{"type":21,"tag":187,"props":2976,"children":2977},{},[2978,2984,2986,2993],{"type":21,"tag":246,"props":2979,"children":2981},{"className":2980},[],[2982],{"type":26,"value":2983},"models/",{"type":26,"value":2985},": Contains the ",{"type":21,"tag":322,"props":2987,"children":2990},{"href":2988,"rel":2989},"https://mongoosejs.com/",[326],[2991],{"type":26,"value":2992},"Mongoose",{"type":26,"value":2994}," models used for interfacing with MongoDB",{"type":21,"tag":187,"props":2996,"children":2997},{},[2998,3004],{"type":21,"tag":246,"props":2999,"children":3001},{"className":3000},[],[3002],{"type":26,"value":3003},"public/",{"type":26,"value":3005},": A directory for static assets",{"type":21,"tag":187,"props":3007,"children":3008},{},[3009,3015],{"type":21,"tag":246,"props":3010,"children":3012},{"className":3011},[],[3013],{"type":26,"value":3014},"routes/",{"type":26,"value":3016},": Modules defining the API endpoints of the application",{"type":21,"tag":187,"props":3018,"children":3019},{},[3020,3026,3028,3035],{"type":21,"tag":246,"props":3021,"children":3023},{"className":3022},[],[3024],{"type":26,"value":3025},"tests/",{"type":26,"value":3027},": Contains ",{"type":21,"tag":322,"props":3029,"children":3032},{"href":3030,"rel":3031},"https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/",[326],[3033],{"type":26,"value":3034},"Newman",{"type":26,"value":3036},"-based end-to-end automated testing",{"type":21,"tag":187,"props":3038,"children":3039},{},[3040,3046],{"type":21,"tag":246,"props":3041,"children":3043},{"className":3042},[],[3044],{"type":26,"value":3045},"app.js",{"type":26,"value":3047},": The main entrypoint where the Express server is started",{"type":21,"tag":22,"props":3049,"children":3050},{},[3051,3053,3058,3060,3065],{"type":26,"value":3052},"The first step in the migration will be to change ",{"type":21,"tag":246,"props":3054,"children":3056},{"className":3055},[],[3057],{"type":26,"value":3045},{"type":26,"value":3059}," such that it starts a Fastify server, and using the ",{"type":21,"tag":246,"props":3061,"children":3063},{"className":3062},[],[3064],{"type":26,"value":2893},{"type":26,"value":3066}," plugin in order to load the existing Express application. The end result is an application that works no differently than before, but it'll be ready for Fastify routes to be added which co-exist with the Express ones.",{"type":21,"tag":56,"props":3068,"children":3070},{"id":3069},"step-1-replacing-the-server-component",[3071],{"type":26,"value":3072},"Step 1: Replacing the server component",{"type":21,"tag":22,"props":3074,"children":3075},{},[3076],{"type":26,"value":3077},"First, a few new packages need to be installed:",{"type":21,"tag":239,"props":3079,"children":3082},{"className":3080,"code":3081,"language":26},[242],"npm install --save fastify @fastify/express fastify-plugin @fastify/formbody\n",[3083],{"type":21,"tag":246,"props":3084,"children":3085},{"__ignoreMap":8},[3086],{"type":26,"value":3081},{"type":21,"tag":22,"props":3088,"children":3089},{},[3090,3092,3098,3100,3106],{"type":26,"value":3091},"Note that ",{"type":21,"tag":246,"props":3093,"children":3095},{"className":3094},[],[3096],{"type":26,"value":3097},"fastify-plugin",{"type":26,"value":3099}," is a utility we'll use for defining Fastify plugins, and ",{"type":21,"tag":246,"props":3101,"children":3103},{"className":3102},[],[3104],{"type":26,"value":3105},"@fastify/formbody",{"type":26,"value":3107}," is for an Express-related workaround we'll discuss later.",{"type":21,"tag":22,"props":3109,"children":3110},{},[3111,3113,3118,3120,3126,3128,3134,3136,3141],{"type":26,"value":3112},"Next, we'll want to move the existing Express routes from ",{"type":21,"tag":246,"props":3114,"children":3116},{"className":3115},[],[3117],{"type":26,"value":3014},{"type":26,"value":3119}," into a new ",{"type":21,"tag":246,"props":3121,"children":3123},{"className":3122},[],[3124],{"type":26,"value":3125},"routes/legacy/",{"type":26,"value":3127}," directory, updating ",{"type":21,"tag":246,"props":3129,"children":3131},{"className":3130},[],[3132],{"type":26,"value":3133},"require()",{"type":26,"value":3135}," statements accordingly. This leaves the ",{"type":21,"tag":246,"props":3137,"children":3139},{"className":3138},[],[3140],{"type":26,"value":3014},{"type":26,"value":3142}," directory ready to accept new Fastify routes in step 2.",{"type":21,"tag":22,"props":3144,"children":3145},{},[3146,3148,3153],{"type":26,"value":3147},"With those preparations complete, it's time to replace the Express server in ",{"type":21,"tag":246,"props":3149,"children":3151},{"className":3150},[],[3152],{"type":26,"value":3045},{"type":26,"value":3154}," with a Fastify server. Currently, what that module is doing is:",{"type":21,"tag":22,"props":3156,"children":3157},{},[3158,3160,3166],{"type":26,"value":3159},"Instantiating an ",{"type":21,"tag":246,"props":3161,"children":3163},{"className":3162},[],[3164],{"type":26,"value":3165},"Express",{"type":26,"value":3167}," object...",{"type":21,"tag":239,"props":3169,"children":3172},{"className":3170,"code":3171,"language":2804,"meta":8,"style":8},"language-js shiki shiki-themes github-light github-dark","var express = require('express');\nvar bodyParser = require('body-parser');\nvar cors = require('cors');\nvar mongoose = require('mongoose');\n/* ... */\n\nvar isProduction = process.env.NODE_ENV === 'production';\n\nvar app = express();\n",[3173],{"type":21,"tag":246,"props":3174,"children":3175},{"__ignoreMap":8},[3176,3212,3245,3278,3311,3319,3326,3366,3373],{"type":21,"tag":455,"props":3177,"children":3178},{"class":457,"line":458},[3179,3184,3189,3194,3199,3203,3208],{"type":21,"tag":455,"props":3180,"children":3181},{"style":1349},[3182],{"type":26,"value":3183},"var",{"type":21,"tag":455,"props":3185,"children":3186},{"style":486},[3187],{"type":26,"value":3188}," express ",{"type":21,"tag":455,"props":3190,"children":3191},{"style":1349},[3192],{"type":26,"value":3193},"=",{"type":21,"tag":455,"props":3195,"children":3196},{"style":1365},[3197],{"type":26,"value":3198}," require",{"type":21,"tag":455,"props":3200,"children":3201},{"style":486},[3202],{"type":26,"value":489},{"type":21,"tag":455,"props":3204,"children":3205},{"style":492},[3206],{"type":26,"value":3207},"'express'",{"type":21,"tag":455,"props":3209,"children":3210},{"style":486},[3211],{"type":26,"value":2171},{"type":21,"tag":455,"props":3213,"children":3214},{"class":457,"line":336},[3215,3219,3224,3228,3232,3236,3241],{"type":21,"tag":455,"props":3216,"children":3217},{"style":1349},[3218],{"type":26,"value":3183},{"type":21,"tag":455,"props":3220,"children":3221},{"style":486},[3222],{"type":26,"value":3223}," bodyParser ",{"type":21,"tag":455,"props":3225,"children":3226},{"style":1349},[3227],{"type":26,"value":3193},{"type":21,"tag":455,"props":3229,"children":3230},{"style":1365},[3231],{"type":26,"value":3198},{"type":21,"tag":455,"props":3233,"children":3234},{"style":486},[3235],{"type":26,"value":489},{"type":21,"tag":455,"props":3237,"children":3238},{"style":492},[3239],{"type":26,"value":3240},"'body-parser'",{"type":21,"tag":455,"props":3242,"children":3243},{"style":486},[3244],{"type":26,"value":2171},{"type":21,"tag":455,"props":3246,"children":3247},{"class":457,"line":333},[3248,3252,3257,3261,3265,3269,3274],{"type":21,"tag":455,"props":3249,"children":3250},{"style":1349},[3251],{"type":26,"value":3183},{"type":21,"tag":455,"props":3253,"children":3254},{"style":486},[3255],{"type":26,"value":3256}," cors ",{"type":21,"tag":455,"props":3258,"children":3259},{"style":1349},[3260],{"type":26,"value":3193},{"type":21,"tag":455,"props":3262,"children":3263},{"style":1365},[3264],{"type":26,"value":3198},{"type":21,"tag":455,"props":3266,"children":3267},{"style":486},[3268],{"type":26,"value":489},{"type":21,"tag":455,"props":3270,"children":3271},{"style":492},[3272],{"type":26,"value":3273},"'cors'",{"type":21,"tag":455,"props":3275,"children":3276},{"style":486},[3277],{"type":26,"value":2171},{"type":21,"tag":455,"props":3279,"children":3280},{"class":457,"line":1424},[3281,3285,3290,3294,3298,3302,3307],{"type":21,"tag":455,"props":3282,"children":3283},{"style":1349},[3284],{"type":26,"value":3183},{"type":21,"tag":455,"props":3286,"children":3287},{"style":486},[3288],{"type":26,"value":3289}," mongoose ",{"type":21,"tag":455,"props":3291,"children":3292},{"style":1349},[3293],{"type":26,"value":3193},{"type":21,"tag":455,"props":3295,"children":3296},{"style":1365},[3297],{"type":26,"value":3198},{"type":21,"tag":455,"props":3299,"children":3300},{"style":486},[3301],{"type":26,"value":489},{"type":21,"tag":455,"props":3303,"children":3304},{"style":492},[3305],{"type":26,"value":3306},"'mongoose'",{"type":21,"tag":455,"props":3308,"children":3309},{"style":486},[3310],{"type":26,"value":2171},{"type":21,"tag":455,"props":3312,"children":3313},{"class":457,"line":1443},[3314],{"type":21,"tag":455,"props":3315,"children":3316},{"style":462},[3317],{"type":26,"value":3318},"/* ... */\n",{"type":21,"tag":455,"props":3320,"children":3321},{"class":457,"line":1489},[3322],{"type":21,"tag":455,"props":3323,"children":3324},{"emptyLinePlaceholder":471},[3325],{"type":26,"value":474},{"type":21,"tag":455,"props":3327,"children":3328},{"class":457,"line":1506},[3329,3333,3338,3342,3347,3352,3357,3362],{"type":21,"tag":455,"props":3330,"children":3331},{"style":1349},[3332],{"type":26,"value":3183},{"type":21,"tag":455,"props":3334,"children":3335},{"style":486},[3336],{"type":26,"value":3337}," isProduction ",{"type":21,"tag":455,"props":3339,"children":3340},{"style":1349},[3341],{"type":26,"value":3193},{"type":21,"tag":455,"props":3343,"children":3344},{"style":486},[3345],{"type":26,"value":3346}," process.env.",{"type":21,"tag":455,"props":3348,"children":3349},{"style":480},[3350],{"type":26,"value":3351},"NODE_ENV",{"type":21,"tag":455,"props":3353,"children":3354},{"style":1349},[3355],{"type":26,"value":3356}," ===",{"type":21,"tag":455,"props":3358,"children":3359},{"style":492},[3360],{"type":26,"value":3361}," 'production'",{"type":21,"tag":455,"props":3363,"children":3364},{"style":486},[3365],{"type":26,"value":1440},{"type":21,"tag":455,"props":3367,"children":3368},{"class":457,"line":1547},[3369],{"type":21,"tag":455,"props":3370,"children":3371},{"emptyLinePlaceholder":471},[3372],{"type":26,"value":474},{"type":21,"tag":455,"props":3374,"children":3375},{"class":457,"line":1564},[3376,3380,3385,3389,3394],{"type":21,"tag":455,"props":3377,"children":3378},{"style":1349},[3379],{"type":26,"value":3183},{"type":21,"tag":455,"props":3381,"children":3382},{"style":486},[3383],{"type":26,"value":3384}," app ",{"type":21,"tag":455,"props":3386,"children":3387},{"style":1349},[3388],{"type":26,"value":3193},{"type":21,"tag":455,"props":3390,"children":3391},{"style":1365},[3392],{"type":26,"value":3393}," express",{"type":21,"tag":455,"props":3395,"children":3396},{"style":486},[3397],{"type":26,"value":3398},"();\n",{"type":21,"tag":22,"props":3400,"children":3401},{},[3402],{"type":26,"value":3403},"...configuring it, applying middleware, and setting up a database connection...",{"type":21,"tag":239,"props":3405,"children":3407},{"className":3170,"code":3406,"language":2804,"meta":8,"style":8},"app.use(cors());\n\napp.use(require('morgan')('dev'));\napp.use(bodyParser.urlencoded({ extended: false }));\napp.use(bodyParser.json());\n\n/* ... */\n\nif(isProduction){\n  mongoose.connect(process.env.MONGODB_URI);\n} else {\n  mongoose.connect('mongodb://localhost/conduit');\n  mongoose.set('debug', true);\n}\n\nrequire('./models/User');\nrequire('./models/Article');\nrequire('./models/Comment');\nrequire('./config/passport');\n\napp.use(require('./routes'));\n\n/* ... */\n",[3408],{"type":21,"tag":246,"props":3409,"children":3410},{"__ignoreMap":8},[3411,3438,3445,3489,3525,3549,3556,3563,3570,3582,3609,3626,3650,3684,3691,3698,3718,3738,3758,3778,3785,3817,3824],{"type":21,"tag":455,"props":3412,"children":3413},{"class":457,"line":458},[3414,3419,3424,3428,3433],{"type":21,"tag":455,"props":3415,"children":3416},{"style":486},[3417],{"type":26,"value":3418},"app.",{"type":21,"tag":455,"props":3420,"children":3421},{"style":1365},[3422],{"type":26,"value":3423},"use",{"type":21,"tag":455,"props":3425,"children":3426},{"style":486},[3427],{"type":26,"value":489},{"type":21,"tag":455,"props":3429,"children":3430},{"style":1365},[3431],{"type":26,"value":3432},"cors",{"type":21,"tag":455,"props":3434,"children":3435},{"style":486},[3436],{"type":26,"value":3437},"());\n",{"type":21,"tag":455,"props":3439,"children":3440},{"class":457,"line":336},[3441],{"type":21,"tag":455,"props":3442,"children":3443},{"emptyLinePlaceholder":471},[3444],{"type":26,"value":474},{"type":21,"tag":455,"props":3446,"children":3447},{"class":457,"line":333},[3448,3452,3456,3460,3465,3469,3474,3479,3484],{"type":21,"tag":455,"props":3449,"children":3450},{"style":486},[3451],{"type":26,"value":3418},{"type":21,"tag":455,"props":3453,"children":3454},{"style":1365},[3455],{"type":26,"value":3423},{"type":21,"tag":455,"props":3457,"children":3458},{"style":486},[3459],{"type":26,"value":489},{"type":21,"tag":455,"props":3461,"children":3462},{"style":1365},[3463],{"type":26,"value":3464},"require",{"type":21,"tag":455,"props":3466,"children":3467},{"style":486},[3468],{"type":26,"value":489},{"type":21,"tag":455,"props":3470,"children":3471},{"style":492},[3472],{"type":26,"value":3473},"'morgan'",{"type":21,"tag":455,"props":3475,"children":3476},{"style":486},[3477],{"type":26,"value":3478},")(",{"type":21,"tag":455,"props":3480,"children":3481},{"style":492},[3482],{"type":26,"value":3483},"'dev'",{"type":21,"tag":455,"props":3485,"children":3486},{"style":486},[3487],{"type":26,"value":3488},"));\n",{"type":21,"tag":455,"props":3490,"children":3491},{"class":457,"line":1424},[3492,3496,3500,3505,3510,3515,3520],{"type":21,"tag":455,"props":3493,"children":3494},{"style":486},[3495],{"type":26,"value":3418},{"type":21,"tag":455,"props":3497,"children":3498},{"style":1365},[3499],{"type":26,"value":3423},{"type":21,"tag":455,"props":3501,"children":3502},{"style":486},[3503],{"type":26,"value":3504},"(bodyParser.",{"type":21,"tag":455,"props":3506,"children":3507},{"style":1365},[3508],{"type":26,"value":3509},"urlencoded",{"type":21,"tag":455,"props":3511,"children":3512},{"style":486},[3513],{"type":26,"value":3514},"({ extended: ",{"type":21,"tag":455,"props":3516,"children":3517},{"style":480},[3518],{"type":26,"value":3519},"false",{"type":21,"tag":455,"props":3521,"children":3522},{"style":486},[3523],{"type":26,"value":3524}," }));\n",{"type":21,"tag":455,"props":3526,"children":3527},{"class":457,"line":1443},[3528,3532,3536,3540,3545],{"type":21,"tag":455,"props":3529,"children":3530},{"style":486},[3531],{"type":26,"value":3418},{"type":21,"tag":455,"props":3533,"children":3534},{"style":1365},[3535],{"type":26,"value":3423},{"type":21,"tag":455,"props":3537,"children":3538},{"style":486},[3539],{"type":26,"value":3504},{"type":21,"tag":455,"props":3541,"children":3542},{"style":1365},[3543],{"type":26,"value":3544},"json",{"type":21,"tag":455,"props":3546,"children":3547},{"style":486},[3548],{"type":26,"value":3437},{"type":21,"tag":455,"props":3550,"children":3551},{"class":457,"line":1489},[3552],{"type":21,"tag":455,"props":3553,"children":3554},{"emptyLinePlaceholder":471},[3555],{"type":26,"value":474},{"type":21,"tag":455,"props":3557,"children":3558},{"class":457,"line":1506},[3559],{"type":21,"tag":455,"props":3560,"children":3561},{"style":462},[3562],{"type":26,"value":3318},{"type":21,"tag":455,"props":3564,"children":3565},{"class":457,"line":1547},[3566],{"type":21,"tag":455,"props":3567,"children":3568},{"emptyLinePlaceholder":471},[3569],{"type":26,"value":474},{"type":21,"tag":455,"props":3571,"children":3572},{"class":457,"line":1564},[3573,3577],{"type":21,"tag":455,"props":3574,"children":3575},{"style":1349},[3576],{"type":26,"value":2041},{"type":21,"tag":455,"props":3578,"children":3579},{"style":486},[3580],{"type":26,"value":3581},"(isProduction){\n",{"type":21,"tag":455,"props":3583,"children":3584},{"class":457,"line":1605},[3585,3590,3595,3600,3605],{"type":21,"tag":455,"props":3586,"children":3587},{"style":486},[3588],{"type":26,"value":3589},"  mongoose.",{"type":21,"tag":455,"props":3591,"children":3592},{"style":1365},[3593],{"type":26,"value":3594},"connect",{"type":21,"tag":455,"props":3596,"children":3597},{"style":486},[3598],{"type":26,"value":3599},"(process.env.",{"type":21,"tag":455,"props":3601,"children":3602},{"style":480},[3603],{"type":26,"value":3604},"MONGODB_URI",{"type":21,"tag":455,"props":3606,"children":3607},{"style":486},[3608],{"type":26,"value":2171},{"type":21,"tag":455,"props":3610,"children":3611},{"class":457,"line":1622},[3612,3617,3622],{"type":21,"tag":455,"props":3613,"children":3614},{"style":486},[3615],{"type":26,"value":3616},"} ",{"type":21,"tag":455,"props":3618,"children":3619},{"style":1349},[3620],{"type":26,"value":3621},"else",{"type":21,"tag":455,"props":3623,"children":3624},{"style":486},[3625],{"type":26,"value":2470},{"type":21,"tag":455,"props":3627,"children":3628},{"class":457,"line":1663},[3629,3633,3637,3641,3646],{"type":21,"tag":455,"props":3630,"children":3631},{"style":486},[3632],{"type":26,"value":3589},{"type":21,"tag":455,"props":3634,"children":3635},{"style":1365},[3636],{"type":26,"value":3594},{"type":21,"tag":455,"props":3638,"children":3639},{"style":486},[3640],{"type":26,"value":489},{"type":21,"tag":455,"props":3642,"children":3643},{"style":492},[3644],{"type":26,"value":3645},"'mongodb://localhost/conduit'",{"type":21,"tag":455,"props":3647,"children":3648},{"style":486},[3649],{"type":26,"value":2171},{"type":21,"tag":455,"props":3651,"children":3652},{"class":457,"line":1680},[3653,3657,3662,3666,3671,3675,3680],{"type":21,"tag":455,"props":3654,"children":3655},{"style":486},[3656],{"type":26,"value":3589},{"type":21,"tag":455,"props":3658,"children":3659},{"style":1365},[3660],{"type":26,"value":3661},"set",{"type":21,"tag":455,"props":3663,"children":3664},{"style":486},[3665],{"type":26,"value":489},{"type":21,"tag":455,"props":3667,"children":3668},{"style":492},[3669],{"type":26,"value":3670},"'debug'",{"type":21,"tag":455,"props":3672,"children":3673},{"style":486},[3674],{"type":26,"value":2228},{"type":21,"tag":455,"props":3676,"children":3677},{"style":480},[3678],{"type":26,"value":3679},"true",{"type":21,"tag":455,"props":3681,"children":3682},{"style":486},[3683],{"type":26,"value":2171},{"type":21,"tag":455,"props":3685,"children":3686},{"class":457,"line":1721},[3687],{"type":21,"tag":455,"props":3688,"children":3689},{"style":486},[3690],{"type":26,"value":2002},{"type":21,"tag":455,"props":3692,"children":3693},{"class":457,"line":1738},[3694],{"type":21,"tag":455,"props":3695,"children":3696},{"emptyLinePlaceholder":471},[3697],{"type":26,"value":474},{"type":21,"tag":455,"props":3699,"children":3700},{"class":457,"line":1779},[3701,3705,3709,3714],{"type":21,"tag":455,"props":3702,"children":3703},{"style":1365},[3704],{"type":26,"value":3464},{"type":21,"tag":455,"props":3706,"children":3707},{"style":486},[3708],{"type":26,"value":489},{"type":21,"tag":455,"props":3710,"children":3711},{"style":492},[3712],{"type":26,"value":3713},"'./models/User'",{"type":21,"tag":455,"props":3715,"children":3716},{"style":486},[3717],{"type":26,"value":2171},{"type":21,"tag":455,"props":3719,"children":3720},{"class":457,"line":1796},[3721,3725,3729,3734],{"type":21,"tag":455,"props":3722,"children":3723},{"style":1365},[3724],{"type":26,"value":3464},{"type":21,"tag":455,"props":3726,"children":3727},{"style":486},[3728],{"type":26,"value":489},{"type":21,"tag":455,"props":3730,"children":3731},{"style":492},[3732],{"type":26,"value":3733},"'./models/Article'",{"type":21,"tag":455,"props":3735,"children":3736},{"style":486},[3737],{"type":26,"value":2171},{"type":21,"tag":455,"props":3739,"children":3740},{"class":457,"line":1837},[3741,3745,3749,3754],{"type":21,"tag":455,"props":3742,"children":3743},{"style":1365},[3744],{"type":26,"value":3464},{"type":21,"tag":455,"props":3746,"children":3747},{"style":486},[3748],{"type":26,"value":489},{"type":21,"tag":455,"props":3750,"children":3751},{"style":492},[3752],{"type":26,"value":3753},"'./models/Comment'",{"type":21,"tag":455,"props":3755,"children":3756},{"style":486},[3757],{"type":26,"value":2171},{"type":21,"tag":455,"props":3759,"children":3760},{"class":457,"line":1854},[3761,3765,3769,3774],{"type":21,"tag":455,"props":3762,"children":3763},{"style":1365},[3764],{"type":26,"value":3464},{"type":21,"tag":455,"props":3766,"children":3767},{"style":486},[3768],{"type":26,"value":489},{"type":21,"tag":455,"props":3770,"children":3771},{"style":492},[3772],{"type":26,"value":3773},"'./config/passport'",{"type":21,"tag":455,"props":3775,"children":3776},{"style":486},[3777],{"type":26,"value":2171},{"type":21,"tag":455,"props":3779,"children":3780},{"class":457,"line":1895},[3781],{"type":21,"tag":455,"props":3782,"children":3783},{"emptyLinePlaceholder":471},[3784],{"type":26,"value":474},{"type":21,"tag":455,"props":3786,"children":3787},{"class":457,"line":1912},[3788,3792,3796,3800,3804,3808,3813],{"type":21,"tag":455,"props":3789,"children":3790},{"style":486},[3791],{"type":26,"value":3418},{"type":21,"tag":455,"props":3793,"children":3794},{"style":1365},[3795],{"type":26,"value":3423},{"type":21,"tag":455,"props":3797,"children":3798},{"style":486},[3799],{"type":26,"value":489},{"type":21,"tag":455,"props":3801,"children":3802},{"style":1365},[3803],{"type":26,"value":3464},{"type":21,"tag":455,"props":3805,"children":3806},{"style":486},[3807],{"type":26,"value":489},{"type":21,"tag":455,"props":3809,"children":3810},{"style":492},[3811],{"type":26,"value":3812},"'./routes'",{"type":21,"tag":455,"props":3814,"children":3815},{"style":486},[3816],{"type":26,"value":3488},{"type":21,"tag":455,"props":3818,"children":3819},{"class":457,"line":1953},[3820],{"type":21,"tag":455,"props":3821,"children":3822},{"emptyLinePlaceholder":471},[3823],{"type":26,"value":474},{"type":21,"tag":455,"props":3825,"children":3826},{"class":457,"line":1970},[3827],{"type":21,"tag":455,"props":3828,"children":3829},{"style":462},[3830],{"type":26,"value":3318},{"type":21,"tag":22,"props":3832,"children":3833},{},[3834],{"type":26,"value":3835},"...and finally, starting the server:",{"type":21,"tag":239,"props":3837,"children":3839},{"className":3170,"code":3838,"language":2804,"meta":8,"style":8},"var server = app.listen( process.env.PORT || 3000, function(){\n  console.log('Listening on port ' + server.address().port);\n});\n",[3840],{"type":21,"tag":246,"props":3841,"children":3842},{"__ignoreMap":8},[3843,3903,3945],{"type":21,"tag":455,"props":3844,"children":3845},{"class":457,"line":458},[3846,3850,3855,3859,3864,3869,3874,3879,3884,3889,3893,3898],{"type":21,"tag":455,"props":3847,"children":3848},{"style":1349},[3849],{"type":26,"value":3183},{"type":21,"tag":455,"props":3851,"children":3852},{"style":486},[3853],{"type":26,"value":3854}," server ",{"type":21,"tag":455,"props":3856,"children":3857},{"style":1349},[3858],{"type":26,"value":3193},{"type":21,"tag":455,"props":3860,"children":3861},{"style":486},[3862],{"type":26,"value":3863}," app.",{"type":21,"tag":455,"props":3865,"children":3866},{"style":1365},[3867],{"type":26,"value":3868},"listen",{"type":21,"tag":455,"props":3870,"children":3871},{"style":486},[3872],{"type":26,"value":3873},"( process.env.",{"type":21,"tag":455,"props":3875,"children":3876},{"style":480},[3877],{"type":26,"value":3878},"PORT",{"type":21,"tag":455,"props":3880,"children":3881},{"style":1349},[3882],{"type":26,"value":3883}," ||",{"type":21,"tag":455,"props":3885,"children":3886},{"style":480},[3887],{"type":26,"value":3888}," 3000",{"type":21,"tag":455,"props":3890,"children":3891},{"style":486},[3892],{"type":26,"value":2228},{"type":21,"tag":455,"props":3894,"children":3895},{"style":1349},[3896],{"type":26,"value":3897},"function",{"type":21,"tag":455,"props":3899,"children":3900},{"style":486},[3901],{"type":26,"value":3902},"(){\n",{"type":21,"tag":455,"props":3904,"children":3905},{"class":457,"line":336},[3906,3911,3916,3920,3925,3930,3935,3940],{"type":21,"tag":455,"props":3907,"children":3908},{"style":486},[3909],{"type":26,"value":3910},"  console.",{"type":21,"tag":455,"props":3912,"children":3913},{"style":1365},[3914],{"type":26,"value":3915},"log",{"type":21,"tag":455,"props":3917,"children":3918},{"style":486},[3919],{"type":26,"value":489},{"type":21,"tag":455,"props":3921,"children":3922},{"style":492},[3923],{"type":26,"value":3924},"'Listening on port '",{"type":21,"tag":455,"props":3926,"children":3927},{"style":1349},[3928],{"type":26,"value":3929}," +",{"type":21,"tag":455,"props":3931,"children":3932},{"style":486},[3933],{"type":26,"value":3934}," server.",{"type":21,"tag":455,"props":3936,"children":3937},{"style":1365},[3938],{"type":26,"value":3939},"address",{"type":21,"tag":455,"props":3941,"children":3942},{"style":486},[3943],{"type":26,"value":3944},"().port);\n",{"type":21,"tag":455,"props":3946,"children":3947},{"class":457,"line":333},[3948],{"type":21,"tag":455,"props":3949,"children":3950},{"style":486},[3951],{"type":26,"value":3952},"});\n",{"type":21,"tag":22,"props":3954,"children":3955},{},[3956,3958,3965,3967,3972],{"type":26,"value":3957},"Fastify works similarly, but it has a ",{"type":21,"tag":322,"props":3959,"children":3962},{"href":3960,"rel":3961},"https://www.fastify.io/docs/latest/Reference/Plugins/",[326],[3963],{"type":26,"value":3964},"plugin system",{"type":26,"value":3966}," which provides a flexible way to extend a Fastify server's capabilities. One property that plugins can enforce is ",{"type":21,"tag":33,"props":3968,"children":3969},{},[3970],{"type":26,"value":3971},"encapsulation",{"type":26,"value":3973},": ensuring that the capabilities added by a plugin can only be used within that plugin.",{"type":21,"tag":22,"props":3975,"children":3976},{},[3977],{"type":26,"value":3978},"In this first step where we intend to temporarily load an Express application within a Fastify application, encapsulation is a great way to ensure that none of the Fastify-based code can inadvertently use functionality added by the Express application, so it makes sense to create a Fastify plugin for it. For now the entirety of the application will exist in there, but we'll change that incrementally later.",{"type":21,"tag":22,"props":3980,"children":3981},{},[3982,3984,3989,3991,3997,3999,4005],{"type":26,"value":3983},"The Fastify version of ",{"type":21,"tag":246,"props":3985,"children":3987},{"className":3986},[],[3988],{"type":26,"value":3045},{"type":26,"value":3990}," is similar in structure, defining a ",{"type":21,"tag":246,"props":3992,"children":3994},{"className":3993},[],[3995],{"type":26,"value":3996},"build()",{"type":26,"value":3998}," function which sets up the database and loads plugins, and then running it and calling a ",{"type":21,"tag":246,"props":4000,"children":4002},{"className":4001},[],[4003],{"type":26,"value":4004},".listen()",{"type":26,"value":4006}," method to start the server:",{"type":21,"tag":239,"props":4008,"children":4010},{"className":3170,"code":4009,"language":2804,"meta":8,"style":8},"var isProduction = process.env.NODE_ENV === 'production';\n\nasync function build() {\n  const mongoose = require('mongoose');\n\n  if(isProduction){\n    mongoose.connect(process.env.MONGODB_URI);\n  } else {\n    mongoose.connect('mongodb://localhost/conduit');\n    mongoose.set('debug', true);\n  }\n\n  require('./models/User');\n  require('./models/Article');\n  require('./models/Comment');\n  require('./config/passport');\n\n  // Create global app object\n  const app = require('fastify')({\n    logger: true,\n  });\n  await app.register(require('@fastify/formbody'));\n  await app.register(require('./routes/legacy'), {isProduction});\n  return app;\n}\n\nbuild()\n  .then(app => app.listen({port: 3000}))\n  .then((address) => {\n    console.log('Listening on ' + address);\n  }).catch(console.log);\n",[4011],{"type":21,"tag":246,"props":4012,"children":4013},{"__ignoreMap":8},[4014,4049,4056,4079,4112,4119,4131,4155,4171,4194,4225,4233,4240,4260,4279,4298,4317,4324,4332,4366,4382,4390,4428,4465,4478,4485,4493,4507,4559,4594,4625],{"type":21,"tag":455,"props":4015,"children":4016},{"class":457,"line":458},[4017,4021,4025,4029,4033,4037,4041,4045],{"type":21,"tag":455,"props":4018,"children":4019},{"style":1349},[4020],{"type":26,"value":3183},{"type":21,"tag":455,"props":4022,"children":4023},{"style":486},[4024],{"type":26,"value":3337},{"type":21,"tag":455,"props":4026,"children":4027},{"style":1349},[4028],{"type":26,"value":3193},{"type":21,"tag":455,"props":4030,"children":4031},{"style":486},[4032],{"type":26,"value":3346},{"type":21,"tag":455,"props":4034,"children":4035},{"style":480},[4036],{"type":26,"value":3351},{"type":21,"tag":455,"props":4038,"children":4039},{"style":1349},[4040],{"type":26,"value":3356},{"type":21,"tag":455,"props":4042,"children":4043},{"style":492},[4044],{"type":26,"value":3361},{"type":21,"tag":455,"props":4046,"children":4047},{"style":486},[4048],{"type":26,"value":1440},{"type":21,"tag":455,"props":4050,"children":4051},{"class":457,"line":336},[4052],{"type":21,"tag":455,"props":4053,"children":4054},{"emptyLinePlaceholder":471},[4055],{"type":26,"value":474},{"type":21,"tag":455,"props":4057,"children":4058},{"class":457,"line":333},[4059,4064,4069,4074],{"type":21,"tag":455,"props":4060,"children":4061},{"style":1349},[4062],{"type":26,"value":4063},"async",{"type":21,"tag":455,"props":4065,"children":4066},{"style":1349},[4067],{"type":26,"value":4068}," function",{"type":21,"tag":455,"props":4070,"children":4071},{"style":1365},[4072],{"type":26,"value":4073}," build",{"type":21,"tag":455,"props":4075,"children":4076},{"style":486},[4077],{"type":26,"value":4078},"() {\n",{"type":21,"tag":455,"props":4080,"children":4081},{"class":457,"line":1424},[4082,4087,4092,4096,4100,4104,4108],{"type":21,"tag":455,"props":4083,"children":4084},{"style":1349},[4085],{"type":26,"value":4086},"  const",{"type":21,"tag":455,"props":4088,"children":4089},{"style":480},[4090],{"type":26,"value":4091}," mongoose",{"type":21,"tag":455,"props":4093,"children":4094},{"style":1349},[4095],{"type":26,"value":2131},{"type":21,"tag":455,"props":4097,"children":4098},{"style":1365},[4099],{"type":26,"value":3198},{"type":21,"tag":455,"props":4101,"children":4102},{"style":486},[4103],{"type":26,"value":489},{"type":21,"tag":455,"props":4105,"children":4106},{"style":492},[4107],{"type":26,"value":3306},{"type":21,"tag":455,"props":4109,"children":4110},{"style":486},[4111],{"type":26,"value":2171},{"type":21,"tag":455,"props":4113,"children":4114},{"class":457,"line":1443},[4115],{"type":21,"tag":455,"props":4116,"children":4117},{"emptyLinePlaceholder":471},[4118],{"type":26,"value":474},{"type":21,"tag":455,"props":4120,"children":4121},{"class":457,"line":1489},[4122,4127],{"type":21,"tag":455,"props":4123,"children":4124},{"style":1349},[4125],{"type":26,"value":4126},"  if",{"type":21,"tag":455,"props":4128,"children":4129},{"style":486},[4130],{"type":26,"value":3581},{"type":21,"tag":455,"props":4132,"children":4133},{"class":457,"line":1506},[4134,4139,4143,4147,4151],{"type":21,"tag":455,"props":4135,"children":4136},{"style":486},[4137],{"type":26,"value":4138},"    mongoose.",{"type":21,"tag":455,"props":4140,"children":4141},{"style":1365},[4142],{"type":26,"value":3594},{"type":21,"tag":455,"props":4144,"children":4145},{"style":486},[4146],{"type":26,"value":3599},{"type":21,"tag":455,"props":4148,"children":4149},{"style":480},[4150],{"type":26,"value":3604},{"type":21,"tag":455,"props":4152,"children":4153},{"style":486},[4154],{"type":26,"value":2171},{"type":21,"tag":455,"props":4156,"children":4157},{"class":457,"line":1547},[4158,4163,4167],{"type":21,"tag":455,"props":4159,"children":4160},{"style":486},[4161],{"type":26,"value":4162},"  } ",{"type":21,"tag":455,"props":4164,"children":4165},{"style":1349},[4166],{"type":26,"value":3621},{"type":21,"tag":455,"props":4168,"children":4169},{"style":486},[4170],{"type":26,"value":2470},{"type":21,"tag":455,"props":4172,"children":4173},{"class":457,"line":1564},[4174,4178,4182,4186,4190],{"type":21,"tag":455,"props":4175,"children":4176},{"style":486},[4177],{"type":26,"value":4138},{"type":21,"tag":455,"props":4179,"children":4180},{"style":1365},[4181],{"type":26,"value":3594},{"type":21,"tag":455,"props":4183,"children":4184},{"style":486},[4185],{"type":26,"value":489},{"type":21,"tag":455,"props":4187,"children":4188},{"style":492},[4189],{"type":26,"value":3645},{"type":21,"tag":455,"props":4191,"children":4192},{"style":486},[4193],{"type":26,"value":2171},{"type":21,"tag":455,"props":4195,"children":4196},{"class":457,"line":1605},[4197,4201,4205,4209,4213,4217,4221],{"type":21,"tag":455,"props":4198,"children":4199},{"style":486},[4200],{"type":26,"value":4138},{"type":21,"tag":455,"props":4202,"children":4203},{"style":1365},[4204],{"type":26,"value":3661},{"type":21,"tag":455,"props":4206,"children":4207},{"style":486},[4208],{"type":26,"value":489},{"type":21,"tag":455,"props":4210,"children":4211},{"style":492},[4212],{"type":26,"value":3670},{"type":21,"tag":455,"props":4214,"children":4215},{"style":486},[4216],{"type":26,"value":2228},{"type":21,"tag":455,"props":4218,"children":4219},{"style":480},[4220],{"type":26,"value":3679},{"type":21,"tag":455,"props":4222,"children":4223},{"style":486},[4224],{"type":26,"value":2171},{"type":21,"tag":455,"props":4226,"children":4227},{"class":457,"line":1622},[4228],{"type":21,"tag":455,"props":4229,"children":4230},{"style":486},[4231],{"type":26,"value":4232},"  }\n",{"type":21,"tag":455,"props":4234,"children":4235},{"class":457,"line":1663},[4236],{"type":21,"tag":455,"props":4237,"children":4238},{"emptyLinePlaceholder":471},[4239],{"type":26,"value":474},{"type":21,"tag":455,"props":4241,"children":4242},{"class":457,"line":1680},[4243,4248,4252,4256],{"type":21,"tag":455,"props":4244,"children":4245},{"style":1365},[4246],{"type":26,"value":4247},"  require",{"type":21,"tag":455,"props":4249,"children":4250},{"style":486},[4251],{"type":26,"value":489},{"type":21,"tag":455,"props":4253,"children":4254},{"style":492},[4255],{"type":26,"value":3713},{"type":21,"tag":455,"props":4257,"children":4258},{"style":486},[4259],{"type":26,"value":2171},{"type":21,"tag":455,"props":4261,"children":4262},{"class":457,"line":1721},[4263,4267,4271,4275],{"type":21,"tag":455,"props":4264,"children":4265},{"style":1365},[4266],{"type":26,"value":4247},{"type":21,"tag":455,"props":4268,"children":4269},{"style":486},[4270],{"type":26,"value":489},{"type":21,"tag":455,"props":4272,"children":4273},{"style":492},[4274],{"type":26,"value":3733},{"type":21,"tag":455,"props":4276,"children":4277},{"style":486},[4278],{"type":26,"value":2171},{"type":21,"tag":455,"props":4280,"children":4281},{"class":457,"line":1738},[4282,4286,4290,4294],{"type":21,"tag":455,"props":4283,"children":4284},{"style":1365},[4285],{"type":26,"value":4247},{"type":21,"tag":455,"props":4287,"children":4288},{"style":486},[4289],{"type":26,"value":489},{"type":21,"tag":455,"props":4291,"children":4292},{"style":492},[4293],{"type":26,"value":3753},{"type":21,"tag":455,"props":4295,"children":4296},{"style":486},[4297],{"type":26,"value":2171},{"type":21,"tag":455,"props":4299,"children":4300},{"class":457,"line":1779},[4301,4305,4309,4313],{"type":21,"tag":455,"props":4302,"children":4303},{"style":1365},[4304],{"type":26,"value":4247},{"type":21,"tag":455,"props":4306,"children":4307},{"style":486},[4308],{"type":26,"value":489},{"type":21,"tag":455,"props":4310,"children":4311},{"style":492},[4312],{"type":26,"value":3773},{"type":21,"tag":455,"props":4314,"children":4315},{"style":486},[4316],{"type":26,"value":2171},{"type":21,"tag":455,"props":4318,"children":4319},{"class":457,"line":1796},[4320],{"type":21,"tag":455,"props":4321,"children":4322},{"emptyLinePlaceholder":471},[4323],{"type":26,"value":474},{"type":21,"tag":455,"props":4325,"children":4326},{"class":457,"line":1837},[4327],{"type":21,"tag":455,"props":4328,"children":4329},{"style":462},[4330],{"type":26,"value":4331},"  // Create global app object\n",{"type":21,"tag":455,"props":4333,"children":4334},{"class":457,"line":1854},[4335,4339,4344,4348,4352,4356,4361],{"type":21,"tag":455,"props":4336,"children":4337},{"style":1349},[4338],{"type":26,"value":4086},{"type":21,"tag":455,"props":4340,"children":4341},{"style":480},[4342],{"type":26,"value":4343}," app",{"type":21,"tag":455,"props":4345,"children":4346},{"style":1349},[4347],{"type":26,"value":2131},{"type":21,"tag":455,"props":4349,"children":4350},{"style":1365},[4351],{"type":26,"value":3198},{"type":21,"tag":455,"props":4353,"children":4354},{"style":486},[4355],{"type":26,"value":489},{"type":21,"tag":455,"props":4357,"children":4358},{"style":492},[4359],{"type":26,"value":4360},"'fastify'",{"type":21,"tag":455,"props":4362,"children":4363},{"style":486},[4364],{"type":26,"value":4365},")({\n",{"type":21,"tag":455,"props":4367,"children":4368},{"class":457,"line":1895},[4369,4374,4378],{"type":21,"tag":455,"props":4370,"children":4371},{"style":486},[4372],{"type":26,"value":4373},"    logger: ",{"type":21,"tag":455,"props":4375,"children":4376},{"style":480},[4377],{"type":26,"value":3679},{"type":21,"tag":455,"props":4379,"children":4380},{"style":486},[4381],{"type":26,"value":2483},{"type":21,"tag":455,"props":4383,"children":4384},{"class":457,"line":1912},[4385],{"type":21,"tag":455,"props":4386,"children":4387},{"style":486},[4388],{"type":26,"value":4389},"  });\n",{"type":21,"tag":455,"props":4391,"children":4392},{"class":457,"line":1953},[4393,4398,4402,4407,4411,4415,4419,4424],{"type":21,"tag":455,"props":4394,"children":4395},{"style":1349},[4396],{"type":26,"value":4397},"  await",{"type":21,"tag":455,"props":4399,"children":4400},{"style":486},[4401],{"type":26,"value":3863},{"type":21,"tag":455,"props":4403,"children":4404},{"style":1365},[4405],{"type":26,"value":4406},"register",{"type":21,"tag":455,"props":4408,"children":4409},{"style":486},[4410],{"type":26,"value":489},{"type":21,"tag":455,"props":4412,"children":4413},{"style":1365},[4414],{"type":26,"value":3464},{"type":21,"tag":455,"props":4416,"children":4417},{"style":486},[4418],{"type":26,"value":489},{"type":21,"tag":455,"props":4420,"children":4421},{"style":492},[4422],{"type":26,"value":4423},"'@fastify/formbody'",{"type":21,"tag":455,"props":4425,"children":4426},{"style":486},[4427],{"type":26,"value":3488},{"type":21,"tag":455,"props":4429,"children":4430},{"class":457,"line":1970},[4431,4435,4439,4443,4447,4451,4455,4460],{"type":21,"tag":455,"props":4432,"children":4433},{"style":1349},[4434],{"type":26,"value":4397},{"type":21,"tag":455,"props":4436,"children":4437},{"style":486},[4438],{"type":26,"value":3863},{"type":21,"tag":455,"props":4440,"children":4441},{"style":1365},[4442],{"type":26,"value":4406},{"type":21,"tag":455,"props":4444,"children":4445},{"style":486},[4446],{"type":26,"value":489},{"type":21,"tag":455,"props":4448,"children":4449},{"style":1365},[4450],{"type":26,"value":3464},{"type":21,"tag":455,"props":4452,"children":4453},{"style":486},[4454],{"type":26,"value":489},{"type":21,"tag":455,"props":4456,"children":4457},{"style":492},[4458],{"type":26,"value":4459},"'./routes/legacy'",{"type":21,"tag":455,"props":4461,"children":4462},{"style":486},[4463],{"type":26,"value":4464},"), {isProduction});\n",{"type":21,"tag":455,"props":4466,"children":4467},{"class":457,"line":1978},[4468,4473],{"type":21,"tag":455,"props":4469,"children":4470},{"style":1349},[4471],{"type":26,"value":4472},"  return",{"type":21,"tag":455,"props":4474,"children":4475},{"style":486},[4476],{"type":26,"value":4477}," app;\n",{"type":21,"tag":455,"props":4479,"children":4480},{"class":457,"line":1996},[4481],{"type":21,"tag":455,"props":4482,"children":4483},{"style":486},[4484],{"type":26,"value":2002},{"type":21,"tag":455,"props":4486,"children":4488},{"class":457,"line":4487},26,[4489],{"type":21,"tag":455,"props":4490,"children":4491},{"emptyLinePlaceholder":471},[4492],{"type":26,"value":474},{"type":21,"tag":455,"props":4494,"children":4496},{"class":457,"line":4495},27,[4497,4502],{"type":21,"tag":455,"props":4498,"children":4499},{"style":1365},[4500],{"type":26,"value":4501},"build",{"type":21,"tag":455,"props":4503,"children":4504},{"style":486},[4505],{"type":26,"value":4506},"()\n",{"type":21,"tag":455,"props":4508,"children":4510},{"class":457,"line":4509},28,[4511,4516,4521,4525,4531,4536,4540,4544,4549,4554],{"type":21,"tag":455,"props":4512,"children":4513},{"style":486},[4514],{"type":26,"value":4515},"  .",{"type":21,"tag":455,"props":4517,"children":4518},{"style":1365},[4519],{"type":26,"value":4520},"then",{"type":21,"tag":455,"props":4522,"children":4523},{"style":486},[4524],{"type":26,"value":489},{"type":21,"tag":455,"props":4526,"children":4528},{"style":4527},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[4529],{"type":26,"value":4530},"app",{"type":21,"tag":455,"props":4532,"children":4533},{"style":1349},[4534],{"type":26,"value":4535}," =>",{"type":21,"tag":455,"props":4537,"children":4538},{"style":486},[4539],{"type":26,"value":3863},{"type":21,"tag":455,"props":4541,"children":4542},{"style":1365},[4543],{"type":26,"value":3868},{"type":21,"tag":455,"props":4545,"children":4546},{"style":486},[4547],{"type":26,"value":4548},"({port: ",{"type":21,"tag":455,"props":4550,"children":4551},{"style":480},[4552],{"type":26,"value":4553},"3000",{"type":21,"tag":455,"props":4555,"children":4556},{"style":486},[4557],{"type":26,"value":4558},"}))\n",{"type":21,"tag":455,"props":4560,"children":4562},{"class":457,"line":4561},29,[4563,4567,4571,4576,4580,4585,4590],{"type":21,"tag":455,"props":4564,"children":4565},{"style":486},[4566],{"type":26,"value":4515},{"type":21,"tag":455,"props":4568,"children":4569},{"style":1365},[4570],{"type":26,"value":4520},{"type":21,"tag":455,"props":4572,"children":4573},{"style":486},[4574],{"type":26,"value":4575},"((",{"type":21,"tag":455,"props":4577,"children":4578},{"style":4527},[4579],{"type":26,"value":3939},{"type":21,"tag":455,"props":4581,"children":4582},{"style":486},[4583],{"type":26,"value":4584},") ",{"type":21,"tag":455,"props":4586,"children":4587},{"style":1349},[4588],{"type":26,"value":4589},"=>",{"type":21,"tag":455,"props":4591,"children":4592},{"style":486},[4593],{"type":26,"value":2470},{"type":21,"tag":455,"props":4595,"children":4597},{"class":457,"line":4596},30,[4598,4603,4607,4611,4616,4620],{"type":21,"tag":455,"props":4599,"children":4600},{"style":486},[4601],{"type":26,"value":4602},"    console.",{"type":21,"tag":455,"props":4604,"children":4605},{"style":1365},[4606],{"type":26,"value":3915},{"type":21,"tag":455,"props":4608,"children":4609},{"style":486},[4610],{"type":26,"value":489},{"type":21,"tag":455,"props":4612,"children":4613},{"style":492},[4614],{"type":26,"value":4615},"'Listening on '",{"type":21,"tag":455,"props":4617,"children":4618},{"style":1349},[4619],{"type":26,"value":3929},{"type":21,"tag":455,"props":4621,"children":4622},{"style":486},[4623],{"type":26,"value":4624}," address);\n",{"type":21,"tag":455,"props":4626,"children":4628},{"class":457,"line":4627},31,[4629,4634,4639],{"type":21,"tag":455,"props":4630,"children":4631},{"style":486},[4632],{"type":26,"value":4633},"  }).",{"type":21,"tag":455,"props":4635,"children":4636},{"style":1365},[4637],{"type":26,"value":4638},"catch",{"type":21,"tag":455,"props":4640,"children":4641},{"style":486},[4642],{"type":26,"value":4643},"(console.log);\n",{"type":21,"tag":22,"props":4645,"children":4646},{},[4647,4649,4655],{"type":26,"value":4648},"The Express application configuration has been moved to a plugin defined in ",{"type":21,"tag":246,"props":4650,"children":4652},{"className":4651},[],[4653],{"type":26,"value":4654},"routes/legacy/index.js",{"type":26,"value":4656},", which is registered in the Fastify app above. It looks like this:",{"type":21,"tag":239,"props":4658,"children":4660},{"className":3170,"code":4659,"language":2804,"meta":8,"style":8},"const express = require('express'),\n      session = require('express-session'),\n      fp = require('fastify-plugin'),\n      cors = require('cors'),\n      errorhandler = require('errorhandler');\n\nmodule.exports = fp(async function (fastify, opts) {\n  await fastify.register(require('@fastify/express'), {\n    // run express after `fastify-formbody` logic\n    expressHook: 'preHandler',\n  });\n\n  fastify.use(cors());\n\n  fastify.use(require('morgan')('dev'));\n\n  /* ... (the same .use() calls that used to be in app.js) */\n\n  const router = require('express').Router();\n  router.use('/api', require('./api'));\n\n  fastify.use(router);\n});\n",[4661],{"type":21,"tag":246,"props":4662,"children":4663},{"__ignoreMap":8},[4664,4697,4726,4755,4783,4812,4819,4880,4918,4926,4943,4950,4957,4981,4988,5027,5034,5042,5049,5091,5133,5140,5156],{"type":21,"tag":455,"props":4665,"children":4666},{"class":457,"line":458},[4667,4672,4676,4680,4684,4688,4692],{"type":21,"tag":455,"props":4668,"children":4669},{"style":1349},[4670],{"type":26,"value":4671},"const",{"type":21,"tag":455,"props":4673,"children":4674},{"style":480},[4675],{"type":26,"value":3393},{"type":21,"tag":455,"props":4677,"children":4678},{"style":1349},[4679],{"type":26,"value":2131},{"type":21,"tag":455,"props":4681,"children":4682},{"style":1365},[4683],{"type":26,"value":3198},{"type":21,"tag":455,"props":4685,"children":4686},{"style":486},[4687],{"type":26,"value":489},{"type":21,"tag":455,"props":4689,"children":4690},{"style":492},[4691],{"type":26,"value":3207},{"type":21,"tag":455,"props":4693,"children":4694},{"style":486},[4695],{"type":26,"value":4696},"),\n",{"type":21,"tag":455,"props":4698,"children":4699},{"class":457,"line":336},[4700,4705,4709,4713,4717,4722],{"type":21,"tag":455,"props":4701,"children":4702},{"style":480},[4703],{"type":26,"value":4704},"      session",{"type":21,"tag":455,"props":4706,"children":4707},{"style":1349},[4708],{"type":26,"value":2131},{"type":21,"tag":455,"props":4710,"children":4711},{"style":1365},[4712],{"type":26,"value":3198},{"type":21,"tag":455,"props":4714,"children":4715},{"style":486},[4716],{"type":26,"value":489},{"type":21,"tag":455,"props":4718,"children":4719},{"style":492},[4720],{"type":26,"value":4721},"'express-session'",{"type":21,"tag":455,"props":4723,"children":4724},{"style":486},[4725],{"type":26,"value":4696},{"type":21,"tag":455,"props":4727,"children":4728},{"class":457,"line":333},[4729,4734,4738,4742,4746,4751],{"type":21,"tag":455,"props":4730,"children":4731},{"style":480},[4732],{"type":26,"value":4733},"      fp",{"type":21,"tag":455,"props":4735,"children":4736},{"style":1349},[4737],{"type":26,"value":2131},{"type":21,"tag":455,"props":4739,"children":4740},{"style":1365},[4741],{"type":26,"value":3198},{"type":21,"tag":455,"props":4743,"children":4744},{"style":486},[4745],{"type":26,"value":489},{"type":21,"tag":455,"props":4747,"children":4748},{"style":492},[4749],{"type":26,"value":4750},"'fastify-plugin'",{"type":21,"tag":455,"props":4752,"children":4753},{"style":486},[4754],{"type":26,"value":4696},{"type":21,"tag":455,"props":4756,"children":4757},{"class":457,"line":1424},[4758,4763,4767,4771,4775,4779],{"type":21,"tag":455,"props":4759,"children":4760},{"style":480},[4761],{"type":26,"value":4762},"      cors",{"type":21,"tag":455,"props":4764,"children":4765},{"style":1349},[4766],{"type":26,"value":2131},{"type":21,"tag":455,"props":4768,"children":4769},{"style":1365},[4770],{"type":26,"value":3198},{"type":21,"tag":455,"props":4772,"children":4773},{"style":486},[4774],{"type":26,"value":489},{"type":21,"tag":455,"props":4776,"children":4777},{"style":492},[4778],{"type":26,"value":3273},{"type":21,"tag":455,"props":4780,"children":4781},{"style":486},[4782],{"type":26,"value":4696},{"type":21,"tag":455,"props":4784,"children":4785},{"class":457,"line":1443},[4786,4791,4795,4799,4803,4808],{"type":21,"tag":455,"props":4787,"children":4788},{"style":480},[4789],{"type":26,"value":4790},"      errorhandler",{"type":21,"tag":455,"props":4792,"children":4793},{"style":1349},[4794],{"type":26,"value":2131},{"type":21,"tag":455,"props":4796,"children":4797},{"style":1365},[4798],{"type":26,"value":3198},{"type":21,"tag":455,"props":4800,"children":4801},{"style":486},[4802],{"type":26,"value":489},{"type":21,"tag":455,"props":4804,"children":4805},{"style":492},[4806],{"type":26,"value":4807},"'errorhandler'",{"type":21,"tag":455,"props":4809,"children":4810},{"style":486},[4811],{"type":26,"value":2171},{"type":21,"tag":455,"props":4813,"children":4814},{"class":457,"line":1489},[4815],{"type":21,"tag":455,"props":4816,"children":4817},{"emptyLinePlaceholder":471},[4818],{"type":26,"value":474},{"type":21,"tag":455,"props":4820,"children":4821},{"class":457,"line":1506},[4822,4827,4831,4836,4840,4845,4849,4853,4857,4861,4866,4870,4875],{"type":21,"tag":455,"props":4823,"children":4824},{"style":480},[4825],{"type":26,"value":4826},"module",{"type":21,"tag":455,"props":4828,"children":4829},{"style":486},[4830],{"type":26,"value":551},{"type":21,"tag":455,"props":4832,"children":4833},{"style":480},[4834],{"type":26,"value":4835},"exports",{"type":21,"tag":455,"props":4837,"children":4838},{"style":1349},[4839],{"type":26,"value":2131},{"type":21,"tag":455,"props":4841,"children":4842},{"style":1365},[4843],{"type":26,"value":4844}," fp",{"type":21,"tag":455,"props":4846,"children":4847},{"style":486},[4848],{"type":26,"value":489},{"type":21,"tag":455,"props":4850,"children":4851},{"style":1349},[4852],{"type":26,"value":4063},{"type":21,"tag":455,"props":4854,"children":4855},{"style":1349},[4856],{"type":26,"value":4068},{"type":21,"tag":455,"props":4858,"children":4859},{"style":486},[4860],{"type":26,"value":2136},{"type":21,"tag":455,"props":4862,"children":4863},{"style":4527},[4864],{"type":26,"value":4865},"fastify",{"type":21,"tag":455,"props":4867,"children":4868},{"style":486},[4869],{"type":26,"value":2228},{"type":21,"tag":455,"props":4871,"children":4872},{"style":4527},[4873],{"type":26,"value":4874},"opts",{"type":21,"tag":455,"props":4876,"children":4877},{"style":486},[4878],{"type":26,"value":4879},") {\n",{"type":21,"tag":455,"props":4881,"children":4882},{"class":457,"line":1547},[4883,4887,4892,4896,4900,4904,4908,4913],{"type":21,"tag":455,"props":4884,"children":4885},{"style":1349},[4886],{"type":26,"value":4397},{"type":21,"tag":455,"props":4888,"children":4889},{"style":486},[4890],{"type":26,"value":4891}," fastify.",{"type":21,"tag":455,"props":4893,"children":4894},{"style":1365},[4895],{"type":26,"value":4406},{"type":21,"tag":455,"props":4897,"children":4898},{"style":486},[4899],{"type":26,"value":489},{"type":21,"tag":455,"props":4901,"children":4902},{"style":1365},[4903],{"type":26,"value":3464},{"type":21,"tag":455,"props":4905,"children":4906},{"style":486},[4907],{"type":26,"value":489},{"type":21,"tag":455,"props":4909,"children":4910},{"style":492},[4911],{"type":26,"value":4912},"'@fastify/express'",{"type":21,"tag":455,"props":4914,"children":4915},{"style":486},[4916],{"type":26,"value":4917},"), {\n",{"type":21,"tag":455,"props":4919,"children":4920},{"class":457,"line":1564},[4921],{"type":21,"tag":455,"props":4922,"children":4923},{"style":462},[4924],{"type":26,"value":4925},"    // run express after `fastify-formbody` logic\n",{"type":21,"tag":455,"props":4927,"children":4928},{"class":457,"line":1605},[4929,4934,4939],{"type":21,"tag":455,"props":4930,"children":4931},{"style":486},[4932],{"type":26,"value":4933},"    expressHook: ",{"type":21,"tag":455,"props":4935,"children":4936},{"style":492},[4937],{"type":26,"value":4938},"'preHandler'",{"type":21,"tag":455,"props":4940,"children":4941},{"style":486},[4942],{"type":26,"value":2483},{"type":21,"tag":455,"props":4944,"children":4945},{"class":457,"line":1622},[4946],{"type":21,"tag":455,"props":4947,"children":4948},{"style":486},[4949],{"type":26,"value":4389},{"type":21,"tag":455,"props":4951,"children":4952},{"class":457,"line":1663},[4953],{"type":21,"tag":455,"props":4954,"children":4955},{"emptyLinePlaceholder":471},[4956],{"type":26,"value":474},{"type":21,"tag":455,"props":4958,"children":4959},{"class":457,"line":1680},[4960,4965,4969,4973,4977],{"type":21,"tag":455,"props":4961,"children":4962},{"style":486},[4963],{"type":26,"value":4964},"  fastify.",{"type":21,"tag":455,"props":4966,"children":4967},{"style":1365},[4968],{"type":26,"value":3423},{"type":21,"tag":455,"props":4970,"children":4971},{"style":486},[4972],{"type":26,"value":489},{"type":21,"tag":455,"props":4974,"children":4975},{"style":1365},[4976],{"type":26,"value":3432},{"type":21,"tag":455,"props":4978,"children":4979},{"style":486},[4980],{"type":26,"value":3437},{"type":21,"tag":455,"props":4982,"children":4983},{"class":457,"line":1721},[4984],{"type":21,"tag":455,"props":4985,"children":4986},{"emptyLinePlaceholder":471},[4987],{"type":26,"value":474},{"type":21,"tag":455,"props":4989,"children":4990},{"class":457,"line":1738},[4991,4995,4999,5003,5007,5011,5015,5019,5023],{"type":21,"tag":455,"props":4992,"children":4993},{"style":486},[4994],{"type":26,"value":4964},{"type":21,"tag":455,"props":4996,"children":4997},{"style":1365},[4998],{"type":26,"value":3423},{"type":21,"tag":455,"props":5000,"children":5001},{"style":486},[5002],{"type":26,"value":489},{"type":21,"tag":455,"props":5004,"children":5005},{"style":1365},[5006],{"type":26,"value":3464},{"type":21,"tag":455,"props":5008,"children":5009},{"style":486},[5010],{"type":26,"value":489},{"type":21,"tag":455,"props":5012,"children":5013},{"style":492},[5014],{"type":26,"value":3473},{"type":21,"tag":455,"props":5016,"children":5017},{"style":486},[5018],{"type":26,"value":3478},{"type":21,"tag":455,"props":5020,"children":5021},{"style":492},[5022],{"type":26,"value":3483},{"type":21,"tag":455,"props":5024,"children":5025},{"style":486},[5026],{"type":26,"value":3488},{"type":21,"tag":455,"props":5028,"children":5029},{"class":457,"line":1779},[5030],{"type":21,"tag":455,"props":5031,"children":5032},{"emptyLinePlaceholder":471},[5033],{"type":26,"value":474},{"type":21,"tag":455,"props":5035,"children":5036},{"class":457,"line":1796},[5037],{"type":21,"tag":455,"props":5038,"children":5039},{"style":462},[5040],{"type":26,"value":5041},"  /* ... (the same .use() calls that used to be in app.js) */\n",{"type":21,"tag":455,"props":5043,"children":5044},{"class":457,"line":1837},[5045],{"type":21,"tag":455,"props":5046,"children":5047},{"emptyLinePlaceholder":471},[5048],{"type":26,"value":474},{"type":21,"tag":455,"props":5050,"children":5051},{"class":457,"line":1854},[5052,5056,5061,5065,5069,5073,5077,5082,5087],{"type":21,"tag":455,"props":5053,"children":5054},{"style":1349},[5055],{"type":26,"value":4086},{"type":21,"tag":455,"props":5057,"children":5058},{"style":480},[5059],{"type":26,"value":5060}," router",{"type":21,"tag":455,"props":5062,"children":5063},{"style":1349},[5064],{"type":26,"value":2131},{"type":21,"tag":455,"props":5066,"children":5067},{"style":1365},[5068],{"type":26,"value":3198},{"type":21,"tag":455,"props":5070,"children":5071},{"style":486},[5072],{"type":26,"value":489},{"type":21,"tag":455,"props":5074,"children":5075},{"style":492},[5076],{"type":26,"value":3207},{"type":21,"tag":455,"props":5078,"children":5079},{"style":486},[5080],{"type":26,"value":5081},").",{"type":21,"tag":455,"props":5083,"children":5084},{"style":1365},[5085],{"type":26,"value":5086},"Router",{"type":21,"tag":455,"props":5088,"children":5089},{"style":486},[5090],{"type":26,"value":3398},{"type":21,"tag":455,"props":5092,"children":5093},{"class":457,"line":1895},[5094,5099,5103,5107,5112,5116,5120,5124,5129],{"type":21,"tag":455,"props":5095,"children":5096},{"style":486},[5097],{"type":26,"value":5098},"  router.",{"type":21,"tag":455,"props":5100,"children":5101},{"style":1365},[5102],{"type":26,"value":3423},{"type":21,"tag":455,"props":5104,"children":5105},{"style":486},[5106],{"type":26,"value":489},{"type":21,"tag":455,"props":5108,"children":5109},{"style":492},[5110],{"type":26,"value":5111},"'/api'",{"type":21,"tag":455,"props":5113,"children":5114},{"style":486},[5115],{"type":26,"value":2228},{"type":21,"tag":455,"props":5117,"children":5118},{"style":1365},[5119],{"type":26,"value":3464},{"type":21,"tag":455,"props":5121,"children":5122},{"style":486},[5123],{"type":26,"value":489},{"type":21,"tag":455,"props":5125,"children":5126},{"style":492},[5127],{"type":26,"value":5128},"'./api'",{"type":21,"tag":455,"props":5130,"children":5131},{"style":486},[5132],{"type":26,"value":3488},{"type":21,"tag":455,"props":5134,"children":5135},{"class":457,"line":1912},[5136],{"type":21,"tag":455,"props":5137,"children":5138},{"emptyLinePlaceholder":471},[5139],{"type":26,"value":474},{"type":21,"tag":455,"props":5141,"children":5142},{"class":457,"line":1953},[5143,5147,5151],{"type":21,"tag":455,"props":5144,"children":5145},{"style":486},[5146],{"type":26,"value":4964},{"type":21,"tag":455,"props":5148,"children":5149},{"style":1365},[5150],{"type":26,"value":3423},{"type":21,"tag":455,"props":5152,"children":5153},{"style":486},[5154],{"type":26,"value":5155},"(router);\n",{"type":21,"tag":455,"props":5157,"children":5158},{"class":457,"line":1970},[5159],{"type":21,"tag":455,"props":5160,"children":5161},{"style":486},[5162],{"type":26,"value":3952},{"type":21,"tag":22,"props":5164,"children":5165},{},[5166,5168,5173,5175,5180,5182,5187],{"type":26,"value":5167},"For the most part it contains the same Express configuration that previously existed in ",{"type":21,"tag":246,"props":5169,"children":5171},{"className":5170},[],[5172],{"type":26,"value":3045},{"type":26,"value":5174},", except that it's contained in a Fastify plugin, and the configuration is applied to the ",{"type":21,"tag":246,"props":5176,"children":5178},{"className":5177},[],[5179],{"type":26,"value":4865},{"type":26,"value":5181}," instance provided to the plugin. The plugin registers ",{"type":21,"tag":246,"props":5183,"children":5185},{"className":5184},[],[5186],{"type":26,"value":2893},{"type":26,"value":5188}," as a sub-plugin, which is what makes all that Express configuration work seamlessly.",{"type":21,"tag":22,"props":5190,"children":5191},{},[5192,5194,5199,5201,5207,5209,5215,5217,5222,5224,5229],{"type":26,"value":5193},"There's one messy bit, which is the business with ",{"type":21,"tag":246,"props":5195,"children":5197},{"className":5196},[],[5198],{"type":26,"value":3105},{"type":26,"value":5200}," and the ",{"type":21,"tag":246,"props":5202,"children":5204},{"className":5203},[],[5205],{"type":26,"value":5206},"expressHook: 'preHandler'",{"type":26,"value":5208}," configuration line. There's a known incompatibility with the Express ",{"type":21,"tag":246,"props":5210,"children":5212},{"className":5211},[],[5213],{"type":26,"value":5214},"body-parser",{"type":26,"value":5216}," library, and the workaround is to immediately remove ",{"type":21,"tag":246,"props":5218,"children":5220},{"className":5219},[],[5221],{"type":26,"value":5214},{"type":26,"value":5223}," and substitute it with the ",{"type":21,"tag":246,"props":5225,"children":5227},{"className":5226},[],[5228],{"type":26,"value":3105},{"type":26,"value":5230}," plugin which works similarly. This was the only hangup like this that I encountered; otherwise the application runs just like before, using Fastify as the server.",{"type":21,"tag":22,"props":5232,"children":5233},{},[5234,5236,5242],{"type":26,"value":5235},"Click ",{"type":21,"tag":322,"props":5237,"children":5240},{"href":5238,"rel":5239},"https://github.com/artandlogic/node-express-to-fastify-example/commit/38504ce47943246eb71aa25894ecae00cc302955",[326],[5241],{"type":26,"value":2869},{"type":26,"value":5243}," to view the full set of changes up to this point.",{"type":21,"tag":56,"props":5245,"children":5247},{"id":5246},"step-2-migrating-a-route-to-fastify",[5248],{"type":26,"value":5249},"Step 2: Migrating a route to Fastify",{"type":21,"tag":80,"props":5251,"children":5253},{"id":5252},"prerequisite-1-implementing-authentication-decorators-for-fastify-routes",[5254],{"type":26,"value":5255},"Prerequisite #1: Implementing authentication decorators for Fastify routes",{"type":21,"tag":22,"props":5257,"children":5258},{},[5259,5261,5267,5268,5274],{"type":26,"value":5260},"In the Express application, authentication is applied to a route by adding an ",{"type":21,"tag":246,"props":5262,"children":5264},{"className":5263},[],[5265],{"type":26,"value":5266},"auth.optional",{"type":26,"value":411},{"type":21,"tag":246,"props":5269,"children":5271},{"className":5270},[],[5272],{"type":26,"value":5273},"auth.required",{"type":26,"value":5275}," middleware. Before migrating a route to Fastify, we'll need an equivalent way to add authentication requirements to a route.",{"type":21,"tag":22,"props":5277,"children":5278},{},[5279,5281,5288,5289,5296,5298,5304],{"type":26,"value":5280},"In Fastify, this can be accomplished via ",{"type":21,"tag":322,"props":5282,"children":5285},{"href":5283,"rel":5284},"https://www.fastify.io/docs/latest/Reference/Hooks/",[326],[5286],{"type":26,"value":5287},"Hooks",{"type":26,"value":386},{"type":21,"tag":322,"props":5290,"children":5293},{"href":5291,"rel":5292},"https://www.fastify.io/docs/latest/Reference/Decorators/",[326],[5294],{"type":26,"value":5295},"Decorators",{"type":26,"value":5297},". For organizational purposes, this can be set up as a Fastify plugin, ",{"type":21,"tag":246,"props":5299,"children":5301},{"className":5300},[],[5302],{"type":26,"value":5303},"plugins/auth.js",{"type":26,"value":831},{"type":21,"tag":239,"props":5306,"children":5309},{"className":5307,"code":5308,"language":26},[242],"npm install --save @fastify/jwt\n",[5310],{"type":21,"tag":246,"props":5311,"children":5312},{"__ignoreMap":8},[5313],{"type":26,"value":5308},{"type":21,"tag":239,"props":5315,"children":5317},{"className":3170,"code":5316,"language":2804,"meta":8,"style":8},"const fp = require(\"fastify-plugin\");\nconst secret = require('../config').secret;\n\nmodule.exports = fp(async function (fastify, opts) {\n  await fastify.register(require(\"@fastify/jwt\"), {\n    // @fastify/jwt puts the token data on request.user by default; use\n    // request.payload to match the existing application\n    decoratorName: 'payload',\n    secret,\n    verify: {\n      // The application uses \"Token\" in the Authorization header rather than\n      // the more standard \"Bearer\", so add custom code to parse that\n      extractToken: (request) => {\n        const parts = request.headers.authorization.split(' ');\n        if (parts.length !== 2) {\n          throw new BadRequestError();\n        }\n\n        const [scheme, token] = parts;\n\n        if (!/^Token$/i.test(scheme)) {\n          throw new BadRequestError()\n        }\n\n        return token;\n      },\n    },\n  });\n  // Add a decorator so that routes can reference this via `fastify.authenticated`\n  await fastify.decorate(\"authenticated\", async function(request, reply) {\n    try {\n      await request.jwtVerify();\n    } catch (err) {\n      reply.send(err);\n    }\n  });\n  // Add a decorator so that routes can reference this via `fastify.authenticatedOptional`\n  await fastify.decorate(\"authenticatedOptional\", async function(request, reply) {\n    try {\n      await request.jwtVerify();\n    } catch {}\n  });\n});\n",[5318],{"type":21,"tag":246,"props":5319,"children":5320},{"__ignoreMap":8},[5321,5353,5387,5394,5449,5485,5493,5501,5518,5526,5534,5542,5550,5580,5620,5652,5673,5681,5688,5728,5735,5794,5813,5820,5827,5839,5847,5855,5862,5870,5928,5940,5963,5981,6000,6009,6017,6026,6083,6095,6115,6132,6140],{"type":21,"tag":455,"props":5322,"children":5323},{"class":457,"line":458},[5324,5328,5332,5336,5340,5344,5349],{"type":21,"tag":455,"props":5325,"children":5326},{"style":1349},[5327],{"type":26,"value":4671},{"type":21,"tag":455,"props":5329,"children":5330},{"style":480},[5331],{"type":26,"value":4844},{"type":21,"tag":455,"props":5333,"children":5334},{"style":1349},[5335],{"type":26,"value":2131},{"type":21,"tag":455,"props":5337,"children":5338},{"style":1365},[5339],{"type":26,"value":3198},{"type":21,"tag":455,"props":5341,"children":5342},{"style":486},[5343],{"type":26,"value":489},{"type":21,"tag":455,"props":5345,"children":5346},{"style":492},[5347],{"type":26,"value":5348},"\"fastify-plugin\"",{"type":21,"tag":455,"props":5350,"children":5351},{"style":486},[5352],{"type":26,"value":2171},{"type":21,"tag":455,"props":5354,"children":5355},{"class":457,"line":336},[5356,5360,5365,5369,5373,5377,5382],{"type":21,"tag":455,"props":5357,"children":5358},{"style":1349},[5359],{"type":26,"value":4671},{"type":21,"tag":455,"props":5361,"children":5362},{"style":480},[5363],{"type":26,"value":5364}," secret",{"type":21,"tag":455,"props":5366,"children":5367},{"style":1349},[5368],{"type":26,"value":2131},{"type":21,"tag":455,"props":5370,"children":5371},{"style":1365},[5372],{"type":26,"value":3198},{"type":21,"tag":455,"props":5374,"children":5375},{"style":486},[5376],{"type":26,"value":489},{"type":21,"tag":455,"props":5378,"children":5379},{"style":492},[5380],{"type":26,"value":5381},"'../config'",{"type":21,"tag":455,"props":5383,"children":5384},{"style":486},[5385],{"type":26,"value":5386},").secret;\n",{"type":21,"tag":455,"props":5388,"children":5389},{"class":457,"line":333},[5390],{"type":21,"tag":455,"props":5391,"children":5392},{"emptyLinePlaceholder":471},[5393],{"type":26,"value":474},{"type":21,"tag":455,"props":5395,"children":5396},{"class":457,"line":1424},[5397,5401,5405,5409,5413,5417,5421,5425,5429,5433,5437,5441,5445],{"type":21,"tag":455,"props":5398,"children":5399},{"style":480},[5400],{"type":26,"value":4826},{"type":21,"tag":455,"props":5402,"children":5403},{"style":486},[5404],{"type":26,"value":551},{"type":21,"tag":455,"props":5406,"children":5407},{"style":480},[5408],{"type":26,"value":4835},{"type":21,"tag":455,"props":5410,"children":5411},{"style":1349},[5412],{"type":26,"value":2131},{"type":21,"tag":455,"props":5414,"children":5415},{"style":1365},[5416],{"type":26,"value":4844},{"type":21,"tag":455,"props":5418,"children":5419},{"style":486},[5420],{"type":26,"value":489},{"type":21,"tag":455,"props":5422,"children":5423},{"style":1349},[5424],{"type":26,"value":4063},{"type":21,"tag":455,"props":5426,"children":5427},{"style":1349},[5428],{"type":26,"value":4068},{"type":21,"tag":455,"props":5430,"children":5431},{"style":486},[5432],{"type":26,"value":2136},{"type":21,"tag":455,"props":5434,"children":5435},{"style":4527},[5436],{"type":26,"value":4865},{"type":21,"tag":455,"props":5438,"children":5439},{"style":486},[5440],{"type":26,"value":2228},{"type":21,"tag":455,"props":5442,"children":5443},{"style":4527},[5444],{"type":26,"value":4874},{"type":21,"tag":455,"props":5446,"children":5447},{"style":486},[5448],{"type":26,"value":4879},{"type":21,"tag":455,"props":5450,"children":5451},{"class":457,"line":1443},[5452,5456,5460,5464,5468,5472,5476,5481],{"type":21,"tag":455,"props":5453,"children":5454},{"style":1349},[5455],{"type":26,"value":4397},{"type":21,"tag":455,"props":5457,"children":5458},{"style":486},[5459],{"type":26,"value":4891},{"type":21,"tag":455,"props":5461,"children":5462},{"style":1365},[5463],{"type":26,"value":4406},{"type":21,"tag":455,"props":5465,"children":5466},{"style":486},[5467],{"type":26,"value":489},{"type":21,"tag":455,"props":5469,"children":5470},{"style":1365},[5471],{"type":26,"value":3464},{"type":21,"tag":455,"props":5473,"children":5474},{"style":486},[5475],{"type":26,"value":489},{"type":21,"tag":455,"props":5477,"children":5478},{"style":492},[5479],{"type":26,"value":5480},"\"@fastify/jwt\"",{"type":21,"tag":455,"props":5482,"children":5483},{"style":486},[5484],{"type":26,"value":4917},{"type":21,"tag":455,"props":5486,"children":5487},{"class":457,"line":1489},[5488],{"type":21,"tag":455,"props":5489,"children":5490},{"style":462},[5491],{"type":26,"value":5492},"    // @fastify/jwt puts the token data on request.user by default; use\n",{"type":21,"tag":455,"props":5494,"children":5495},{"class":457,"line":1506},[5496],{"type":21,"tag":455,"props":5497,"children":5498},{"style":462},[5499],{"type":26,"value":5500},"    // request.payload to match the existing application\n",{"type":21,"tag":455,"props":5502,"children":5503},{"class":457,"line":1547},[5504,5509,5514],{"type":21,"tag":455,"props":5505,"children":5506},{"style":486},[5507],{"type":26,"value":5508},"    decoratorName: ",{"type":21,"tag":455,"props":5510,"children":5511},{"style":492},[5512],{"type":26,"value":5513},"'payload'",{"type":21,"tag":455,"props":5515,"children":5516},{"style":486},[5517],{"type":26,"value":2483},{"type":21,"tag":455,"props":5519,"children":5520},{"class":457,"line":1564},[5521],{"type":21,"tag":455,"props":5522,"children":5523},{"style":486},[5524],{"type":26,"value":5525},"    secret,\n",{"type":21,"tag":455,"props":5527,"children":5528},{"class":457,"line":1605},[5529],{"type":21,"tag":455,"props":5530,"children":5531},{"style":486},[5532],{"type":26,"value":5533},"    verify: {\n",{"type":21,"tag":455,"props":5535,"children":5536},{"class":457,"line":1622},[5537],{"type":21,"tag":455,"props":5538,"children":5539},{"style":462},[5540],{"type":26,"value":5541},"      // The application uses \"Token\" in the Authorization header rather than\n",{"type":21,"tag":455,"props":5543,"children":5544},{"class":457,"line":1663},[5545],{"type":21,"tag":455,"props":5546,"children":5547},{"style":462},[5548],{"type":26,"value":5549},"      // the more standard \"Bearer\", so add custom code to parse that\n",{"type":21,"tag":455,"props":5551,"children":5552},{"class":457,"line":1680},[5553,5558,5563,5568,5572,5576],{"type":21,"tag":455,"props":5554,"children":5555},{"style":1365},[5556],{"type":26,"value":5557},"      extractToken",{"type":21,"tag":455,"props":5559,"children":5560},{"style":486},[5561],{"type":26,"value":5562},": (",{"type":21,"tag":455,"props":5564,"children":5565},{"style":4527},[5566],{"type":26,"value":5567},"request",{"type":21,"tag":455,"props":5569,"children":5570},{"style":486},[5571],{"type":26,"value":4584},{"type":21,"tag":455,"props":5573,"children":5574},{"style":1349},[5575],{"type":26,"value":4589},{"type":21,"tag":455,"props":5577,"children":5578},{"style":486},[5579],{"type":26,"value":2470},{"type":21,"tag":455,"props":5581,"children":5582},{"class":457,"line":1721},[5583,5588,5593,5597,5602,5607,5611,5616],{"type":21,"tag":455,"props":5584,"children":5585},{"style":1349},[5586],{"type":26,"value":5587},"        const",{"type":21,"tag":455,"props":5589,"children":5590},{"style":480},[5591],{"type":26,"value":5592}," parts",{"type":21,"tag":455,"props":5594,"children":5595},{"style":1349},[5596],{"type":26,"value":2131},{"type":21,"tag":455,"props":5598,"children":5599},{"style":486},[5600],{"type":26,"value":5601}," request.headers.authorization.",{"type":21,"tag":455,"props":5603,"children":5604},{"style":1365},[5605],{"type":26,"value":5606},"split",{"type":21,"tag":455,"props":5608,"children":5609},{"style":486},[5610],{"type":26,"value":489},{"type":21,"tag":455,"props":5612,"children":5613},{"style":492},[5614],{"type":26,"value":5615},"' '",{"type":21,"tag":455,"props":5617,"children":5618},{"style":486},[5619],{"type":26,"value":2171},{"type":21,"tag":455,"props":5621,"children":5622},{"class":457,"line":1738},[5623,5628,5633,5638,5643,5648],{"type":21,"tag":455,"props":5624,"children":5625},{"style":1349},[5626],{"type":26,"value":5627},"        if",{"type":21,"tag":455,"props":5629,"children":5630},{"style":486},[5631],{"type":26,"value":5632}," (parts.",{"type":21,"tag":455,"props":5634,"children":5635},{"style":480},[5636],{"type":26,"value":5637},"length",{"type":21,"tag":455,"props":5639,"children":5640},{"style":1349},[5641],{"type":26,"value":5642}," !==",{"type":21,"tag":455,"props":5644,"children":5645},{"style":480},[5646],{"type":26,"value":5647}," 2",{"type":21,"tag":455,"props":5649,"children":5650},{"style":486},[5651],{"type":26,"value":4879},{"type":21,"tag":455,"props":5653,"children":5654},{"class":457,"line":1779},[5655,5660,5664,5669],{"type":21,"tag":455,"props":5656,"children":5657},{"style":1349},[5658],{"type":26,"value":5659},"          throw",{"type":21,"tag":455,"props":5661,"children":5662},{"style":1349},[5663],{"type":26,"value":2183},{"type":21,"tag":455,"props":5665,"children":5666},{"style":1365},[5667],{"type":26,"value":5668}," BadRequestError",{"type":21,"tag":455,"props":5670,"children":5671},{"style":486},[5672],{"type":26,"value":3398},{"type":21,"tag":455,"props":5674,"children":5675},{"class":457,"line":1796},[5676],{"type":21,"tag":455,"props":5677,"children":5678},{"style":486},[5679],{"type":26,"value":5680},"        }\n",{"type":21,"tag":455,"props":5682,"children":5683},{"class":457,"line":1837},[5684],{"type":21,"tag":455,"props":5685,"children":5686},{"emptyLinePlaceholder":471},[5687],{"type":26,"value":474},{"type":21,"tag":455,"props":5689,"children":5690},{"class":457,"line":1854},[5691,5695,5700,5705,5709,5714,5719,5723],{"type":21,"tag":455,"props":5692,"children":5693},{"style":1349},[5694],{"type":26,"value":5587},{"type":21,"tag":455,"props":5696,"children":5697},{"style":486},[5698],{"type":26,"value":5699}," [",{"type":21,"tag":455,"props":5701,"children":5702},{"style":480},[5703],{"type":26,"value":5704},"scheme",{"type":21,"tag":455,"props":5706,"children":5707},{"style":486},[5708],{"type":26,"value":2228},{"type":21,"tag":455,"props":5710,"children":5711},{"style":480},[5712],{"type":26,"value":5713},"token",{"type":21,"tag":455,"props":5715,"children":5716},{"style":486},[5717],{"type":26,"value":5718},"] ",{"type":21,"tag":455,"props":5720,"children":5721},{"style":1349},[5722],{"type":26,"value":3193},{"type":21,"tag":455,"props":5724,"children":5725},{"style":486},[5726],{"type":26,"value":5727}," parts;\n",{"type":21,"tag":455,"props":5729,"children":5730},{"class":457,"line":1895},[5731],{"type":21,"tag":455,"props":5732,"children":5733},{"emptyLinePlaceholder":471},[5734],{"type":26,"value":474},{"type":21,"tag":455,"props":5736,"children":5737},{"class":457,"line":1912},[5738,5742,5746,5750,5755,5760,5766,5771,5775,5780,5784,5789],{"type":21,"tag":455,"props":5739,"children":5740},{"style":1349},[5741],{"type":26,"value":5627},{"type":21,"tag":455,"props":5743,"children":5744},{"style":486},[5745],{"type":26,"value":2136},{"type":21,"tag":455,"props":5747,"children":5748},{"style":1349},[5749],{"type":26,"value":1272},{"type":21,"tag":455,"props":5751,"children":5752},{"style":492},[5753],{"type":26,"value":5754},"/",{"type":21,"tag":455,"props":5756,"children":5757},{"style":1349},[5758],{"type":26,"value":5759},"^",{"type":21,"tag":455,"props":5761,"children":5763},{"style":5762},"--shiki-default:#032F62;--shiki-dark:#DBEDFF",[5764],{"type":26,"value":5765},"Token",{"type":21,"tag":455,"props":5767,"children":5768},{"style":1349},[5769],{"type":26,"value":5770},"$",{"type":21,"tag":455,"props":5772,"children":5773},{"style":492},[5774],{"type":26,"value":5754},{"type":21,"tag":455,"props":5776,"children":5777},{"style":1349},[5778],{"type":26,"value":5779},"i",{"type":21,"tag":455,"props":5781,"children":5782},{"style":486},[5783],{"type":26,"value":551},{"type":21,"tag":455,"props":5785,"children":5786},{"style":1365},[5787],{"type":26,"value":5788},"test",{"type":21,"tag":455,"props":5790,"children":5791},{"style":486},[5792],{"type":26,"value":5793},"(scheme)) {\n",{"type":21,"tag":455,"props":5795,"children":5796},{"class":457,"line":1953},[5797,5801,5805,5809],{"type":21,"tag":455,"props":5798,"children":5799},{"style":1349},[5800],{"type":26,"value":5659},{"type":21,"tag":455,"props":5802,"children":5803},{"style":1349},[5804],{"type":26,"value":2183},{"type":21,"tag":455,"props":5806,"children":5807},{"style":1365},[5808],{"type":26,"value":5668},{"type":21,"tag":455,"props":5810,"children":5811},{"style":486},[5812],{"type":26,"value":4506},{"type":21,"tag":455,"props":5814,"children":5815},{"class":457,"line":1970},[5816],{"type":21,"tag":455,"props":5817,"children":5818},{"style":486},[5819],{"type":26,"value":5680},{"type":21,"tag":455,"props":5821,"children":5822},{"class":457,"line":1978},[5823],{"type":21,"tag":455,"props":5824,"children":5825},{"emptyLinePlaceholder":471},[5826],{"type":26,"value":474},{"type":21,"tag":455,"props":5828,"children":5829},{"class":457,"line":1996},[5830,5834],{"type":21,"tag":455,"props":5831,"children":5832},{"style":1349},[5833],{"type":26,"value":1430},{"type":21,"tag":455,"props":5835,"children":5836},{"style":486},[5837],{"type":26,"value":5838}," token;\n",{"type":21,"tag":455,"props":5840,"children":5841},{"class":457,"line":4487},[5842],{"type":21,"tag":455,"props":5843,"children":5844},{"style":486},[5845],{"type":26,"value":5846},"      },\n",{"type":21,"tag":455,"props":5848,"children":5849},{"class":457,"line":4495},[5850],{"type":21,"tag":455,"props":5851,"children":5852},{"style":486},[5853],{"type":26,"value":5854},"    },\n",{"type":21,"tag":455,"props":5856,"children":5857},{"class":457,"line":4509},[5858],{"type":21,"tag":455,"props":5859,"children":5860},{"style":486},[5861],{"type":26,"value":4389},{"type":21,"tag":455,"props":5863,"children":5864},{"class":457,"line":4561},[5865],{"type":21,"tag":455,"props":5866,"children":5867},{"style":462},[5868],{"type":26,"value":5869},"  // Add a decorator so that routes can reference this via `fastify.authenticated`\n",{"type":21,"tag":455,"props":5871,"children":5872},{"class":457,"line":4596},[5873,5877,5881,5886,5890,5895,5899,5903,5907,5911,5915,5919,5924],{"type":21,"tag":455,"props":5874,"children":5875},{"style":1349},[5876],{"type":26,"value":4397},{"type":21,"tag":455,"props":5878,"children":5879},{"style":486},[5880],{"type":26,"value":4891},{"type":21,"tag":455,"props":5882,"children":5883},{"style":1365},[5884],{"type":26,"value":5885},"decorate",{"type":21,"tag":455,"props":5887,"children":5888},{"style":486},[5889],{"type":26,"value":489},{"type":21,"tag":455,"props":5891,"children":5892},{"style":492},[5893],{"type":26,"value":5894},"\"authenticated\"",{"type":21,"tag":455,"props":5896,"children":5897},{"style":486},[5898],{"type":26,"value":2228},{"type":21,"tag":455,"props":5900,"children":5901},{"style":1349},[5902],{"type":26,"value":4063},{"type":21,"tag":455,"props":5904,"children":5905},{"style":1349},[5906],{"type":26,"value":4068},{"type":21,"tag":455,"props":5908,"children":5909},{"style":486},[5910],{"type":26,"value":489},{"type":21,"tag":455,"props":5912,"children":5913},{"style":4527},[5914],{"type":26,"value":5567},{"type":21,"tag":455,"props":5916,"children":5917},{"style":486},[5918],{"type":26,"value":2228},{"type":21,"tag":455,"props":5920,"children":5921},{"style":4527},[5922],{"type":26,"value":5923},"reply",{"type":21,"tag":455,"props":5925,"children":5926},{"style":486},[5927],{"type":26,"value":4879},{"type":21,"tag":455,"props":5929,"children":5930},{"class":457,"line":4627},[5931,5936],{"type":21,"tag":455,"props":5932,"children":5933},{"style":1349},[5934],{"type":26,"value":5935},"    try",{"type":21,"tag":455,"props":5937,"children":5938},{"style":486},[5939],{"type":26,"value":2470},{"type":21,"tag":455,"props":5941,"children":5943},{"class":457,"line":5942},32,[5944,5949,5954,5959],{"type":21,"tag":455,"props":5945,"children":5946},{"style":1349},[5947],{"type":26,"value":5948},"      await",{"type":21,"tag":455,"props":5950,"children":5951},{"style":486},[5952],{"type":26,"value":5953}," request.",{"type":21,"tag":455,"props":5955,"children":5956},{"style":1365},[5957],{"type":26,"value":5958},"jwtVerify",{"type":21,"tag":455,"props":5960,"children":5961},{"style":486},[5962],{"type":26,"value":3398},{"type":21,"tag":455,"props":5964,"children":5966},{"class":457,"line":5965},33,[5967,5972,5976],{"type":21,"tag":455,"props":5968,"children":5969},{"style":486},[5970],{"type":26,"value":5971},"    } ",{"type":21,"tag":455,"props":5973,"children":5974},{"style":1349},[5975],{"type":26,"value":4638},{"type":21,"tag":455,"props":5977,"children":5978},{"style":486},[5979],{"type":26,"value":5980}," (err) {\n",{"type":21,"tag":455,"props":5982,"children":5984},{"class":457,"line":5983},34,[5985,5990,5995],{"type":21,"tag":455,"props":5986,"children":5987},{"style":486},[5988],{"type":26,"value":5989},"      reply.",{"type":21,"tag":455,"props":5991,"children":5992},{"style":1365},[5993],{"type":26,"value":5994},"send",{"type":21,"tag":455,"props":5996,"children":5997},{"style":486},[5998],{"type":26,"value":5999},"(err);\n",{"type":21,"tag":455,"props":6001,"children":6003},{"class":457,"line":6002},35,[6004],{"type":21,"tag":455,"props":6005,"children":6006},{"style":486},[6007],{"type":26,"value":6008},"    }\n",{"type":21,"tag":455,"props":6010,"children":6012},{"class":457,"line":6011},36,[6013],{"type":21,"tag":455,"props":6014,"children":6015},{"style":486},[6016],{"type":26,"value":4389},{"type":21,"tag":455,"props":6018,"children":6020},{"class":457,"line":6019},37,[6021],{"type":21,"tag":455,"props":6022,"children":6023},{"style":462},[6024],{"type":26,"value":6025},"  // Add a decorator so that routes can reference this via `fastify.authenticatedOptional`\n",{"type":21,"tag":455,"props":6027,"children":6029},{"class":457,"line":6028},38,[6030,6034,6038,6042,6046,6051,6055,6059,6063,6067,6071,6075,6079],{"type":21,"tag":455,"props":6031,"children":6032},{"style":1349},[6033],{"type":26,"value":4397},{"type":21,"tag":455,"props":6035,"children":6036},{"style":486},[6037],{"type":26,"value":4891},{"type":21,"tag":455,"props":6039,"children":6040},{"style":1365},[6041],{"type":26,"value":5885},{"type":21,"tag":455,"props":6043,"children":6044},{"style":486},[6045],{"type":26,"value":489},{"type":21,"tag":455,"props":6047,"children":6048},{"style":492},[6049],{"type":26,"value":6050},"\"authenticatedOptional\"",{"type":21,"tag":455,"props":6052,"children":6053},{"style":486},[6054],{"type":26,"value":2228},{"type":21,"tag":455,"props":6056,"children":6057},{"style":1349},[6058],{"type":26,"value":4063},{"type":21,"tag":455,"props":6060,"children":6061},{"style":1349},[6062],{"type":26,"value":4068},{"type":21,"tag":455,"props":6064,"children":6065},{"style":486},[6066],{"type":26,"value":489},{"type":21,"tag":455,"props":6068,"children":6069},{"style":4527},[6070],{"type":26,"value":5567},{"type":21,"tag":455,"props":6072,"children":6073},{"style":486},[6074],{"type":26,"value":2228},{"type":21,"tag":455,"props":6076,"children":6077},{"style":4527},[6078],{"type":26,"value":5923},{"type":21,"tag":455,"props":6080,"children":6081},{"style":486},[6082],{"type":26,"value":4879},{"type":21,"tag":455,"props":6084,"children":6086},{"class":457,"line":6085},39,[6087,6091],{"type":21,"tag":455,"props":6088,"children":6089},{"style":1349},[6090],{"type":26,"value":5935},{"type":21,"tag":455,"props":6092,"children":6093},{"style":486},[6094],{"type":26,"value":2470},{"type":21,"tag":455,"props":6096,"children":6098},{"class":457,"line":6097},40,[6099,6103,6107,6111],{"type":21,"tag":455,"props":6100,"children":6101},{"style":1349},[6102],{"type":26,"value":5948},{"type":21,"tag":455,"props":6104,"children":6105},{"style":486},[6106],{"type":26,"value":5953},{"type":21,"tag":455,"props":6108,"children":6109},{"style":1365},[6110],{"type":26,"value":5958},{"type":21,"tag":455,"props":6112,"children":6113},{"style":486},[6114],{"type":26,"value":3398},{"type":21,"tag":455,"props":6116,"children":6118},{"class":457,"line":6117},41,[6119,6123,6127],{"type":21,"tag":455,"props":6120,"children":6121},{"style":486},[6122],{"type":26,"value":5971},{"type":21,"tag":455,"props":6124,"children":6125},{"style":1349},[6126],{"type":26,"value":4638},{"type":21,"tag":455,"props":6128,"children":6129},{"style":486},[6130],{"type":26,"value":6131}," {}\n",{"type":21,"tag":455,"props":6133,"children":6135},{"class":457,"line":6134},42,[6136],{"type":21,"tag":455,"props":6137,"children":6138},{"style":486},[6139],{"type":26,"value":4389},{"type":21,"tag":455,"props":6141,"children":6143},{"class":457,"line":6142},43,[6144],{"type":21,"tag":455,"props":6145,"children":6146},{"style":486},[6147],{"type":26,"value":3952},{"type":21,"tag":22,"props":6149,"children":6150},{},[6151,6153,6158],{"type":26,"value":6152},"To load this plugin, ",{"type":21,"tag":246,"props":6154,"children":6156},{"className":6155},[],[6157],{"type":26,"value":3045},{"type":26,"value":6159}," will need to register it:",{"type":21,"tag":239,"props":6161,"children":6163},{"className":3170,"code":6162,"language":2804,"meta":8,"style":8},"  await app.register(require('@fastify/formbody'));\n  await app.register(require('./plugins/auth'));  // \u003C-- Register the plugin\n  await app.register(require('./routes/legacy'), {isProduction});\n  return app;\n",[6164],{"type":21,"tag":246,"props":6165,"children":6166},{"__ignoreMap":8},[6167,6202,6244,6279],{"type":21,"tag":455,"props":6168,"children":6169},{"class":457,"line":458},[6170,6174,6178,6182,6186,6190,6194,6198],{"type":21,"tag":455,"props":6171,"children":6172},{"style":1349},[6173],{"type":26,"value":4397},{"type":21,"tag":455,"props":6175,"children":6176},{"style":486},[6177],{"type":26,"value":3863},{"type":21,"tag":455,"props":6179,"children":6180},{"style":1365},[6181],{"type":26,"value":4406},{"type":21,"tag":455,"props":6183,"children":6184},{"style":486},[6185],{"type":26,"value":489},{"type":21,"tag":455,"props":6187,"children":6188},{"style":1365},[6189],{"type":26,"value":3464},{"type":21,"tag":455,"props":6191,"children":6192},{"style":486},[6193],{"type":26,"value":489},{"type":21,"tag":455,"props":6195,"children":6196},{"style":492},[6197],{"type":26,"value":4423},{"type":21,"tag":455,"props":6199,"children":6200},{"style":486},[6201],{"type":26,"value":3488},{"type":21,"tag":455,"props":6203,"children":6204},{"class":457,"line":336},[6205,6209,6213,6217,6221,6225,6229,6234,6239],{"type":21,"tag":455,"props":6206,"children":6207},{"style":1349},[6208],{"type":26,"value":4397},{"type":21,"tag":455,"props":6210,"children":6211},{"style":486},[6212],{"type":26,"value":3863},{"type":21,"tag":455,"props":6214,"children":6215},{"style":1365},[6216],{"type":26,"value":4406},{"type":21,"tag":455,"props":6218,"children":6219},{"style":486},[6220],{"type":26,"value":489},{"type":21,"tag":455,"props":6222,"children":6223},{"style":1365},[6224],{"type":26,"value":3464},{"type":21,"tag":455,"props":6226,"children":6227},{"style":486},[6228],{"type":26,"value":489},{"type":21,"tag":455,"props":6230,"children":6231},{"style":492},[6232],{"type":26,"value":6233},"'./plugins/auth'",{"type":21,"tag":455,"props":6235,"children":6236},{"style":486},[6237],{"type":26,"value":6238},"));  ",{"type":21,"tag":455,"props":6240,"children":6241},{"style":462},[6242],{"type":26,"value":6243},"// \u003C-- Register the plugin\n",{"type":21,"tag":455,"props":6245,"children":6246},{"class":457,"line":333},[6247,6251,6255,6259,6263,6267,6271,6275],{"type":21,"tag":455,"props":6248,"children":6249},{"style":1349},[6250],{"type":26,"value":4397},{"type":21,"tag":455,"props":6252,"children":6253},{"style":486},[6254],{"type":26,"value":3863},{"type":21,"tag":455,"props":6256,"children":6257},{"style":1365},[6258],{"type":26,"value":4406},{"type":21,"tag":455,"props":6260,"children":6261},{"style":486},[6262],{"type":26,"value":489},{"type":21,"tag":455,"props":6264,"children":6265},{"style":1365},[6266],{"type":26,"value":3464},{"type":21,"tag":455,"props":6268,"children":6269},{"style":486},[6270],{"type":26,"value":489},{"type":21,"tag":455,"props":6272,"children":6273},{"style":492},[6274],{"type":26,"value":4459},{"type":21,"tag":455,"props":6276,"children":6277},{"style":486},[6278],{"type":26,"value":4464},{"type":21,"tag":455,"props":6280,"children":6281},{"class":457,"line":1424},[6282,6286],{"type":21,"tag":455,"props":6283,"children":6284},{"style":1349},[6285],{"type":26,"value":4472},{"type":21,"tag":455,"props":6287,"children":6288},{"style":486},[6289],{"type":26,"value":4477},{"type":21,"tag":22,"props":6291,"children":6292},{},[6293,6295,6301,6303,6309,6311,6317],{"type":26,"value":6294},"With these decorators added, a route can now add a ",{"type":21,"tag":246,"props":6296,"children":6298},{"className":6297},[],[6299],{"type":26,"value":6300},"onRequest: [fastify.authenticated]",{"type":26,"value":6302}," or a ",{"type":21,"tag":246,"props":6304,"children":6306},{"className":6305},[],[6307],{"type":26,"value":6308},"onRequest: [fastify.authenticatedOptional]",{"type":26,"value":6310}," hook to handle authentication and to make user information available in the ",{"type":21,"tag":246,"props":6312,"children":6314},{"className":6313},[],[6315],{"type":26,"value":6316},"request.payload",{"type":26,"value":6318}," field.",{"type":21,"tag":80,"props":6320,"children":6322},{"id":6321},"prerequisite-2-setting-up-route-plugins",[6323],{"type":26,"value":6324},"Prerequisite #2: Setting up route plugins",{"type":21,"tag":22,"props":6326,"children":6327},{},[6328,6330,6336],{"type":26,"value":6329},"Adding a route to a Fastify instance is no different from adding any other piece of functionality to a Fastify instance, which is what plugins are designed to help organize. As such, routes can be defined as a nested tree of plugins. The now-legacy ",{"type":21,"tag":246,"props":6331,"children":6333},{"className":6332},[],[6334],{"type":26,"value":6335},"articles.js",{"type":26,"value":6337}," module is as good a place as any to start converting routes, so to set up an equivalent module for the Fastify routes, we'll:",{"type":21,"tag":183,"props":6339,"children":6340},{},[6341,6522,6662,6800],{"type":21,"tag":187,"props":6342,"children":6343},{},[6344,6346,6352,6354,6359,6360],{"type":26,"value":6345},"Import and register a ",{"type":21,"tag":246,"props":6347,"children":6349},{"className":6348},[],[6350],{"type":26,"value":6351},"routes",{"type":26,"value":6353}," plugin in ",{"type":21,"tag":246,"props":6355,"children":6357},{"className":6356},[],[6358],{"type":26,"value":3045},{"type":26,"value":831},{"type":21,"tag":239,"props":6361,"children":6363},{"className":3170,"code":6362,"language":2804,"meta":8,"style":8},"  await app.register(require('@fastify/formbody'));\n  await app.register(require('./plugins/auth'));\n  await app.register(require('./routes'));  // \u003C-- Register the plugin\n  await app.register(require('./routes/legacy'), {isProduction});\n  return app;\n",[6364],{"type":21,"tag":246,"props":6365,"children":6366},{"__ignoreMap":8},[6367,6402,6437,6476,6511],{"type":21,"tag":455,"props":6368,"children":6369},{"class":457,"line":458},[6370,6374,6378,6382,6386,6390,6394,6398],{"type":21,"tag":455,"props":6371,"children":6372},{"style":1349},[6373],{"type":26,"value":4397},{"type":21,"tag":455,"props":6375,"children":6376},{"style":486},[6377],{"type":26,"value":3863},{"type":21,"tag":455,"props":6379,"children":6380},{"style":1365},[6381],{"type":26,"value":4406},{"type":21,"tag":455,"props":6383,"children":6384},{"style":486},[6385],{"type":26,"value":489},{"type":21,"tag":455,"props":6387,"children":6388},{"style":1365},[6389],{"type":26,"value":3464},{"type":21,"tag":455,"props":6391,"children":6392},{"style":486},[6393],{"type":26,"value":489},{"type":21,"tag":455,"props":6395,"children":6396},{"style":492},[6397],{"type":26,"value":4423},{"type":21,"tag":455,"props":6399,"children":6400},{"style":486},[6401],{"type":26,"value":3488},{"type":21,"tag":455,"props":6403,"children":6404},{"class":457,"line":336},[6405,6409,6413,6417,6421,6425,6429,6433],{"type":21,"tag":455,"props":6406,"children":6407},{"style":1349},[6408],{"type":26,"value":4397},{"type":21,"tag":455,"props":6410,"children":6411},{"style":486},[6412],{"type":26,"value":3863},{"type":21,"tag":455,"props":6414,"children":6415},{"style":1365},[6416],{"type":26,"value":4406},{"type":21,"tag":455,"props":6418,"children":6419},{"style":486},[6420],{"type":26,"value":489},{"type":21,"tag":455,"props":6422,"children":6423},{"style":1365},[6424],{"type":26,"value":3464},{"type":21,"tag":455,"props":6426,"children":6427},{"style":486},[6428],{"type":26,"value":489},{"type":21,"tag":455,"props":6430,"children":6431},{"style":492},[6432],{"type":26,"value":6233},{"type":21,"tag":455,"props":6434,"children":6435},{"style":486},[6436],{"type":26,"value":3488},{"type":21,"tag":455,"props":6438,"children":6439},{"class":457,"line":333},[6440,6444,6448,6452,6456,6460,6464,6468,6472],{"type":21,"tag":455,"props":6441,"children":6442},{"style":1349},[6443],{"type":26,"value":4397},{"type":21,"tag":455,"props":6445,"children":6446},{"style":486},[6447],{"type":26,"value":3863},{"type":21,"tag":455,"props":6449,"children":6450},{"style":1365},[6451],{"type":26,"value":4406},{"type":21,"tag":455,"props":6453,"children":6454},{"style":486},[6455],{"type":26,"value":489},{"type":21,"tag":455,"props":6457,"children":6458},{"style":1365},[6459],{"type":26,"value":3464},{"type":21,"tag":455,"props":6461,"children":6462},{"style":486},[6463],{"type":26,"value":489},{"type":21,"tag":455,"props":6465,"children":6466},{"style":492},[6467],{"type":26,"value":3812},{"type":21,"tag":455,"props":6469,"children":6470},{"style":486},[6471],{"type":26,"value":6238},{"type":21,"tag":455,"props":6473,"children":6474},{"style":462},[6475],{"type":26,"value":6243},{"type":21,"tag":455,"props":6477,"children":6478},{"class":457,"line":1424},[6479,6483,6487,6491,6495,6499,6503,6507],{"type":21,"tag":455,"props":6480,"children":6481},{"style":1349},[6482],{"type":26,"value":4397},{"type":21,"tag":455,"props":6484,"children":6485},{"style":486},[6486],{"type":26,"value":3863},{"type":21,"tag":455,"props":6488,"children":6489},{"style":1365},[6490],{"type":26,"value":4406},{"type":21,"tag":455,"props":6492,"children":6493},{"style":486},[6494],{"type":26,"value":489},{"type":21,"tag":455,"props":6496,"children":6497},{"style":1365},[6498],{"type":26,"value":3464},{"type":21,"tag":455,"props":6500,"children":6501},{"style":486},[6502],{"type":26,"value":489},{"type":21,"tag":455,"props":6504,"children":6505},{"style":492},[6506],{"type":26,"value":4459},{"type":21,"tag":455,"props":6508,"children":6509},{"style":486},[6510],{"type":26,"value":4464},{"type":21,"tag":455,"props":6512,"children":6513},{"class":457,"line":1443},[6514,6518],{"type":21,"tag":455,"props":6515,"children":6516},{"style":1349},[6517],{"type":26,"value":4472},{"type":21,"tag":455,"props":6519,"children":6520},{"style":486},[6521],{"type":26,"value":4477},{"type":21,"tag":187,"props":6523,"children":6524},{},[6525,6527,6533,6535,6541,6543,6650,6653,6655,6660],{"type":26,"value":6526},"In ",{"type":21,"tag":246,"props":6528,"children":6530},{"className":6529},[],[6531],{"type":26,"value":6532},"routes/index.js",{"type":26,"value":6534},", import and register an ",{"type":21,"tag":246,"props":6536,"children":6538},{"className":6537},[],[6539],{"type":26,"value":6540},"api",{"type":26,"value":6542}," plugin:",{"type":21,"tag":239,"props":6544,"children":6546},{"className":3170,"code":6545,"language":2804,"meta":8,"style":8},"module.exports = async function (fastify, opts) {\n  await fastify.register(require('./api'), {prefix: \"/api\"});\n};\n",[6547],{"type":21,"tag":246,"props":6548,"children":6549},{"__ignoreMap":8},[6550,6598,6643],{"type":21,"tag":455,"props":6551,"children":6552},{"class":457,"line":458},[6553,6557,6561,6565,6569,6574,6578,6582,6586,6590,6594],{"type":21,"tag":455,"props":6554,"children":6555},{"style":480},[6556],{"type":26,"value":4826},{"type":21,"tag":455,"props":6558,"children":6559},{"style":486},[6560],{"type":26,"value":551},{"type":21,"tag":455,"props":6562,"children":6563},{"style":480},[6564],{"type":26,"value":4835},{"type":21,"tag":455,"props":6566,"children":6567},{"style":1349},[6568],{"type":26,"value":2131},{"type":21,"tag":455,"props":6570,"children":6571},{"style":1349},[6572],{"type":26,"value":6573}," async",{"type":21,"tag":455,"props":6575,"children":6576},{"style":1349},[6577],{"type":26,"value":4068},{"type":21,"tag":455,"props":6579,"children":6580},{"style":486},[6581],{"type":26,"value":2136},{"type":21,"tag":455,"props":6583,"children":6584},{"style":4527},[6585],{"type":26,"value":4865},{"type":21,"tag":455,"props":6587,"children":6588},{"style":486},[6589],{"type":26,"value":2228},{"type":21,"tag":455,"props":6591,"children":6592},{"style":4527},[6593],{"type":26,"value":4874},{"type":21,"tag":455,"props":6595,"children":6596},{"style":486},[6597],{"type":26,"value":4879},{"type":21,"tag":455,"props":6599,"children":6600},{"class":457,"line":336},[6601,6605,6609,6613,6617,6621,6625,6629,6634,6639],{"type":21,"tag":455,"props":6602,"children":6603},{"style":1349},[6604],{"type":26,"value":4397},{"type":21,"tag":455,"props":6606,"children":6607},{"style":486},[6608],{"type":26,"value":4891},{"type":21,"tag":455,"props":6610,"children":6611},{"style":1365},[6612],{"type":26,"value":4406},{"type":21,"tag":455,"props":6614,"children":6615},{"style":486},[6616],{"type":26,"value":489},{"type":21,"tag":455,"props":6618,"children":6619},{"style":1365},[6620],{"type":26,"value":3464},{"type":21,"tag":455,"props":6622,"children":6623},{"style":486},[6624],{"type":26,"value":489},{"type":21,"tag":455,"props":6626,"children":6627},{"style":492},[6628],{"type":26,"value":5128},{"type":21,"tag":455,"props":6630,"children":6631},{"style":486},[6632],{"type":26,"value":6633},"), {prefix: ",{"type":21,"tag":455,"props":6635,"children":6636},{"style":492},[6637],{"type":26,"value":6638},"\"/api\"",{"type":21,"tag":455,"props":6640,"children":6641},{"style":486},[6642],{"type":26,"value":3952},{"type":21,"tag":455,"props":6644,"children":6645},{"class":457,"line":333},[6646],{"type":21,"tag":455,"props":6647,"children":6648},{"style":486},[6649],{"type":26,"value":2611},{"type":21,"tag":648,"props":6651,"children":6652},{},[],{"type":26,"value":6654},"(Note how this plugin is registered with a ",{"type":21,"tag":246,"props":6656,"children":6658},{"className":6657},[],[6659],{"type":26,"value":6638},{"type":26,"value":6661}," prefix, so that all routes within will automatically have this prefixed to their route path)",{"type":21,"tag":187,"props":6663,"children":6664},{},[6665,6666,6672,6674,6680,6682,6788,6791,6793,6798],{"type":26,"value":6526},{"type":21,"tag":246,"props":6667,"children":6669},{"className":6668},[],[6670],{"type":26,"value":6671},"routes/api/index.js",{"type":26,"value":6673},", import and register the ",{"type":21,"tag":246,"props":6675,"children":6677},{"className":6676},[],[6678],{"type":26,"value":6679},"articles",{"type":26,"value":6681}," plugin that will contain our migrated routes:",{"type":21,"tag":239,"props":6683,"children":6685},{"className":3170,"code":6684,"language":2804,"meta":8,"style":8},"module.exports = async function (fastify, opts) {\n  await fastify.register(require('./articles'), {prefix: \"/articles\"});\n}\n",[6686],{"type":21,"tag":246,"props":6687,"children":6688},{"__ignoreMap":8},[6689,6736,6781],{"type":21,"tag":455,"props":6690,"children":6691},{"class":457,"line":458},[6692,6696,6700,6704,6708,6712,6716,6720,6724,6728,6732],{"type":21,"tag":455,"props":6693,"children":6694},{"style":480},[6695],{"type":26,"value":4826},{"type":21,"tag":455,"props":6697,"children":6698},{"style":486},[6699],{"type":26,"value":551},{"type":21,"tag":455,"props":6701,"children":6702},{"style":480},[6703],{"type":26,"value":4835},{"type":21,"tag":455,"props":6705,"children":6706},{"style":1349},[6707],{"type":26,"value":2131},{"type":21,"tag":455,"props":6709,"children":6710},{"style":1349},[6711],{"type":26,"value":6573},{"type":21,"tag":455,"props":6713,"children":6714},{"style":1349},[6715],{"type":26,"value":4068},{"type":21,"tag":455,"props":6717,"children":6718},{"style":486},[6719],{"type":26,"value":2136},{"type":21,"tag":455,"props":6721,"children":6722},{"style":4527},[6723],{"type":26,"value":4865},{"type":21,"tag":455,"props":6725,"children":6726},{"style":486},[6727],{"type":26,"value":2228},{"type":21,"tag":455,"props":6729,"children":6730},{"style":4527},[6731],{"type":26,"value":4874},{"type":21,"tag":455,"props":6733,"children":6734},{"style":486},[6735],{"type":26,"value":4879},{"type":21,"tag":455,"props":6737,"children":6738},{"class":457,"line":336},[6739,6743,6747,6751,6755,6759,6763,6768,6772,6777],{"type":21,"tag":455,"props":6740,"children":6741},{"style":1349},[6742],{"type":26,"value":4397},{"type":21,"tag":455,"props":6744,"children":6745},{"style":486},[6746],{"type":26,"value":4891},{"type":21,"tag":455,"props":6748,"children":6749},{"style":1365},[6750],{"type":26,"value":4406},{"type":21,"tag":455,"props":6752,"children":6753},{"style":486},[6754],{"type":26,"value":489},{"type":21,"tag":455,"props":6756,"children":6757},{"style":1365},[6758],{"type":26,"value":3464},{"type":21,"tag":455,"props":6760,"children":6761},{"style":486},[6762],{"type":26,"value":489},{"type":21,"tag":455,"props":6764,"children":6765},{"style":492},[6766],{"type":26,"value":6767},"'./articles'",{"type":21,"tag":455,"props":6769,"children":6770},{"style":486},[6771],{"type":26,"value":6633},{"type":21,"tag":455,"props":6773,"children":6774},{"style":492},[6775],{"type":26,"value":6776},"\"/articles\"",{"type":21,"tag":455,"props":6778,"children":6779},{"style":486},[6780],{"type":26,"value":3952},{"type":21,"tag":455,"props":6782,"children":6783},{"class":457,"line":333},[6784],{"type":21,"tag":455,"props":6785,"children":6786},{"style":486},[6787],{"type":26,"value":2002},{"type":21,"tag":648,"props":6789,"children":6790},{},[],{"type":26,"value":6792},"(Note again the automatic ",{"type":21,"tag":246,"props":6794,"children":6796},{"className":6795},[],[6797],{"type":26,"value":6776},{"type":26,"value":6799}," prefix which will be applied to those routes)",{"type":21,"tag":187,"props":6801,"children":6802},{},[6803,6805,6811,6813],{"type":26,"value":6804},"Finally, ",{"type":21,"tag":246,"props":6806,"children":6808},{"className":6807},[],[6809],{"type":26,"value":6810},"routes/api/articles.js",{"type":26,"value":6812}," can start as an empty plugin, ready for us to define routes within:",{"type":21,"tag":239,"props":6814,"children":6816},{"className":3170,"code":6815,"language":2804,"meta":8,"style":8},"module.exports = async function (fastify, opts) {\n}\n",[6817],{"type":21,"tag":246,"props":6818,"children":6819},{"__ignoreMap":8},[6820,6867],{"type":21,"tag":455,"props":6821,"children":6822},{"class":457,"line":458},[6823,6827,6831,6835,6839,6843,6847,6851,6855,6859,6863],{"type":21,"tag":455,"props":6824,"children":6825},{"style":480},[6826],{"type":26,"value":4826},{"type":21,"tag":455,"props":6828,"children":6829},{"style":486},[6830],{"type":26,"value":551},{"type":21,"tag":455,"props":6832,"children":6833},{"style":480},[6834],{"type":26,"value":4835},{"type":21,"tag":455,"props":6836,"children":6837},{"style":1349},[6838],{"type":26,"value":2131},{"type":21,"tag":455,"props":6840,"children":6841},{"style":1349},[6842],{"type":26,"value":6573},{"type":21,"tag":455,"props":6844,"children":6845},{"style":1349},[6846],{"type":26,"value":4068},{"type":21,"tag":455,"props":6848,"children":6849},{"style":486},[6850],{"type":26,"value":2136},{"type":21,"tag":455,"props":6852,"children":6853},{"style":4527},[6854],{"type":26,"value":4865},{"type":21,"tag":455,"props":6856,"children":6857},{"style":486},[6858],{"type":26,"value":2228},{"type":21,"tag":455,"props":6860,"children":6861},{"style":4527},[6862],{"type":26,"value":4874},{"type":21,"tag":455,"props":6864,"children":6865},{"style":486},[6866],{"type":26,"value":4879},{"type":21,"tag":455,"props":6868,"children":6869},{"class":457,"line":336},[6870],{"type":21,"tag":455,"props":6871,"children":6872},{"style":486},[6873],{"type":26,"value":2002},{"type":21,"tag":80,"props":6875,"children":6877},{"id":6876},"migrating-a-route",[6878],{"type":26,"value":6879},"Migrating a route",{"type":21,"tag":22,"props":6881,"children":6882},{},[6883,6885,6890,6892,6898],{"type":26,"value":6884},"With the Fastify ",{"type":21,"tag":246,"props":6886,"children":6888},{"className":6887},[],[6889],{"type":26,"value":6679},{"type":26,"value":6891}," plugin ready to accept new routes, let's look at the first Express route, ",{"type":21,"tag":246,"props":6893,"children":6895},{"className":6894},[],[6896],{"type":26,"value":6897},"GET /api/articles",{"type":26,"value":6899},", piece by piece:",{"type":21,"tag":22,"props":6901,"children":6902},{},[6903,6909],{"type":21,"tag":246,"props":6904,"children":6906},{"className":6905},[],[6907],{"type":26,"value":6908},"routes/legacy/api/articles.js",{"type":26,"value":831},{"type":21,"tag":239,"props":6911,"children":6913},{"className":3170,"code":6912,"language":2804,"meta":8,"style":8},"/* ... */\n\nrouter.get('/', auth.optional, function(req, res, next) {\n  /* ... */\n});\n\n",[6914],{"type":21,"tag":246,"props":6915,"children":6916},{"__ignoreMap":8},[6917,6924,6931,6993,7001],{"type":21,"tag":455,"props":6918,"children":6919},{"class":457,"line":458},[6920],{"type":21,"tag":455,"props":6921,"children":6922},{"style":462},[6923],{"type":26,"value":3318},{"type":21,"tag":455,"props":6925,"children":6926},{"class":457,"line":336},[6927],{"type":21,"tag":455,"props":6928,"children":6929},{"emptyLinePlaceholder":471},[6930],{"type":26,"value":474},{"type":21,"tag":455,"props":6932,"children":6933},{"class":457,"line":333},[6934,6939,6944,6948,6953,6958,6962,6966,6971,6975,6980,6984,6989],{"type":21,"tag":455,"props":6935,"children":6936},{"style":486},[6937],{"type":26,"value":6938},"router.",{"type":21,"tag":455,"props":6940,"children":6941},{"style":1365},[6942],{"type":26,"value":6943},"get",{"type":21,"tag":455,"props":6945,"children":6946},{"style":486},[6947],{"type":26,"value":489},{"type":21,"tag":455,"props":6949,"children":6950},{"style":492},[6951],{"type":26,"value":6952},"'/'",{"type":21,"tag":455,"props":6954,"children":6955},{"style":486},[6956],{"type":26,"value":6957},", auth.optional, ",{"type":21,"tag":455,"props":6959,"children":6960},{"style":1349},[6961],{"type":26,"value":3897},{"type":21,"tag":455,"props":6963,"children":6964},{"style":486},[6965],{"type":26,"value":489},{"type":21,"tag":455,"props":6967,"children":6968},{"style":4527},[6969],{"type":26,"value":6970},"req",{"type":21,"tag":455,"props":6972,"children":6973},{"style":486},[6974],{"type":26,"value":2228},{"type":21,"tag":455,"props":6976,"children":6977},{"style":4527},[6978],{"type":26,"value":6979},"res",{"type":21,"tag":455,"props":6981,"children":6982},{"style":486},[6983],{"type":26,"value":2228},{"type":21,"tag":455,"props":6985,"children":6986},{"style":4527},[6987],{"type":26,"value":6988},"next",{"type":21,"tag":455,"props":6990,"children":6991},{"style":486},[6992],{"type":26,"value":4879},{"type":21,"tag":455,"props":6994,"children":6995},{"class":457,"line":1424},[6996],{"type":21,"tag":455,"props":6997,"children":6998},{"style":462},[6999],{"type":26,"value":7000},"  /* ... */\n",{"type":21,"tag":455,"props":7002,"children":7003},{"class":457,"line":1443},[7004],{"type":21,"tag":455,"props":7005,"children":7006},{"style":486},[7007],{"type":26,"value":3952},{"type":21,"tag":22,"props":7009,"children":7010},{},[7011,7013,7019,7021,7027],{"type":26,"value":7012},"Fastify does have a ",{"type":21,"tag":246,"props":7014,"children":7016},{"className":7015},[],[7017],{"type":26,"value":7018},".get()",{"type":26,"value":7020}," shorthand like this, but I recommend getting used to using the full ",{"type":21,"tag":246,"props":7022,"children":7024},{"className":7023},[],[7025],{"type":26,"value":7026},".route()",{"type":26,"value":7028}," declaration since I find it more readable when other options are defined:",{"type":21,"tag":22,"props":7030,"children":7031},{},[7032,7037],{"type":21,"tag":246,"props":7033,"children":7035},{"className":7034},[],[7036],{"type":26,"value":6810},{"type":26,"value":831},{"type":21,"tag":239,"props":7039,"children":7041},{"className":3170,"code":7040,"language":2804,"meta":8,"style":8},"  /* ... */\n\n  fastify.route({\n    method: 'GET',\n    url: '/',\n    onRequest: [fastify.authenticatedOptional],\n    handler: async function(request, reply) {\n      /* ... */\n    },\n  });\n\n  /* ... */\n",[7042],{"type":21,"tag":246,"props":7043,"children":7044},{"__ignoreMap":8},[7045,7052,7059,7076,7093,7109,7117,7158,7166,7173,7180,7187],{"type":21,"tag":455,"props":7046,"children":7047},{"class":457,"line":458},[7048],{"type":21,"tag":455,"props":7049,"children":7050},{"style":462},[7051],{"type":26,"value":7000},{"type":21,"tag":455,"props":7053,"children":7054},{"class":457,"line":336},[7055],{"type":21,"tag":455,"props":7056,"children":7057},{"emptyLinePlaceholder":471},[7058],{"type":26,"value":474},{"type":21,"tag":455,"props":7060,"children":7061},{"class":457,"line":333},[7062,7066,7071],{"type":21,"tag":455,"props":7063,"children":7064},{"style":486},[7065],{"type":26,"value":4964},{"type":21,"tag":455,"props":7067,"children":7068},{"style":1365},[7069],{"type":26,"value":7070},"route",{"type":21,"tag":455,"props":7072,"children":7073},{"style":486},[7074],{"type":26,"value":7075},"({\n",{"type":21,"tag":455,"props":7077,"children":7078},{"class":457,"line":1424},[7079,7084,7089],{"type":21,"tag":455,"props":7080,"children":7081},{"style":486},[7082],{"type":26,"value":7083},"    method: ",{"type":21,"tag":455,"props":7085,"children":7086},{"style":492},[7087],{"type":26,"value":7088},"'GET'",{"type":21,"tag":455,"props":7090,"children":7091},{"style":486},[7092],{"type":26,"value":2483},{"type":21,"tag":455,"props":7094,"children":7095},{"class":457,"line":1443},[7096,7101,7105],{"type":21,"tag":455,"props":7097,"children":7098},{"style":486},[7099],{"type":26,"value":7100},"    url: ",{"type":21,"tag":455,"props":7102,"children":7103},{"style":492},[7104],{"type":26,"value":6952},{"type":21,"tag":455,"props":7106,"children":7107},{"style":486},[7108],{"type":26,"value":2483},{"type":21,"tag":455,"props":7110,"children":7111},{"class":457,"line":1489},[7112],{"type":21,"tag":455,"props":7113,"children":7114},{"style":486},[7115],{"type":26,"value":7116},"    onRequest: [fastify.authenticatedOptional],\n",{"type":21,"tag":455,"props":7118,"children":7119},{"class":457,"line":1506},[7120,7125,7130,7134,7138,7142,7146,7150,7154],{"type":21,"tag":455,"props":7121,"children":7122},{"style":1365},[7123],{"type":26,"value":7124},"    handler",{"type":21,"tag":455,"props":7126,"children":7127},{"style":486},[7128],{"type":26,"value":7129},": ",{"type":21,"tag":455,"props":7131,"children":7132},{"style":1349},[7133],{"type":26,"value":4063},{"type":21,"tag":455,"props":7135,"children":7136},{"style":1349},[7137],{"type":26,"value":4068},{"type":21,"tag":455,"props":7139,"children":7140},{"style":486},[7141],{"type":26,"value":489},{"type":21,"tag":455,"props":7143,"children":7144},{"style":4527},[7145],{"type":26,"value":5567},{"type":21,"tag":455,"props":7147,"children":7148},{"style":486},[7149],{"type":26,"value":2228},{"type":21,"tag":455,"props":7151,"children":7152},{"style":4527},[7153],{"type":26,"value":5923},{"type":21,"tag":455,"props":7155,"children":7156},{"style":486},[7157],{"type":26,"value":4879},{"type":21,"tag":455,"props":7159,"children":7160},{"class":457,"line":1547},[7161],{"type":21,"tag":455,"props":7162,"children":7163},{"style":462},[7164],{"type":26,"value":7165},"      /* ... */\n",{"type":21,"tag":455,"props":7167,"children":7168},{"class":457,"line":1564},[7169],{"type":21,"tag":455,"props":7170,"children":7171},{"style":486},[7172],{"type":26,"value":5854},{"type":21,"tag":455,"props":7174,"children":7175},{"class":457,"line":1605},[7176],{"type":21,"tag":455,"props":7177,"children":7178},{"style":486},[7179],{"type":26,"value":4389},{"type":21,"tag":455,"props":7181,"children":7182},{"class":457,"line":1622},[7183],{"type":21,"tag":455,"props":7184,"children":7185},{"emptyLinePlaceholder":471},[7186],{"type":26,"value":474},{"type":21,"tag":455,"props":7188,"children":7189},{"class":457,"line":1663},[7190],{"type":21,"tag":455,"props":7191,"children":7192},{"style":462},[7193],{"type":26,"value":7000},{"type":21,"tag":22,"props":7195,"children":7196},{},[7197,7199,7205,7207,7213],{"type":26,"value":7198},"Note the ",{"type":21,"tag":246,"props":7200,"children":7202},{"className":7201},[],[7203],{"type":26,"value":7204},"onRequest",{"type":26,"value":7206}," hook which uses the authentication hook and decorator we defined previously in the ",{"type":21,"tag":246,"props":7208,"children":7210},{"className":7209},[],[7211],{"type":26,"value":7212},"auth",{"type":26,"value":7214}," plugin.",{"type":21,"tag":22,"props":7216,"children":7217},{},[7218],{"type":26,"value":7219},"Next, the Express route checks for a number of query parameters and fetches some database objects:",{"type":21,"tag":22,"props":7221,"children":7222},{},[7223,7228],{"type":21,"tag":246,"props":7224,"children":7226},{"className":7225},[],[7227],{"type":26,"value":6908},{"type":26,"value":831},{"type":21,"tag":239,"props":7230,"children":7232},{"className":3170,"code":7231,"language":2804,"meta":8,"style":8},"  /* ... */\n\n  var query = {};\n  var limit = 20;\n  var offset = 0;\n\n  if(typeof req.query.limit !== 'undefined'){\n    limit = req.query.limit;\n  }\n\n  if(typeof req.query.offset !== 'undefined'){\n    offset = req.query.offset;\n  }\n\n  if( typeof req.query.tag !== 'undefined' ){\n    query.tagList = {\"$in\" : [req.query.tag]};\n  }\n\n  Promise.all([\n    req.query.author ? User.findOne({username: req.query.author}) : null,\n    req.query.favorited ? User.findOne({username: req.query.favorited}) : null\n  ]).then(function(results){\n    /* ... */\n  });\n\n  /* ... */\n",[7233],{"type":21,"tag":246,"props":7234,"children":7235},{"__ignoreMap":8},[7236,7243,7250,7272,7297,7321,7328,7364,7381,7388,7395,7427,7444,7451,7458,7492,7519,7526,7533,7555,7595,7629,7662,7670,7677,7684],{"type":21,"tag":455,"props":7237,"children":7238},{"class":457,"line":458},[7239],{"type":21,"tag":455,"props":7240,"children":7241},{"style":462},[7242],{"type":26,"value":7000},{"type":21,"tag":455,"props":7244,"children":7245},{"class":457,"line":336},[7246],{"type":21,"tag":455,"props":7247,"children":7248},{"emptyLinePlaceholder":471},[7249],{"type":26,"value":474},{"type":21,"tag":455,"props":7251,"children":7252},{"class":457,"line":333},[7253,7258,7263,7267],{"type":21,"tag":455,"props":7254,"children":7255},{"style":1349},[7256],{"type":26,"value":7257},"  var",{"type":21,"tag":455,"props":7259,"children":7260},{"style":486},[7261],{"type":26,"value":7262}," query ",{"type":21,"tag":455,"props":7264,"children":7265},{"style":1349},[7266],{"type":26,"value":3193},{"type":21,"tag":455,"props":7268,"children":7269},{"style":486},[7270],{"type":26,"value":7271}," {};\n",{"type":21,"tag":455,"props":7273,"children":7274},{"class":457,"line":1424},[7275,7279,7284,7288,7293],{"type":21,"tag":455,"props":7276,"children":7277},{"style":1349},[7278],{"type":26,"value":7257},{"type":21,"tag":455,"props":7280,"children":7281},{"style":486},[7282],{"type":26,"value":7283}," limit ",{"type":21,"tag":455,"props":7285,"children":7286},{"style":1349},[7287],{"type":26,"value":3193},{"type":21,"tag":455,"props":7289,"children":7290},{"style":480},[7291],{"type":26,"value":7292}," 20",{"type":21,"tag":455,"props":7294,"children":7295},{"style":486},[7296],{"type":26,"value":1440},{"type":21,"tag":455,"props":7298,"children":7299},{"class":457,"line":1443},[7300,7304,7309,7313,7317],{"type":21,"tag":455,"props":7301,"children":7302},{"style":1349},[7303],{"type":26,"value":7257},{"type":21,"tag":455,"props":7305,"children":7306},{"style":486},[7307],{"type":26,"value":7308}," offset ",{"type":21,"tag":455,"props":7310,"children":7311},{"style":1349},[7312],{"type":26,"value":3193},{"type":21,"tag":455,"props":7314,"children":7315},{"style":480},[7316],{"type":26,"value":1417},{"type":21,"tag":455,"props":7318,"children":7319},{"style":486},[7320],{"type":26,"value":1440},{"type":21,"tag":455,"props":7322,"children":7323},{"class":457,"line":1489},[7324],{"type":21,"tag":455,"props":7325,"children":7326},{"emptyLinePlaceholder":471},[7327],{"type":26,"value":474},{"type":21,"tag":455,"props":7329,"children":7330},{"class":457,"line":1506},[7331,7335,7339,7344,7349,7354,7359],{"type":21,"tag":455,"props":7332,"children":7333},{"style":1349},[7334],{"type":26,"value":4126},{"type":21,"tag":455,"props":7336,"children":7337},{"style":486},[7338],{"type":26,"value":489},{"type":21,"tag":455,"props":7340,"children":7341},{"style":1349},[7342],{"type":26,"value":7343},"typeof",{"type":21,"tag":455,"props":7345,"children":7346},{"style":486},[7347],{"type":26,"value":7348}," req.query.limit ",{"type":21,"tag":455,"props":7350,"children":7351},{"style":1349},[7352],{"type":26,"value":7353},"!==",{"type":21,"tag":455,"props":7355,"children":7356},{"style":492},[7357],{"type":26,"value":7358}," 'undefined'",{"type":21,"tag":455,"props":7360,"children":7361},{"style":486},[7362],{"type":26,"value":7363},"){\n",{"type":21,"tag":455,"props":7365,"children":7366},{"class":457,"line":1547},[7367,7372,7376],{"type":21,"tag":455,"props":7368,"children":7369},{"style":486},[7370],{"type":26,"value":7371},"    limit ",{"type":21,"tag":455,"props":7373,"children":7374},{"style":1349},[7375],{"type":26,"value":3193},{"type":21,"tag":455,"props":7377,"children":7378},{"style":486},[7379],{"type":26,"value":7380}," req.query.limit;\n",{"type":21,"tag":455,"props":7382,"children":7383},{"class":457,"line":1564},[7384],{"type":21,"tag":455,"props":7385,"children":7386},{"style":486},[7387],{"type":26,"value":4232},{"type":21,"tag":455,"props":7389,"children":7390},{"class":457,"line":1605},[7391],{"type":21,"tag":455,"props":7392,"children":7393},{"emptyLinePlaceholder":471},[7394],{"type":26,"value":474},{"type":21,"tag":455,"props":7396,"children":7397},{"class":457,"line":1622},[7398,7402,7406,7410,7415,7419,7423],{"type":21,"tag":455,"props":7399,"children":7400},{"style":1349},[7401],{"type":26,"value":4126},{"type":21,"tag":455,"props":7403,"children":7404},{"style":486},[7405],{"type":26,"value":489},{"type":21,"tag":455,"props":7407,"children":7408},{"style":1349},[7409],{"type":26,"value":7343},{"type":21,"tag":455,"props":7411,"children":7412},{"style":486},[7413],{"type":26,"value":7414}," req.query.offset ",{"type":21,"tag":455,"props":7416,"children":7417},{"style":1349},[7418],{"type":26,"value":7353},{"type":21,"tag":455,"props":7420,"children":7421},{"style":492},[7422],{"type":26,"value":7358},{"type":21,"tag":455,"props":7424,"children":7425},{"style":486},[7426],{"type":26,"value":7363},{"type":21,"tag":455,"props":7428,"children":7429},{"class":457,"line":1663},[7430,7435,7439],{"type":21,"tag":455,"props":7431,"children":7432},{"style":486},[7433],{"type":26,"value":7434},"    offset ",{"type":21,"tag":455,"props":7436,"children":7437},{"style":1349},[7438],{"type":26,"value":3193},{"type":21,"tag":455,"props":7440,"children":7441},{"style":486},[7442],{"type":26,"value":7443}," req.query.offset;\n",{"type":21,"tag":455,"props":7445,"children":7446},{"class":457,"line":1680},[7447],{"type":21,"tag":455,"props":7448,"children":7449},{"style":486},[7450],{"type":26,"value":4232},{"type":21,"tag":455,"props":7452,"children":7453},{"class":457,"line":1721},[7454],{"type":21,"tag":455,"props":7455,"children":7456},{"emptyLinePlaceholder":471},[7457],{"type":26,"value":474},{"type":21,"tag":455,"props":7459,"children":7460},{"class":457,"line":1738},[7461,7465,7470,7474,7479,7483,7487],{"type":21,"tag":455,"props":7462,"children":7463},{"style":1349},[7464],{"type":26,"value":4126},{"type":21,"tag":455,"props":7466,"children":7467},{"style":486},[7468],{"type":26,"value":7469},"( ",{"type":21,"tag":455,"props":7471,"children":7472},{"style":1349},[7473],{"type":26,"value":7343},{"type":21,"tag":455,"props":7475,"children":7476},{"style":486},[7477],{"type":26,"value":7478}," req.query.tag ",{"type":21,"tag":455,"props":7480,"children":7481},{"style":1349},[7482],{"type":26,"value":7353},{"type":21,"tag":455,"props":7484,"children":7485},{"style":492},[7486],{"type":26,"value":7358},{"type":21,"tag":455,"props":7488,"children":7489},{"style":486},[7490],{"type":26,"value":7491}," ){\n",{"type":21,"tag":455,"props":7493,"children":7494},{"class":457,"line":1779},[7495,7500,7504,7509,7514],{"type":21,"tag":455,"props":7496,"children":7497},{"style":486},[7498],{"type":26,"value":7499},"    query.tagList ",{"type":21,"tag":455,"props":7501,"children":7502},{"style":1349},[7503],{"type":26,"value":3193},{"type":21,"tag":455,"props":7505,"children":7506},{"style":486},[7507],{"type":26,"value":7508}," {",{"type":21,"tag":455,"props":7510,"children":7511},{"style":492},[7512],{"type":26,"value":7513},"\"$in\"",{"type":21,"tag":455,"props":7515,"children":7516},{"style":486},[7517],{"type":26,"value":7518}," : [req.query.tag]};\n",{"type":21,"tag":455,"props":7520,"children":7521},{"class":457,"line":1796},[7522],{"type":21,"tag":455,"props":7523,"children":7524},{"style":486},[7525],{"type":26,"value":4232},{"type":21,"tag":455,"props":7527,"children":7528},{"class":457,"line":1837},[7529],{"type":21,"tag":455,"props":7530,"children":7531},{"emptyLinePlaceholder":471},[7532],{"type":26,"value":474},{"type":21,"tag":455,"props":7534,"children":7535},{"class":457,"line":1854},[7536,7541,7545,7550],{"type":21,"tag":455,"props":7537,"children":7538},{"style":480},[7539],{"type":26,"value":7540},"  Promise",{"type":21,"tag":455,"props":7542,"children":7543},{"style":486},[7544],{"type":26,"value":551},{"type":21,"tag":455,"props":7546,"children":7547},{"style":1365},[7548],{"type":26,"value":7549},"all",{"type":21,"tag":455,"props":7551,"children":7552},{"style":486},[7553],{"type":26,"value":7554},"([\n",{"type":21,"tag":455,"props":7556,"children":7557},{"class":457,"line":1895},[7558,7563,7567,7572,7577,7582,7586,7591],{"type":21,"tag":455,"props":7559,"children":7560},{"style":486},[7561],{"type":26,"value":7562},"    req.query.author ",{"type":21,"tag":455,"props":7564,"children":7565},{"style":1349},[7566],{"type":26,"value":1116},{"type":21,"tag":455,"props":7568,"children":7569},{"style":486},[7570],{"type":26,"value":7571}," User.",{"type":21,"tag":455,"props":7573,"children":7574},{"style":1365},[7575],{"type":26,"value":7576},"findOne",{"type":21,"tag":455,"props":7578,"children":7579},{"style":486},[7580],{"type":26,"value":7581},"({username: req.query.author}) ",{"type":21,"tag":455,"props":7583,"children":7584},{"style":1349},[7585],{"type":26,"value":831},{"type":21,"tag":455,"props":7587,"children":7588},{"style":480},[7589],{"type":26,"value":7590}," null",{"type":21,"tag":455,"props":7592,"children":7593},{"style":486},[7594],{"type":26,"value":2483},{"type":21,"tag":455,"props":7596,"children":7597},{"class":457,"line":1912},[7598,7603,7607,7611,7615,7620,7624],{"type":21,"tag":455,"props":7599,"children":7600},{"style":486},[7601],{"type":26,"value":7602},"    req.query.favorited ",{"type":21,"tag":455,"props":7604,"children":7605},{"style":1349},[7606],{"type":26,"value":1116},{"type":21,"tag":455,"props":7608,"children":7609},{"style":486},[7610],{"type":26,"value":7571},{"type":21,"tag":455,"props":7612,"children":7613},{"style":1365},[7614],{"type":26,"value":7576},{"type":21,"tag":455,"props":7616,"children":7617},{"style":486},[7618],{"type":26,"value":7619},"({username: req.query.favorited}) ",{"type":21,"tag":455,"props":7621,"children":7622},{"style":1349},[7623],{"type":26,"value":831},{"type":21,"tag":455,"props":7625,"children":7626},{"style":480},[7627],{"type":26,"value":7628}," null\n",{"type":21,"tag":455,"props":7630,"children":7631},{"class":457,"line":1953},[7632,7637,7641,7645,7649,7653,7658],{"type":21,"tag":455,"props":7633,"children":7634},{"style":486},[7635],{"type":26,"value":7636},"  ]).",{"type":21,"tag":455,"props":7638,"children":7639},{"style":1365},[7640],{"type":26,"value":4520},{"type":21,"tag":455,"props":7642,"children":7643},{"style":486},[7644],{"type":26,"value":489},{"type":21,"tag":455,"props":7646,"children":7647},{"style":1349},[7648],{"type":26,"value":3897},{"type":21,"tag":455,"props":7650,"children":7651},{"style":486},[7652],{"type":26,"value":489},{"type":21,"tag":455,"props":7654,"children":7655},{"style":4527},[7656],{"type":26,"value":7657},"results",{"type":21,"tag":455,"props":7659,"children":7660},{"style":486},[7661],{"type":26,"value":7363},{"type":21,"tag":455,"props":7663,"children":7664},{"class":457,"line":1970},[7665],{"type":21,"tag":455,"props":7666,"children":7667},{"style":462},[7668],{"type":26,"value":7669},"    /* ... */\n",{"type":21,"tag":455,"props":7671,"children":7672},{"class":457,"line":1978},[7673],{"type":21,"tag":455,"props":7674,"children":7675},{"style":486},[7676],{"type":26,"value":4389},{"type":21,"tag":455,"props":7678,"children":7679},{"class":457,"line":1996},[7680],{"type":21,"tag":455,"props":7681,"children":7682},{"emptyLinePlaceholder":471},[7683],{"type":26,"value":474},{"type":21,"tag":455,"props":7685,"children":7686},{"class":457,"line":4487},[7687],{"type":21,"tag":455,"props":7688,"children":7689},{"style":462},[7690],{"type":26,"value":7000},{"type":21,"tag":22,"props":7692,"children":7693},{},[7694],{"type":26,"value":7695},"This is a place where beyond just replicating the Express route's behaviour, the Fastify route can easily improve upon it by adding more thorough schema validation for these parameters:",{"type":21,"tag":22,"props":7697,"children":7698},{},[7699,7704],{"type":21,"tag":246,"props":7700,"children":7702},{"className":7701},[],[7703],{"type":26,"value":6810},{"type":26,"value":831},{"type":21,"tag":239,"props":7706,"children":7708},{"className":3170,"code":7707,"language":2804,"meta":8,"style":8},"  fastify.route({\n    method: 'GET',\n    url: '/',\n    onRequest: [fastify.authenticatedOptional],\n    schema: {   // \u003C--- `schema` field added\n      query: {  // \u003C--- query param validation added (also works for body, params, headers)\n        type: \"object\",\n        properties: {\n          limit: {type: \"number\", minimum: 1},\n          offset: {type: \"number\", minimum: 0},\n          tag: {type: \"string\", minLength: 1},\n          author: {type: \"string\", minLength: 1},\n          favorited: {type: \"string\", minLength: 1},\n        },\n        required: [],\n      },\n    },\n    handler: async function(request, reply) {\n      /* ... */\n    },\n  });\n\n  /* ... */\n",[7709],{"type":21,"tag":246,"props":7710,"children":7711},{"__ignoreMap":8},[7712,7727,7742,7757,7764,7777,7790,7807,7815,7843,7868,7894,7918,7942,7950,7958,7965,7972,8011,8018,8025,8032,8039],{"type":21,"tag":455,"props":7713,"children":7714},{"class":457,"line":458},[7715,7719,7723],{"type":21,"tag":455,"props":7716,"children":7717},{"style":486},[7718],{"type":26,"value":4964},{"type":21,"tag":455,"props":7720,"children":7721},{"style":1365},[7722],{"type":26,"value":7070},{"type":21,"tag":455,"props":7724,"children":7725},{"style":486},[7726],{"type":26,"value":7075},{"type":21,"tag":455,"props":7728,"children":7729},{"class":457,"line":336},[7730,7734,7738],{"type":21,"tag":455,"props":7731,"children":7732},{"style":486},[7733],{"type":26,"value":7083},{"type":21,"tag":455,"props":7735,"children":7736},{"style":492},[7737],{"type":26,"value":7088},{"type":21,"tag":455,"props":7739,"children":7740},{"style":486},[7741],{"type":26,"value":2483},{"type":21,"tag":455,"props":7743,"children":7744},{"class":457,"line":333},[7745,7749,7753],{"type":21,"tag":455,"props":7746,"children":7747},{"style":486},[7748],{"type":26,"value":7100},{"type":21,"tag":455,"props":7750,"children":7751},{"style":492},[7752],{"type":26,"value":6952},{"type":21,"tag":455,"props":7754,"children":7755},{"style":486},[7756],{"type":26,"value":2483},{"type":21,"tag":455,"props":7758,"children":7759},{"class":457,"line":1424},[7760],{"type":21,"tag":455,"props":7761,"children":7762},{"style":486},[7763],{"type":26,"value":7116},{"type":21,"tag":455,"props":7765,"children":7766},{"class":457,"line":1443},[7767,7772],{"type":21,"tag":455,"props":7768,"children":7769},{"style":486},[7770],{"type":26,"value":7771},"    schema: {   ",{"type":21,"tag":455,"props":7773,"children":7774},{"style":462},[7775],{"type":26,"value":7776},"// \u003C--- `schema` field added\n",{"type":21,"tag":455,"props":7778,"children":7779},{"class":457,"line":1489},[7780,7785],{"type":21,"tag":455,"props":7781,"children":7782},{"style":486},[7783],{"type":26,"value":7784},"      query: {  ",{"type":21,"tag":455,"props":7786,"children":7787},{"style":462},[7788],{"type":26,"value":7789},"// \u003C--- query param validation added (also works for body, params, headers)\n",{"type":21,"tag":455,"props":7791,"children":7792},{"class":457,"line":1506},[7793,7798,7803],{"type":21,"tag":455,"props":7794,"children":7795},{"style":486},[7796],{"type":26,"value":7797},"        type: ",{"type":21,"tag":455,"props":7799,"children":7800},{"style":492},[7801],{"type":26,"value":7802},"\"object\"",{"type":21,"tag":455,"props":7804,"children":7805},{"style":486},[7806],{"type":26,"value":2483},{"type":21,"tag":455,"props":7808,"children":7809},{"class":457,"line":1547},[7810],{"type":21,"tag":455,"props":7811,"children":7812},{"style":486},[7813],{"type":26,"value":7814},"        properties: {\n",{"type":21,"tag":455,"props":7816,"children":7817},{"class":457,"line":1564},[7818,7823,7828,7833,7838],{"type":21,"tag":455,"props":7819,"children":7820},{"style":486},[7821],{"type":26,"value":7822},"          limit: {type: ",{"type":21,"tag":455,"props":7824,"children":7825},{"style":492},[7826],{"type":26,"value":7827},"\"number\"",{"type":21,"tag":455,"props":7829,"children":7830},{"style":486},[7831],{"type":26,"value":7832},", minimum: ",{"type":21,"tag":455,"props":7834,"children":7835},{"style":480},[7836],{"type":26,"value":7837},"1",{"type":21,"tag":455,"props":7839,"children":7840},{"style":486},[7841],{"type":26,"value":7842},"},\n",{"type":21,"tag":455,"props":7844,"children":7845},{"class":457,"line":1605},[7846,7851,7855,7859,7864],{"type":21,"tag":455,"props":7847,"children":7848},{"style":486},[7849],{"type":26,"value":7850},"          offset: {type: ",{"type":21,"tag":455,"props":7852,"children":7853},{"style":492},[7854],{"type":26,"value":7827},{"type":21,"tag":455,"props":7856,"children":7857},{"style":486},[7858],{"type":26,"value":7832},{"type":21,"tag":455,"props":7860,"children":7861},{"style":480},[7862],{"type":26,"value":7863},"0",{"type":21,"tag":455,"props":7865,"children":7866},{"style":486},[7867],{"type":26,"value":7842},{"type":21,"tag":455,"props":7869,"children":7870},{"class":457,"line":1622},[7871,7876,7881,7886,7890],{"type":21,"tag":455,"props":7872,"children":7873},{"style":486},[7874],{"type":26,"value":7875},"          tag: {type: ",{"type":21,"tag":455,"props":7877,"children":7878},{"style":492},[7879],{"type":26,"value":7880},"\"string\"",{"type":21,"tag":455,"props":7882,"children":7883},{"style":486},[7884],{"type":26,"value":7885},", minLength: ",{"type":21,"tag":455,"props":7887,"children":7888},{"style":480},[7889],{"type":26,"value":7837},{"type":21,"tag":455,"props":7891,"children":7892},{"style":486},[7893],{"type":26,"value":7842},{"type":21,"tag":455,"props":7895,"children":7896},{"class":457,"line":1663},[7897,7902,7906,7910,7914],{"type":21,"tag":455,"props":7898,"children":7899},{"style":486},[7900],{"type":26,"value":7901},"          author: {type: ",{"type":21,"tag":455,"props":7903,"children":7904},{"style":492},[7905],{"type":26,"value":7880},{"type":21,"tag":455,"props":7907,"children":7908},{"style":486},[7909],{"type":26,"value":7885},{"type":21,"tag":455,"props":7911,"children":7912},{"style":480},[7913],{"type":26,"value":7837},{"type":21,"tag":455,"props":7915,"children":7916},{"style":486},[7917],{"type":26,"value":7842},{"type":21,"tag":455,"props":7919,"children":7920},{"class":457,"line":1680},[7921,7926,7930,7934,7938],{"type":21,"tag":455,"props":7922,"children":7923},{"style":486},[7924],{"type":26,"value":7925},"          favorited: {type: ",{"type":21,"tag":455,"props":7927,"children":7928},{"style":492},[7929],{"type":26,"value":7880},{"type":21,"tag":455,"props":7931,"children":7932},{"style":486},[7933],{"type":26,"value":7885},{"type":21,"tag":455,"props":7935,"children":7936},{"style":480},[7937],{"type":26,"value":7837},{"type":21,"tag":455,"props":7939,"children":7940},{"style":486},[7941],{"type":26,"value":7842},{"type":21,"tag":455,"props":7943,"children":7944},{"class":457,"line":1721},[7945],{"type":21,"tag":455,"props":7946,"children":7947},{"style":486},[7948],{"type":26,"value":7949},"        },\n",{"type":21,"tag":455,"props":7951,"children":7952},{"class":457,"line":1738},[7953],{"type":21,"tag":455,"props":7954,"children":7955},{"style":486},[7956],{"type":26,"value":7957},"        required: [],\n",{"type":21,"tag":455,"props":7959,"children":7960},{"class":457,"line":1779},[7961],{"type":21,"tag":455,"props":7962,"children":7963},{"style":486},[7964],{"type":26,"value":5846},{"type":21,"tag":455,"props":7966,"children":7967},{"class":457,"line":1796},[7968],{"type":21,"tag":455,"props":7969,"children":7970},{"style":486},[7971],{"type":26,"value":5854},{"type":21,"tag":455,"props":7973,"children":7974},{"class":457,"line":1837},[7975,7979,7983,7987,7991,7995,7999,8003,8007],{"type":21,"tag":455,"props":7976,"children":7977},{"style":1365},[7978],{"type":26,"value":7124},{"type":21,"tag":455,"props":7980,"children":7981},{"style":486},[7982],{"type":26,"value":7129},{"type":21,"tag":455,"props":7984,"children":7985},{"style":1349},[7986],{"type":26,"value":4063},{"type":21,"tag":455,"props":7988,"children":7989},{"style":1349},[7990],{"type":26,"value":4068},{"type":21,"tag":455,"props":7992,"children":7993},{"style":486},[7994],{"type":26,"value":489},{"type":21,"tag":455,"props":7996,"children":7997},{"style":4527},[7998],{"type":26,"value":5567},{"type":21,"tag":455,"props":8000,"children":8001},{"style":486},[8002],{"type":26,"value":2228},{"type":21,"tag":455,"props":8004,"children":8005},{"style":4527},[8006],{"type":26,"value":5923},{"type":21,"tag":455,"props":8008,"children":8009},{"style":486},[8010],{"type":26,"value":4879},{"type":21,"tag":455,"props":8012,"children":8013},{"class":457,"line":1854},[8014],{"type":21,"tag":455,"props":8015,"children":8016},{"style":462},[8017],{"type":26,"value":7165},{"type":21,"tag":455,"props":8019,"children":8020},{"class":457,"line":1895},[8021],{"type":21,"tag":455,"props":8022,"children":8023},{"style":486},[8024],{"type":26,"value":5854},{"type":21,"tag":455,"props":8026,"children":8027},{"class":457,"line":1912},[8028],{"type":21,"tag":455,"props":8029,"children":8030},{"style":486},[8031],{"type":26,"value":4389},{"type":21,"tag":455,"props":8033,"children":8034},{"class":457,"line":1953},[8035],{"type":21,"tag":455,"props":8036,"children":8037},{"emptyLinePlaceholder":471},[8038],{"type":26,"value":474},{"type":21,"tag":455,"props":8040,"children":8041},{"class":457,"line":1970},[8042],{"type":21,"tag":455,"props":8043,"children":8044},{"style":462},[8045],{"type":26,"value":7000},{"type":21,"tag":22,"props":8047,"children":8048},{},[8049,8051,8056,8058,8063,8065,8071],{"type":26,"value":8050},"Otherwise, the body of this route can be migrated largely unchanged, except with the ",{"type":21,"tag":246,"props":8052,"children":8054},{"className":8053},[],[8055],{"type":26,"value":6970},{"type":26,"value":8057}," variable renamed to ",{"type":21,"tag":246,"props":8059,"children":8061},{"className":8060},[],[8062],{"type":26,"value":5567},{"type":26,"value":8064}," (a purely stylistic choice but one which seems to be the convention in Fastify). The only other difference is where the response is returned; in Express ",{"type":21,"tag":246,"props":8066,"children":8068},{"className":8067},[],[8069],{"type":26,"value":8070},"res.json()",{"type":26,"value":8072}," is called, but Fastify is JSON-enabled by default and so a JavaScript object can just be returned directly.",{"type":21,"tag":22,"props":8074,"children":8075},{},[8076,8078,8084,8086,8092,8094,8100,8102,8108],{"type":26,"value":8077},"The rest of the routes in this module can be migrated similarly, with one exception: some rely on an Express utility called ",{"type":21,"tag":246,"props":8079,"children":8081},{"className":8080},[],[8082],{"type":26,"value":8083},".param()",{"type":26,"value":8085},", for which Fastify doesn't have a direct equivalent. This utility is used in order to automatically fetch an ",{"type":21,"tag":246,"props":8087,"children":8089},{"className":8088},[],[8090],{"type":26,"value":8091},"Article",{"type":26,"value":8093}," object from the database for any route which has a ",{"type":21,"tag":246,"props":8095,"children":8097},{"className":8096},[],[8098],{"type":26,"value":8099},":article",{"type":26,"value":8101}," query parameter, and make it available to the route handler as ",{"type":21,"tag":246,"props":8103,"children":8105},{"className":8104},[],[8106],{"type":26,"value":8107},"req.article",{"type":26,"value":831},{"type":21,"tag":22,"props":8110,"children":8111},{},[8112,8117],{"type":21,"tag":246,"props":8113,"children":8115},{"className":8114},[],[8116],{"type":26,"value":6908},{"type":26,"value":831},{"type":21,"tag":239,"props":8119,"children":8121},{"className":3170,"code":8120,"language":2804,"meta":8,"style":8},"router.param('article', function(req, res, next, slug) {\n  Article.findOne({ slug: slug})\n    .populate('author')\n    .then(function (article) {\n      if (!article) { return res.sendStatus(404); }\n\n      req.article = article;\n\n      return next();\n    }).catch(next);\n});\n",[8122],{"type":21,"tag":246,"props":8123,"children":8124},{"__ignoreMap":8},[8125,8191,8208,8234,8266,8315,8322,8339,8346,8363,8380],{"type":21,"tag":455,"props":8126,"children":8127},{"class":457,"line":458},[8128,8132,8137,8141,8146,8150,8154,8158,8162,8166,8170,8174,8178,8182,8187],{"type":21,"tag":455,"props":8129,"children":8130},{"style":486},[8131],{"type":26,"value":6938},{"type":21,"tag":455,"props":8133,"children":8134},{"style":1365},[8135],{"type":26,"value":8136},"param",{"type":21,"tag":455,"props":8138,"children":8139},{"style":486},[8140],{"type":26,"value":489},{"type":21,"tag":455,"props":8142,"children":8143},{"style":492},[8144],{"type":26,"value":8145},"'article'",{"type":21,"tag":455,"props":8147,"children":8148},{"style":486},[8149],{"type":26,"value":2228},{"type":21,"tag":455,"props":8151,"children":8152},{"style":1349},[8153],{"type":26,"value":3897},{"type":21,"tag":455,"props":8155,"children":8156},{"style":486},[8157],{"type":26,"value":489},{"type":21,"tag":455,"props":8159,"children":8160},{"style":4527},[8161],{"type":26,"value":6970},{"type":21,"tag":455,"props":8163,"children":8164},{"style":486},[8165],{"type":26,"value":2228},{"type":21,"tag":455,"props":8167,"children":8168},{"style":4527},[8169],{"type":26,"value":6979},{"type":21,"tag":455,"props":8171,"children":8172},{"style":486},[8173],{"type":26,"value":2228},{"type":21,"tag":455,"props":8175,"children":8176},{"style":4527},[8177],{"type":26,"value":6988},{"type":21,"tag":455,"props":8179,"children":8180},{"style":486},[8181],{"type":26,"value":2228},{"type":21,"tag":455,"props":8183,"children":8184},{"style":4527},[8185],{"type":26,"value":8186},"slug",{"type":21,"tag":455,"props":8188,"children":8189},{"style":486},[8190],{"type":26,"value":4879},{"type":21,"tag":455,"props":8192,"children":8193},{"class":457,"line":336},[8194,8199,8203],{"type":21,"tag":455,"props":8195,"children":8196},{"style":486},[8197],{"type":26,"value":8198},"  Article.",{"type":21,"tag":455,"props":8200,"children":8201},{"style":1365},[8202],{"type":26,"value":7576},{"type":21,"tag":455,"props":8204,"children":8205},{"style":486},[8206],{"type":26,"value":8207},"({ slug: slug})\n",{"type":21,"tag":455,"props":8209,"children":8210},{"class":457,"line":333},[8211,8216,8221,8225,8230],{"type":21,"tag":455,"props":8212,"children":8213},{"style":486},[8214],{"type":26,"value":8215},"    .",{"type":21,"tag":455,"props":8217,"children":8218},{"style":1365},[8219],{"type":26,"value":8220},"populate",{"type":21,"tag":455,"props":8222,"children":8223},{"style":486},[8224],{"type":26,"value":489},{"type":21,"tag":455,"props":8226,"children":8227},{"style":492},[8228],{"type":26,"value":8229},"'author'",{"type":21,"tag":455,"props":8231,"children":8232},{"style":486},[8233],{"type":26,"value":500},{"type":21,"tag":455,"props":8235,"children":8236},{"class":457,"line":1424},[8237,8241,8245,8249,8253,8257,8262],{"type":21,"tag":455,"props":8238,"children":8239},{"style":486},[8240],{"type":26,"value":8215},{"type":21,"tag":455,"props":8242,"children":8243},{"style":1365},[8244],{"type":26,"value":4520},{"type":21,"tag":455,"props":8246,"children":8247},{"style":486},[8248],{"type":26,"value":489},{"type":21,"tag":455,"props":8250,"children":8251},{"style":1349},[8252],{"type":26,"value":3897},{"type":21,"tag":455,"props":8254,"children":8255},{"style":486},[8256],{"type":26,"value":2136},{"type":21,"tag":455,"props":8258,"children":8259},{"style":4527},[8260],{"type":26,"value":8261},"article",{"type":21,"tag":455,"props":8263,"children":8264},{"style":486},[8265],{"type":26,"value":4879},{"type":21,"tag":455,"props":8267,"children":8268},{"class":457,"line":1443},[8269,8274,8278,8282,8287,8291,8296,8301,8305,8310],{"type":21,"tag":455,"props":8270,"children":8271},{"style":1349},[8272],{"type":26,"value":8273},"      if",{"type":21,"tag":455,"props":8275,"children":8276},{"style":486},[8277],{"type":26,"value":2136},{"type":21,"tag":455,"props":8279,"children":8280},{"style":1349},[8281],{"type":26,"value":1272},{"type":21,"tag":455,"props":8283,"children":8284},{"style":486},[8285],{"type":26,"value":8286},"article) { ",{"type":21,"tag":455,"props":8288,"children":8289},{"style":1349},[8290],{"type":26,"value":260},{"type":21,"tag":455,"props":8292,"children":8293},{"style":486},[8294],{"type":26,"value":8295}," res.",{"type":21,"tag":455,"props":8297,"children":8298},{"style":1365},[8299],{"type":26,"value":8300},"sendStatus",{"type":21,"tag":455,"props":8302,"children":8303},{"style":486},[8304],{"type":26,"value":489},{"type":21,"tag":455,"props":8306,"children":8307},{"style":480},[8308],{"type":26,"value":8309},"404",{"type":21,"tag":455,"props":8311,"children":8312},{"style":486},[8313],{"type":26,"value":8314},"); }\n",{"type":21,"tag":455,"props":8316,"children":8317},{"class":457,"line":1489},[8318],{"type":21,"tag":455,"props":8319,"children":8320},{"emptyLinePlaceholder":471},[8321],{"type":26,"value":474},{"type":21,"tag":455,"props":8323,"children":8324},{"class":457,"line":1506},[8325,8330,8334],{"type":21,"tag":455,"props":8326,"children":8327},{"style":486},[8328],{"type":26,"value":8329},"      req.article ",{"type":21,"tag":455,"props":8331,"children":8332},{"style":1349},[8333],{"type":26,"value":3193},{"type":21,"tag":455,"props":8335,"children":8336},{"style":486},[8337],{"type":26,"value":8338}," article;\n",{"type":21,"tag":455,"props":8340,"children":8341},{"class":457,"line":1547},[8342],{"type":21,"tag":455,"props":8343,"children":8344},{"emptyLinePlaceholder":471},[8345],{"type":26,"value":474},{"type":21,"tag":455,"props":8347,"children":8348},{"class":457,"line":1564},[8349,8354,8359],{"type":21,"tag":455,"props":8350,"children":8351},{"style":1349},[8352],{"type":26,"value":8353},"      return",{"type":21,"tag":455,"props":8355,"children":8356},{"style":1365},[8357],{"type":26,"value":8358}," next",{"type":21,"tag":455,"props":8360,"children":8361},{"style":486},[8362],{"type":26,"value":3398},{"type":21,"tag":455,"props":8364,"children":8365},{"class":457,"line":1605},[8366,8371,8375],{"type":21,"tag":455,"props":8367,"children":8368},{"style":486},[8369],{"type":26,"value":8370},"    }).",{"type":21,"tag":455,"props":8372,"children":8373},{"style":1365},[8374],{"type":26,"value":4638},{"type":21,"tag":455,"props":8376,"children":8377},{"style":486},[8378],{"type":26,"value":8379},"(next);\n",{"type":21,"tag":455,"props":8381,"children":8382},{"class":457,"line":1622},[8383],{"type":21,"tag":455,"props":8384,"children":8385},{"style":486},[8386],{"type":26,"value":3952},{"type":21,"tag":22,"props":8388,"children":8389},{},[8390],{"type":26,"value":8391},"It's possible to do the same in Fastify using the more generic \"hooks\" system:",{"type":21,"tag":239,"props":8393,"children":8395},{"className":3170,"code":8394,"language":2804,"meta":8,"style":8},"fastify.addHook('onRequest', async (request, reply) => {\n  if ('article' in request.params) {\n    request.article = await Article\n      .findOne({ slug: request.params.article})\n      .populate('author');\n\n    if (!request.article) {\n        reply.code(401).send();\n        return reply;\n    }\n  }\n});\n",[8396],{"type":21,"tag":246,"props":8397,"children":8398},{"__ignoreMap":8},[8399,8457,8482,8504,8521,8544,8551,8571,8604,8616,8623,8630],{"type":21,"tag":455,"props":8400,"children":8401},{"class":457,"line":458},[8402,8407,8412,8416,8421,8425,8429,8433,8437,8441,8445,8449,8453],{"type":21,"tag":455,"props":8403,"children":8404},{"style":486},[8405],{"type":26,"value":8406},"fastify.",{"type":21,"tag":455,"props":8408,"children":8409},{"style":1365},[8410],{"type":26,"value":8411},"addHook",{"type":21,"tag":455,"props":8413,"children":8414},{"style":486},[8415],{"type":26,"value":489},{"type":21,"tag":455,"props":8417,"children":8418},{"style":492},[8419],{"type":26,"value":8420},"'onRequest'",{"type":21,"tag":455,"props":8422,"children":8423},{"style":486},[8424],{"type":26,"value":2228},{"type":21,"tag":455,"props":8426,"children":8427},{"style":1349},[8428],{"type":26,"value":4063},{"type":21,"tag":455,"props":8430,"children":8431},{"style":486},[8432],{"type":26,"value":2136},{"type":21,"tag":455,"props":8434,"children":8435},{"style":4527},[8436],{"type":26,"value":5567},{"type":21,"tag":455,"props":8438,"children":8439},{"style":486},[8440],{"type":26,"value":2228},{"type":21,"tag":455,"props":8442,"children":8443},{"style":4527},[8444],{"type":26,"value":5923},{"type":21,"tag":455,"props":8446,"children":8447},{"style":486},[8448],{"type":26,"value":4584},{"type":21,"tag":455,"props":8450,"children":8451},{"style":1349},[8452],{"type":26,"value":4589},{"type":21,"tag":455,"props":8454,"children":8455},{"style":486},[8456],{"type":26,"value":2470},{"type":21,"tag":455,"props":8458,"children":8459},{"class":457,"line":336},[8460,8464,8468,8472,8477],{"type":21,"tag":455,"props":8461,"children":8462},{"style":1349},[8463],{"type":26,"value":4126},{"type":21,"tag":455,"props":8465,"children":8466},{"style":486},[8467],{"type":26,"value":2136},{"type":21,"tag":455,"props":8469,"children":8470},{"style":492},[8471],{"type":26,"value":8145},{"type":21,"tag":455,"props":8473,"children":8474},{"style":1349},[8475],{"type":26,"value":8476}," in",{"type":21,"tag":455,"props":8478,"children":8479},{"style":486},[8480],{"type":26,"value":8481}," request.params) {\n",{"type":21,"tag":455,"props":8483,"children":8484},{"class":457,"line":333},[8485,8490,8494,8499],{"type":21,"tag":455,"props":8486,"children":8487},{"style":486},[8488],{"type":26,"value":8489},"    request.article ",{"type":21,"tag":455,"props":8491,"children":8492},{"style":1349},[8493],{"type":26,"value":3193},{"type":21,"tag":455,"props":8495,"children":8496},{"style":1349},[8497],{"type":26,"value":8498}," await",{"type":21,"tag":455,"props":8500,"children":8501},{"style":486},[8502],{"type":26,"value":8503}," Article\n",{"type":21,"tag":455,"props":8505,"children":8506},{"class":457,"line":1424},[8507,8512,8516],{"type":21,"tag":455,"props":8508,"children":8509},{"style":486},[8510],{"type":26,"value":8511},"      .",{"type":21,"tag":455,"props":8513,"children":8514},{"style":1365},[8515],{"type":26,"value":7576},{"type":21,"tag":455,"props":8517,"children":8518},{"style":486},[8519],{"type":26,"value":8520},"({ slug: request.params.article})\n",{"type":21,"tag":455,"props":8522,"children":8523},{"class":457,"line":1443},[8524,8528,8532,8536,8540],{"type":21,"tag":455,"props":8525,"children":8526},{"style":486},[8527],{"type":26,"value":8511},{"type":21,"tag":455,"props":8529,"children":8530},{"style":1365},[8531],{"type":26,"value":8220},{"type":21,"tag":455,"props":8533,"children":8534},{"style":486},[8535],{"type":26,"value":489},{"type":21,"tag":455,"props":8537,"children":8538},{"style":492},[8539],{"type":26,"value":8229},{"type":21,"tag":455,"props":8541,"children":8542},{"style":486},[8543],{"type":26,"value":2171},{"type":21,"tag":455,"props":8545,"children":8546},{"class":457,"line":1489},[8547],{"type":21,"tag":455,"props":8548,"children":8549},{"emptyLinePlaceholder":471},[8550],{"type":26,"value":474},{"type":21,"tag":455,"props":8552,"children":8553},{"class":457,"line":1506},[8554,8558,8562,8566],{"type":21,"tag":455,"props":8555,"children":8556},{"style":1349},[8557],{"type":26,"value":1402},{"type":21,"tag":455,"props":8559,"children":8560},{"style":486},[8561],{"type":26,"value":2136},{"type":21,"tag":455,"props":8563,"children":8564},{"style":1349},[8565],{"type":26,"value":1272},{"type":21,"tag":455,"props":8567,"children":8568},{"style":486},[8569],{"type":26,"value":8570},"request.article) {\n",{"type":21,"tag":455,"props":8572,"children":8573},{"class":457,"line":1547},[8574,8579,8583,8587,8592,8596,8600],{"type":21,"tag":455,"props":8575,"children":8576},{"style":486},[8577],{"type":26,"value":8578},"        reply.",{"type":21,"tag":455,"props":8580,"children":8581},{"style":1365},[8582],{"type":26,"value":246},{"type":21,"tag":455,"props":8584,"children":8585},{"style":486},[8586],{"type":26,"value":489},{"type":21,"tag":455,"props":8588,"children":8589},{"style":480},[8590],{"type":26,"value":8591},"401",{"type":21,"tag":455,"props":8593,"children":8594},{"style":486},[8595],{"type":26,"value":5081},{"type":21,"tag":455,"props":8597,"children":8598},{"style":1365},[8599],{"type":26,"value":5994},{"type":21,"tag":455,"props":8601,"children":8602},{"style":486},[8603],{"type":26,"value":3398},{"type":21,"tag":455,"props":8605,"children":8606},{"class":457,"line":1564},[8607,8611],{"type":21,"tag":455,"props":8608,"children":8609},{"style":1349},[8610],{"type":26,"value":1430},{"type":21,"tag":455,"props":8612,"children":8613},{"style":486},[8614],{"type":26,"value":8615}," reply;\n",{"type":21,"tag":455,"props":8617,"children":8618},{"class":457,"line":1605},[8619],{"type":21,"tag":455,"props":8620,"children":8621},{"style":486},[8622],{"type":26,"value":6008},{"type":21,"tag":455,"props":8624,"children":8625},{"class":457,"line":1622},[8626],{"type":21,"tag":455,"props":8627,"children":8628},{"style":486},[8629],{"type":26,"value":4232},{"type":21,"tag":455,"props":8631,"children":8632},{"class":457,"line":1663},[8633],{"type":21,"tag":455,"props":8634,"children":8635},{"style":486},[8636],{"type":26,"value":3952},{"type":21,"tag":22,"props":8638,"children":8639},{},[8640],{"type":26,"value":8641},"Note that for style reasons however, I opted to remove hooks like these and perform these database requests within the route handlers themselves; this makes the code more direct and easy to reason about, and also allows us to query for the Article in parallel with other database queries.",{"type":21,"tag":22,"props":8643,"children":8644},{},[8645,8646,8652],{"type":26,"value":5235},{"type":21,"tag":322,"props":8647,"children":8650},{"href":8648,"rel":8649},"https://github.com/artandlogic/node-express-to-fastify-example/commit/de495b65e32db961d84db8c78fd96621deac8404",[326],[8651],{"type":26,"value":2869},{"type":26,"value":8653}," to view the rest of the partial conversion to Fastify. This process can continue piece by piece until the last of the Express routes are migrated.",{"type":21,"tag":56,"props":8655,"children":8657},{"id":8656},"step-3-removing-express-and-related-plugins",[8658],{"type":26,"value":8659},"Step 3: Removing Express and related plugins",{"type":21,"tag":22,"props":8661,"children":8662},{},[8663,8665,8671,8672,8677,8679,8685,8687,8693,8695,8700],{"type":26,"value":8664},"Once the migration is complete, the registration of the ",{"type":21,"tag":246,"props":8666,"children":8668},{"className":8667},[],[8669],{"type":26,"value":8670},"routes/legacy",{"type":26,"value":6353},{"type":21,"tag":246,"props":8673,"children":8675},{"className":8674},[],[8676],{"type":26,"value":3045},{"type":26,"value":8678}," can be removed, the modules in the ",{"type":21,"tag":246,"props":8680,"children":8682},{"className":8681},[],[8683],{"type":26,"value":8684},"legacy/",{"type":26,"value":8686}," directory can be deleted, and all Express-related plugins can be removed from ",{"type":21,"tag":246,"props":8688,"children":8690},{"className":8689},[],[8691],{"type":26,"value":8692},"package.json",{"type":26,"value":8694}," (including ",{"type":21,"tag":246,"props":8696,"children":8698},{"className":8697},[],[8699],{"type":26,"value":2893},{"type":26,"value":8701},"). What's left is an application that has been fully migrated to Fastify, while remaining fully functional at every step along the way.",{"type":21,"tag":22,"props":8703,"children":8704},{},[8705,8706,8712],{"type":26,"value":5235},{"type":21,"tag":322,"props":8707,"children":8710},{"href":8708,"rel":8709},"https://github.com/artandlogic/node-express-to-fastify-example/commit/25da1c0e9b0a3b7d9c6e8b102fd4112796634fdf",[326],[8711],{"type":26,"value":2869},{"type":26,"value":8713}," to view the fully-migrated codebase.",{"type":21,"tag":56,"props":8715,"children":8717},{"id":8716},"bonus-adding-integration-tests",[8718],{"type":26,"value":8719},"Bonus: Adding integration tests",{"type":21,"tag":22,"props":8721,"children":8722},{},[8723],{"type":26,"value":8724},"This project currently relies on end-to-end tests that execute against a full running instance of the application. It's valuable to have some tests like these, but most of them would be better off as integration tests, such that test cases are independent of one another and they have the ability to set up data before they run.",{"type":21,"tag":22,"props":8726,"children":8727},{},[8728],{"type":26,"value":8729},"To use Fastify's built-in testing utilities to this end, a few minor things in this project need to be refactored:",{"type":21,"tag":183,"props":8731,"children":8732},{},[8733,9079],{"type":21,"tag":187,"props":8734,"children":8735},{},[8736,8738,8743,8745,8750,8752,8755,8760,8762,9015,9018,9020,9025,9027,9032,9034,9040,9042,9047,9049,9054,9056,9062,9064,9070,9072,9077],{"type":26,"value":8737},"The ",{"type":21,"tag":246,"props":8739,"children":8741},{"className":8740},[],[8742],{"type":26,"value":2819},{"type":26,"value":8744}," instance creation needs to be sparated from the place where the it's used to start up the server itself; this is because the test utilities work on the ",{"type":21,"tag":246,"props":8746,"children":8748},{"className":8747},[],[8749],{"type":26,"value":2819},{"type":26,"value":8751}," app itself without starting up a real Webserver.",{"type":21,"tag":648,"props":8753,"children":8754},{},[],{"type":21,"tag":246,"props":8756,"children":8758},{"className":8757},[],[8759],{"type":26,"value":3045},{"type":26,"value":8761}," is currently doing both:",{"type":21,"tag":239,"props":8763,"children":8765},{"className":3170,"code":8764,"language":2804,"meta":8,"style":8},"async function build() {\n  /* ... */\n\n  const app = require('fastify')({logger: true});\n\n  /* ... */\n\n  return app;\n}\n\nbuild()\n  .then(app => app.listen({port: 3000}))\n  .then((address) => {\n    console.log('Listening on ' + address);\n  }).catch(console.log);\n",[8766],{"type":21,"tag":246,"props":8767,"children":8768},{"__ignoreMap":8},[8769,8788,8795,8802,8842,8849,8856,8863,8874,8881,8888,8899,8942,8973,9000],{"type":21,"tag":455,"props":8770,"children":8771},{"class":457,"line":458},[8772,8776,8780,8784],{"type":21,"tag":455,"props":8773,"children":8774},{"style":1349},[8775],{"type":26,"value":4063},{"type":21,"tag":455,"props":8777,"children":8778},{"style":1349},[8779],{"type":26,"value":4068},{"type":21,"tag":455,"props":8781,"children":8782},{"style":1365},[8783],{"type":26,"value":4073},{"type":21,"tag":455,"props":8785,"children":8786},{"style":486},[8787],{"type":26,"value":4078},{"type":21,"tag":455,"props":8789,"children":8790},{"class":457,"line":336},[8791],{"type":21,"tag":455,"props":8792,"children":8793},{"style":462},[8794],{"type":26,"value":7000},{"type":21,"tag":455,"props":8796,"children":8797},{"class":457,"line":333},[8798],{"type":21,"tag":455,"props":8799,"children":8800},{"emptyLinePlaceholder":471},[8801],{"type":26,"value":474},{"type":21,"tag":455,"props":8803,"children":8804},{"class":457,"line":1424},[8805,8809,8813,8817,8821,8825,8829,8834,8838],{"type":21,"tag":455,"props":8806,"children":8807},{"style":1349},[8808],{"type":26,"value":4086},{"type":21,"tag":455,"props":8810,"children":8811},{"style":480},[8812],{"type":26,"value":4343},{"type":21,"tag":455,"props":8814,"children":8815},{"style":1349},[8816],{"type":26,"value":2131},{"type":21,"tag":455,"props":8818,"children":8819},{"style":1365},[8820],{"type":26,"value":3198},{"type":21,"tag":455,"props":8822,"children":8823},{"style":486},[8824],{"type":26,"value":489},{"type":21,"tag":455,"props":8826,"children":8827},{"style":492},[8828],{"type":26,"value":4360},{"type":21,"tag":455,"props":8830,"children":8831},{"style":486},[8832],{"type":26,"value":8833},")({logger: ",{"type":21,"tag":455,"props":8835,"children":8836},{"style":480},[8837],{"type":26,"value":3679},{"type":21,"tag":455,"props":8839,"children":8840},{"style":486},[8841],{"type":26,"value":3952},{"type":21,"tag":455,"props":8843,"children":8844},{"class":457,"line":1443},[8845],{"type":21,"tag":455,"props":8846,"children":8847},{"emptyLinePlaceholder":471},[8848],{"type":26,"value":474},{"type":21,"tag":455,"props":8850,"children":8851},{"class":457,"line":1489},[8852],{"type":21,"tag":455,"props":8853,"children":8854},{"style":462},[8855],{"type":26,"value":7000},{"type":21,"tag":455,"props":8857,"children":8858},{"class":457,"line":1506},[8859],{"type":21,"tag":455,"props":8860,"children":8861},{"emptyLinePlaceholder":471},[8862],{"type":26,"value":474},{"type":21,"tag":455,"props":8864,"children":8865},{"class":457,"line":1547},[8866,8870],{"type":21,"tag":455,"props":8867,"children":8868},{"style":1349},[8869],{"type":26,"value":4472},{"type":21,"tag":455,"props":8871,"children":8872},{"style":486},[8873],{"type":26,"value":4477},{"type":21,"tag":455,"props":8875,"children":8876},{"class":457,"line":1564},[8877],{"type":21,"tag":455,"props":8878,"children":8879},{"style":486},[8880],{"type":26,"value":2002},{"type":21,"tag":455,"props":8882,"children":8883},{"class":457,"line":1605},[8884],{"type":21,"tag":455,"props":8885,"children":8886},{"emptyLinePlaceholder":471},[8887],{"type":26,"value":474},{"type":21,"tag":455,"props":8889,"children":8890},{"class":457,"line":1622},[8891,8895],{"type":21,"tag":455,"props":8892,"children":8893},{"style":1365},[8894],{"type":26,"value":4501},{"type":21,"tag":455,"props":8896,"children":8897},{"style":486},[8898],{"type":26,"value":4506},{"type":21,"tag":455,"props":8900,"children":8901},{"class":457,"line":1663},[8902,8906,8910,8914,8918,8922,8926,8930,8934,8938],{"type":21,"tag":455,"props":8903,"children":8904},{"style":486},[8905],{"type":26,"value":4515},{"type":21,"tag":455,"props":8907,"children":8908},{"style":1365},[8909],{"type":26,"value":4520},{"type":21,"tag":455,"props":8911,"children":8912},{"style":486},[8913],{"type":26,"value":489},{"type":21,"tag":455,"props":8915,"children":8916},{"style":4527},[8917],{"type":26,"value":4530},{"type":21,"tag":455,"props":8919,"children":8920},{"style":1349},[8921],{"type":26,"value":4535},{"type":21,"tag":455,"props":8923,"children":8924},{"style":486},[8925],{"type":26,"value":3863},{"type":21,"tag":455,"props":8927,"children":8928},{"style":1365},[8929],{"type":26,"value":3868},{"type":21,"tag":455,"props":8931,"children":8932},{"style":486},[8933],{"type":26,"value":4548},{"type":21,"tag":455,"props":8935,"children":8936},{"style":480},[8937],{"type":26,"value":4553},{"type":21,"tag":455,"props":8939,"children":8940},{"style":486},[8941],{"type":26,"value":4558},{"type":21,"tag":455,"props":8943,"children":8944},{"class":457,"line":1680},[8945,8949,8953,8957,8961,8965,8969],{"type":21,"tag":455,"props":8946,"children":8947},{"style":486},[8948],{"type":26,"value":4515},{"type":21,"tag":455,"props":8950,"children":8951},{"style":1365},[8952],{"type":26,"value":4520},{"type":21,"tag":455,"props":8954,"children":8955},{"style":486},[8956],{"type":26,"value":4575},{"type":21,"tag":455,"props":8958,"children":8959},{"style":4527},[8960],{"type":26,"value":3939},{"type":21,"tag":455,"props":8962,"children":8963},{"style":486},[8964],{"type":26,"value":4584},{"type":21,"tag":455,"props":8966,"children":8967},{"style":1349},[8968],{"type":26,"value":4589},{"type":21,"tag":455,"props":8970,"children":8971},{"style":486},[8972],{"type":26,"value":2470},{"type":21,"tag":455,"props":8974,"children":8975},{"class":457,"line":1721},[8976,8980,8984,8988,8992,8996],{"type":21,"tag":455,"props":8977,"children":8978},{"style":486},[8979],{"type":26,"value":4602},{"type":21,"tag":455,"props":8981,"children":8982},{"style":1365},[8983],{"type":26,"value":3915},{"type":21,"tag":455,"props":8985,"children":8986},{"style":486},[8987],{"type":26,"value":489},{"type":21,"tag":455,"props":8989,"children":8990},{"style":492},[8991],{"type":26,"value":4615},{"type":21,"tag":455,"props":8993,"children":8994},{"style":1349},[8995],{"type":26,"value":3929},{"type":21,"tag":455,"props":8997,"children":8998},{"style":486},[8999],{"type":26,"value":4624},{"type":21,"tag":455,"props":9001,"children":9002},{"class":457,"line":1738},[9003,9007,9011],{"type":21,"tag":455,"props":9004,"children":9005},{"style":486},[9006],{"type":26,"value":4633},{"type":21,"tag":455,"props":9008,"children":9009},{"style":1365},[9010],{"type":26,"value":4638},{"type":21,"tag":455,"props":9012,"children":9013},{"style":486},[9014],{"type":26,"value":4643},{"type":21,"tag":648,"props":9016,"children":9017},{},[],{"type":26,"value":9019},"A solution is to have ",{"type":21,"tag":246,"props":9021,"children":9023},{"className":9022},[],[9024],{"type":26,"value":3045},{"type":26,"value":9026}," export its ",{"type":21,"tag":246,"props":9028,"children":9030},{"className":9029},[],[9031],{"type":26,"value":3996},{"type":26,"value":9033}," function, and then create a separate ",{"type":21,"tag":246,"props":9035,"children":9037},{"className":9036},[],[9038],{"type":26,"value":9039},"server.js",{"type":26,"value":9041}," file to move the ",{"type":21,"tag":246,"props":9043,"children":9045},{"className":9044},[],[9046],{"type":26,"value":3996},{"type":26,"value":9048}," invocation into (updating the ",{"type":21,"tag":246,"props":9050,"children":9052},{"className":9051},[],[9053],{"type":26,"value":8692},{"type":26,"value":9055}," scripts so that they invoke ",{"type":21,"tag":246,"props":9057,"children":9059},{"className":9058},[],[9060],{"type":26,"value":9061},"node ./server.js",{"type":26,"value":9063}," instead of ",{"type":21,"tag":246,"props":9065,"children":9067},{"className":9066},[],[9068],{"type":26,"value":9069},"node ./app.js",{"type":26,"value":9071},"). The behaviour is the same, but now the ",{"type":21,"tag":246,"props":9073,"children":9075},{"className":9074},[],[9076],{"type":26,"value":2819},{"type":26,"value":9078}," app can be imported in tests.",{"type":21,"tag":187,"props":9080,"children":9081},{},[9082,9084,9095,9097],{"type":26,"value":9083},"In in-memory MongoDB database needs to be configured, so that the integration tests can create one. This can be done easily with the ",{"type":21,"tag":322,"props":9085,"children":9088},{"href":9086,"rel":9087},"https://www.npmjs.com/package/mongodb-memory-server",[326],[9089],{"type":21,"tag":246,"props":9090,"children":9092},{"className":9091},[],[9093],{"type":26,"value":9094},"mongodb-memory-server",{"type":26,"value":9096}," package:",{"type":21,"tag":239,"props":9098,"children":9100},{"className":3170,"code":9099,"language":2804,"meta":8,"style":8},"const {MongoMemoryServer} = require('mongodb-memory-server');\n\nmongod = await MongoMemoryServer.create();\nprocess.env.MONGODB_URI = mongod.getUri();\n\n/* Do testing... */\n\nmongod.stop();\n",[9101],{"type":21,"tag":246,"props":9102,"children":9103},{"__ignoreMap":8},[9104,9145,9152,9182,9212,9219,9227,9234],{"type":21,"tag":455,"props":9105,"children":9106},{"class":457,"line":458},[9107,9111,9115,9120,9124,9128,9132,9136,9141],{"type":21,"tag":455,"props":9108,"children":9109},{"style":1349},[9110],{"type":26,"value":4671},{"type":21,"tag":455,"props":9112,"children":9113},{"style":486},[9114],{"type":26,"value":7508},{"type":21,"tag":455,"props":9116,"children":9117},{"style":480},[9118],{"type":26,"value":9119},"MongoMemoryServer",{"type":21,"tag":455,"props":9121,"children":9122},{"style":486},[9123],{"type":26,"value":3616},{"type":21,"tag":455,"props":9125,"children":9126},{"style":1349},[9127],{"type":26,"value":3193},{"type":21,"tag":455,"props":9129,"children":9130},{"style":1365},[9131],{"type":26,"value":3198},{"type":21,"tag":455,"props":9133,"children":9134},{"style":486},[9135],{"type":26,"value":489},{"type":21,"tag":455,"props":9137,"children":9138},{"style":492},[9139],{"type":26,"value":9140},"'mongodb-memory-server'",{"type":21,"tag":455,"props":9142,"children":9143},{"style":486},[9144],{"type":26,"value":2171},{"type":21,"tag":455,"props":9146,"children":9147},{"class":457,"line":336},[9148],{"type":21,"tag":455,"props":9149,"children":9150},{"emptyLinePlaceholder":471},[9151],{"type":26,"value":474},{"type":21,"tag":455,"props":9153,"children":9154},{"class":457,"line":333},[9155,9160,9164,9168,9173,9178],{"type":21,"tag":455,"props":9156,"children":9157},{"style":486},[9158],{"type":26,"value":9159},"mongod ",{"type":21,"tag":455,"props":9161,"children":9162},{"style":1349},[9163],{"type":26,"value":3193},{"type":21,"tag":455,"props":9165,"children":9166},{"style":1349},[9167],{"type":26,"value":8498},{"type":21,"tag":455,"props":9169,"children":9170},{"style":486},[9171],{"type":26,"value":9172}," MongoMemoryServer.",{"type":21,"tag":455,"props":9174,"children":9175},{"style":1365},[9176],{"type":26,"value":9177},"create",{"type":21,"tag":455,"props":9179,"children":9180},{"style":486},[9181],{"type":26,"value":3398},{"type":21,"tag":455,"props":9183,"children":9184},{"class":457,"line":1424},[9185,9190,9194,9198,9203,9208],{"type":21,"tag":455,"props":9186,"children":9187},{"style":486},[9188],{"type":26,"value":9189},"process.env.",{"type":21,"tag":455,"props":9191,"children":9192},{"style":480},[9193],{"type":26,"value":3604},{"type":21,"tag":455,"props":9195,"children":9196},{"style":1349},[9197],{"type":26,"value":2131},{"type":21,"tag":455,"props":9199,"children":9200},{"style":486},[9201],{"type":26,"value":9202}," mongod.",{"type":21,"tag":455,"props":9204,"children":9205},{"style":1365},[9206],{"type":26,"value":9207},"getUri",{"type":21,"tag":455,"props":9209,"children":9210},{"style":486},[9211],{"type":26,"value":3398},{"type":21,"tag":455,"props":9213,"children":9214},{"class":457,"line":1443},[9215],{"type":21,"tag":455,"props":9216,"children":9217},{"emptyLinePlaceholder":471},[9218],{"type":26,"value":474},{"type":21,"tag":455,"props":9220,"children":9221},{"class":457,"line":1489},[9222],{"type":21,"tag":455,"props":9223,"children":9224},{"style":462},[9225],{"type":26,"value":9226},"/* Do testing... */\n",{"type":21,"tag":455,"props":9228,"children":9229},{"class":457,"line":1506},[9230],{"type":21,"tag":455,"props":9231,"children":9232},{"emptyLinePlaceholder":471},[9233],{"type":26,"value":474},{"type":21,"tag":455,"props":9235,"children":9236},{"class":457,"line":1547},[9237,9242,9247],{"type":21,"tag":455,"props":9238,"children":9239},{"style":486},[9240],{"type":26,"value":9241},"mongod.",{"type":21,"tag":455,"props":9243,"children":9244},{"style":1365},[9245],{"type":26,"value":9246},"stop",{"type":21,"tag":455,"props":9248,"children":9249},{"style":486},[9250],{"type":26,"value":3398},{"type":21,"tag":22,"props":9252,"children":9253},{},[9254,9256,9263,9265,9271],{"type":26,"value":9255},"In this case, ",{"type":21,"tag":322,"props":9257,"children":9260},{"href":9258,"rel":9259},"https://node-tap.org/",[326],[9261],{"type":26,"value":9262},"Node-Tap",{"type":26,"value":9264}," was used as the test runner, but any test runner will do. An annotated example of one of these tests is shown below, and the full set of changes (including additional test case examples) can be found ",{"type":21,"tag":322,"props":9266,"children":9269},{"href":9267,"rel":9268},"https://github.com/artandlogic/node-express-to-fastify-example/commit/e2a377060c842b7137e3f8e43bcc92734cb55500",[326],[9270],{"type":26,"value":2869},{"type":26,"value":551},{"type":21,"tag":22,"props":9273,"children":9274},{},[9275,9281],{"type":21,"tag":246,"props":9276,"children":9278},{"className":9277},[],[9279],{"type":26,"value":9280},"tests/users.test.js",{"type":26,"value":831},{"type":21,"tag":239,"props":9283,"children":9285},{"className":3170,"code":9284,"language":2804,"meta":8,"style":8},"const {MongoMemoryServer} = require('mongodb-memory-server');\nconst {test} = require('tap');\n\nconst build = require('../app')\nconst {deleteAll} = require('../models');\nconst User = require('../models/User');\n\n// Add a test case for /users endpoints. In Node-Tap, test cases can be\n// nested arbitrarily.\ntest('/users', async t => {\n  let mongod, app;\n\n  t.before(async () => {\n    // Create a new in-memory MongoDB instance before the tests run,\n    // and build the Fastify application\n    mongod = await MongoMemoryServer.create();\n    process.env.NODE_ENV = 'test';\n    process.env.MONGODB_URI = mongod.getUri();\n    app = await build();\n  });\n  t.beforeEach(async t => {\n    // Before each test, call a utility which wipes all content from the\n    // database, so that test cases are fully independent.\n    await deleteAll();\n  });\n  t.teardown(async () => {\n    // Shut down the app and stop the database when tests complete.\n    await app.close();\n    await mongod.stop();\n  });\n\n  // Define a test for POST /users\n  await t.test('POST /users', async t => {\n    // Here is the Fastify inject() utility that's being leveraged for testing:\n    // A POST request is being simulated without a Webserver active, but it\n    // otherwise functions the same way that a true POST request would. The\n    // return value is a Promise that resolves to the Response object.\n    const response = await app.inject({\n      method: 'POST',\n      url: '/api/users',\n      headers: {\n        'Content-Type': 'application/json',\n        'X-Requested-With': '',\n      },\n      payload: {\n        user: {\n          email: 'john@jacob.com',\n          password: 'johnnyjacob',\n          username: 'johnjacob',\n        },\n      },\n    })\n\n    // With the Response in hand, whatever necessary test assertions can\n    // be made.\n    t.equal(response.statusCode, 200, 'returns a status code of 200');\n    const body = response.json();\n    t.hasProp(body, 'user', 'Response has \"user\" property\"');\n    t.hasProp(body.user, 'email', 'User has \"email\" property\"');\n    t.hasProp(body.user, 'username', 'User has \"username\" property\"');\n    t.hasProp(body.user, 'token', 'User has \"token\" property\"');\n  });\n});\n",[9286],{"type":21,"tag":246,"props":9287,"children":9288},{"__ignoreMap":8},[9289,9328,9368,9375,9407,9448,9481,9488,9496,9504,9541,9554,9561,9595,9603,9611,9639,9664,9691,9715,9722,9754,9762,9770,9787,9794,9826,9834,9854,9873,9880,9887,9895,9940,9948,9956,9964,9972,10006,10023,10040,10048,10069,10090,10098,10107,10116,10134,10152,10170,10178,10186,10195,10203,10212,10221,10258,10288,10324,10359,10393,10427,10435],{"type":21,"tag":455,"props":9290,"children":9291},{"class":457,"line":458},[9292,9296,9300,9304,9308,9312,9316,9320,9324],{"type":21,"tag":455,"props":9293,"children":9294},{"style":1349},[9295],{"type":26,"value":4671},{"type":21,"tag":455,"props":9297,"children":9298},{"style":486},[9299],{"type":26,"value":7508},{"type":21,"tag":455,"props":9301,"children":9302},{"style":480},[9303],{"type":26,"value":9119},{"type":21,"tag":455,"props":9305,"children":9306},{"style":486},[9307],{"type":26,"value":3616},{"type":21,"tag":455,"props":9309,"children":9310},{"style":1349},[9311],{"type":26,"value":3193},{"type":21,"tag":455,"props":9313,"children":9314},{"style":1365},[9315],{"type":26,"value":3198},{"type":21,"tag":455,"props":9317,"children":9318},{"style":486},[9319],{"type":26,"value":489},{"type":21,"tag":455,"props":9321,"children":9322},{"style":492},[9323],{"type":26,"value":9140},{"type":21,"tag":455,"props":9325,"children":9326},{"style":486},[9327],{"type":26,"value":2171},{"type":21,"tag":455,"props":9329,"children":9330},{"class":457,"line":336},[9331,9335,9339,9343,9347,9351,9355,9359,9364],{"type":21,"tag":455,"props":9332,"children":9333},{"style":1349},[9334],{"type":26,"value":4671},{"type":21,"tag":455,"props":9336,"children":9337},{"style":486},[9338],{"type":26,"value":7508},{"type":21,"tag":455,"props":9340,"children":9341},{"style":480},[9342],{"type":26,"value":5788},{"type":21,"tag":455,"props":9344,"children":9345},{"style":486},[9346],{"type":26,"value":3616},{"type":21,"tag":455,"props":9348,"children":9349},{"style":1349},[9350],{"type":26,"value":3193},{"type":21,"tag":455,"props":9352,"children":9353},{"style":1365},[9354],{"type":26,"value":3198},{"type":21,"tag":455,"props":9356,"children":9357},{"style":486},[9358],{"type":26,"value":489},{"type":21,"tag":455,"props":9360,"children":9361},{"style":492},[9362],{"type":26,"value":9363},"'tap'",{"type":21,"tag":455,"props":9365,"children":9366},{"style":486},[9367],{"type":26,"value":2171},{"type":21,"tag":455,"props":9369,"children":9370},{"class":457,"line":333},[9371],{"type":21,"tag":455,"props":9372,"children":9373},{"emptyLinePlaceholder":471},[9374],{"type":26,"value":474},{"type":21,"tag":455,"props":9376,"children":9377},{"class":457,"line":1424},[9378,9382,9386,9390,9394,9398,9403],{"type":21,"tag":455,"props":9379,"children":9380},{"style":1349},[9381],{"type":26,"value":4671},{"type":21,"tag":455,"props":9383,"children":9384},{"style":480},[9385],{"type":26,"value":4073},{"type":21,"tag":455,"props":9387,"children":9388},{"style":1349},[9389],{"type":26,"value":2131},{"type":21,"tag":455,"props":9391,"children":9392},{"style":1365},[9393],{"type":26,"value":3198},{"type":21,"tag":455,"props":9395,"children":9396},{"style":486},[9397],{"type":26,"value":489},{"type":21,"tag":455,"props":9399,"children":9400},{"style":492},[9401],{"type":26,"value":9402},"'../app'",{"type":21,"tag":455,"props":9404,"children":9405},{"style":486},[9406],{"type":26,"value":500},{"type":21,"tag":455,"props":9408,"children":9409},{"class":457,"line":1443},[9410,9414,9418,9423,9427,9431,9435,9439,9444],{"type":21,"tag":455,"props":9411,"children":9412},{"style":1349},[9413],{"type":26,"value":4671},{"type":21,"tag":455,"props":9415,"children":9416},{"style":486},[9417],{"type":26,"value":7508},{"type":21,"tag":455,"props":9419,"children":9420},{"style":480},[9421],{"type":26,"value":9422},"deleteAll",{"type":21,"tag":455,"props":9424,"children":9425},{"style":486},[9426],{"type":26,"value":3616},{"type":21,"tag":455,"props":9428,"children":9429},{"style":1349},[9430],{"type":26,"value":3193},{"type":21,"tag":455,"props":9432,"children":9433},{"style":1365},[9434],{"type":26,"value":3198},{"type":21,"tag":455,"props":9436,"children":9437},{"style":486},[9438],{"type":26,"value":489},{"type":21,"tag":455,"props":9440,"children":9441},{"style":492},[9442],{"type":26,"value":9443},"'../models'",{"type":21,"tag":455,"props":9445,"children":9446},{"style":486},[9447],{"type":26,"value":2171},{"type":21,"tag":455,"props":9449,"children":9450},{"class":457,"line":1489},[9451,9455,9460,9464,9468,9472,9477],{"type":21,"tag":455,"props":9452,"children":9453},{"style":1349},[9454],{"type":26,"value":4671},{"type":21,"tag":455,"props":9456,"children":9457},{"style":480},[9458],{"type":26,"value":9459}," User",{"type":21,"tag":455,"props":9461,"children":9462},{"style":1349},[9463],{"type":26,"value":2131},{"type":21,"tag":455,"props":9465,"children":9466},{"style":1365},[9467],{"type":26,"value":3198},{"type":21,"tag":455,"props":9469,"children":9470},{"style":486},[9471],{"type":26,"value":489},{"type":21,"tag":455,"props":9473,"children":9474},{"style":492},[9475],{"type":26,"value":9476},"'../models/User'",{"type":21,"tag":455,"props":9478,"children":9479},{"style":486},[9480],{"type":26,"value":2171},{"type":21,"tag":455,"props":9482,"children":9483},{"class":457,"line":1506},[9484],{"type":21,"tag":455,"props":9485,"children":9486},{"emptyLinePlaceholder":471},[9487],{"type":26,"value":474},{"type":21,"tag":455,"props":9489,"children":9490},{"class":457,"line":1547},[9491],{"type":21,"tag":455,"props":9492,"children":9493},{"style":462},[9494],{"type":26,"value":9495},"// Add a test case for /users endpoints. In Node-Tap, test cases can be\n",{"type":21,"tag":455,"props":9497,"children":9498},{"class":457,"line":1564},[9499],{"type":21,"tag":455,"props":9500,"children":9501},{"style":462},[9502],{"type":26,"value":9503},"// nested arbitrarily.\n",{"type":21,"tag":455,"props":9505,"children":9506},{"class":457,"line":1605},[9507,9511,9515,9520,9524,9528,9533,9537],{"type":21,"tag":455,"props":9508,"children":9509},{"style":1365},[9510],{"type":26,"value":5788},{"type":21,"tag":455,"props":9512,"children":9513},{"style":486},[9514],{"type":26,"value":489},{"type":21,"tag":455,"props":9516,"children":9517},{"style":492},[9518],{"type":26,"value":9519},"'/users'",{"type":21,"tag":455,"props":9521,"children":9522},{"style":486},[9523],{"type":26,"value":2228},{"type":21,"tag":455,"props":9525,"children":9526},{"style":1349},[9527],{"type":26,"value":4063},{"type":21,"tag":455,"props":9529,"children":9530},{"style":4527},[9531],{"type":26,"value":9532}," t",{"type":21,"tag":455,"props":9534,"children":9535},{"style":1349},[9536],{"type":26,"value":4535},{"type":21,"tag":455,"props":9538,"children":9539},{"style":486},[9540],{"type":26,"value":2470},{"type":21,"tag":455,"props":9542,"children":9543},{"class":457,"line":1622},[9544,9549],{"type":21,"tag":455,"props":9545,"children":9546},{"style":1349},[9547],{"type":26,"value":9548},"  let",{"type":21,"tag":455,"props":9550,"children":9551},{"style":486},[9552],{"type":26,"value":9553}," mongod, app;\n",{"type":21,"tag":455,"props":9555,"children":9556},{"class":457,"line":1663},[9557],{"type":21,"tag":455,"props":9558,"children":9559},{"emptyLinePlaceholder":471},[9560],{"type":26,"value":474},{"type":21,"tag":455,"props":9562,"children":9563},{"class":457,"line":1680},[9564,9569,9574,9578,9582,9587,9591],{"type":21,"tag":455,"props":9565,"children":9566},{"style":486},[9567],{"type":26,"value":9568},"  t.",{"type":21,"tag":455,"props":9570,"children":9571},{"style":1365},[9572],{"type":26,"value":9573},"before",{"type":21,"tag":455,"props":9575,"children":9576},{"style":486},[9577],{"type":26,"value":489},{"type":21,"tag":455,"props":9579,"children":9580},{"style":1349},[9581],{"type":26,"value":4063},{"type":21,"tag":455,"props":9583,"children":9584},{"style":486},[9585],{"type":26,"value":9586}," () ",{"type":21,"tag":455,"props":9588,"children":9589},{"style":1349},[9590],{"type":26,"value":4589},{"type":21,"tag":455,"props":9592,"children":9593},{"style":486},[9594],{"type":26,"value":2470},{"type":21,"tag":455,"props":9596,"children":9597},{"class":457,"line":1721},[9598],{"type":21,"tag":455,"props":9599,"children":9600},{"style":462},[9601],{"type":26,"value":9602},"    // Create a new in-memory MongoDB instance before the tests run,\n",{"type":21,"tag":455,"props":9604,"children":9605},{"class":457,"line":1738},[9606],{"type":21,"tag":455,"props":9607,"children":9608},{"style":462},[9609],{"type":26,"value":9610},"    // and build the Fastify application\n",{"type":21,"tag":455,"props":9612,"children":9613},{"class":457,"line":1779},[9614,9619,9623,9627,9631,9635],{"type":21,"tag":455,"props":9615,"children":9616},{"style":486},[9617],{"type":26,"value":9618},"    mongod ",{"type":21,"tag":455,"props":9620,"children":9621},{"style":1349},[9622],{"type":26,"value":3193},{"type":21,"tag":455,"props":9624,"children":9625},{"style":1349},[9626],{"type":26,"value":8498},{"type":21,"tag":455,"props":9628,"children":9629},{"style":486},[9630],{"type":26,"value":9172},{"type":21,"tag":455,"props":9632,"children":9633},{"style":1365},[9634],{"type":26,"value":9177},{"type":21,"tag":455,"props":9636,"children":9637},{"style":486},[9638],{"type":26,"value":3398},{"type":21,"tag":455,"props":9640,"children":9641},{"class":457,"line":1796},[9642,9647,9651,9655,9660],{"type":21,"tag":455,"props":9643,"children":9644},{"style":486},[9645],{"type":26,"value":9646},"    process.env.",{"type":21,"tag":455,"props":9648,"children":9649},{"style":480},[9650],{"type":26,"value":3351},{"type":21,"tag":455,"props":9652,"children":9653},{"style":1349},[9654],{"type":26,"value":2131},{"type":21,"tag":455,"props":9656,"children":9657},{"style":492},[9658],{"type":26,"value":9659}," 'test'",{"type":21,"tag":455,"props":9661,"children":9662},{"style":486},[9663],{"type":26,"value":1440},{"type":21,"tag":455,"props":9665,"children":9666},{"class":457,"line":1837},[9667,9671,9675,9679,9683,9687],{"type":21,"tag":455,"props":9668,"children":9669},{"style":486},[9670],{"type":26,"value":9646},{"type":21,"tag":455,"props":9672,"children":9673},{"style":480},[9674],{"type":26,"value":3604},{"type":21,"tag":455,"props":9676,"children":9677},{"style":1349},[9678],{"type":26,"value":2131},{"type":21,"tag":455,"props":9680,"children":9681},{"style":486},[9682],{"type":26,"value":9202},{"type":21,"tag":455,"props":9684,"children":9685},{"style":1365},[9686],{"type":26,"value":9207},{"type":21,"tag":455,"props":9688,"children":9689},{"style":486},[9690],{"type":26,"value":3398},{"type":21,"tag":455,"props":9692,"children":9693},{"class":457,"line":1854},[9694,9699,9703,9707,9711],{"type":21,"tag":455,"props":9695,"children":9696},{"style":486},[9697],{"type":26,"value":9698},"    app ",{"type":21,"tag":455,"props":9700,"children":9701},{"style":1349},[9702],{"type":26,"value":3193},{"type":21,"tag":455,"props":9704,"children":9705},{"style":1349},[9706],{"type":26,"value":8498},{"type":21,"tag":455,"props":9708,"children":9709},{"style":1365},[9710],{"type":26,"value":4073},{"type":21,"tag":455,"props":9712,"children":9713},{"style":486},[9714],{"type":26,"value":3398},{"type":21,"tag":455,"props":9716,"children":9717},{"class":457,"line":1895},[9718],{"type":21,"tag":455,"props":9719,"children":9720},{"style":486},[9721],{"type":26,"value":4389},{"type":21,"tag":455,"props":9723,"children":9724},{"class":457,"line":1912},[9725,9729,9734,9738,9742,9746,9750],{"type":21,"tag":455,"props":9726,"children":9727},{"style":486},[9728],{"type":26,"value":9568},{"type":21,"tag":455,"props":9730,"children":9731},{"style":1365},[9732],{"type":26,"value":9733},"beforeEach",{"type":21,"tag":455,"props":9735,"children":9736},{"style":486},[9737],{"type":26,"value":489},{"type":21,"tag":455,"props":9739,"children":9740},{"style":1349},[9741],{"type":26,"value":4063},{"type":21,"tag":455,"props":9743,"children":9744},{"style":4527},[9745],{"type":26,"value":9532},{"type":21,"tag":455,"props":9747,"children":9748},{"style":1349},[9749],{"type":26,"value":4535},{"type":21,"tag":455,"props":9751,"children":9752},{"style":486},[9753],{"type":26,"value":2470},{"type":21,"tag":455,"props":9755,"children":9756},{"class":457,"line":1953},[9757],{"type":21,"tag":455,"props":9758,"children":9759},{"style":462},[9760],{"type":26,"value":9761},"    // Before each test, call a utility which wipes all content from the\n",{"type":21,"tag":455,"props":9763,"children":9764},{"class":457,"line":1970},[9765],{"type":21,"tag":455,"props":9766,"children":9767},{"style":462},[9768],{"type":26,"value":9769},"    // database, so that test cases are fully independent.\n",{"type":21,"tag":455,"props":9771,"children":9772},{"class":457,"line":1978},[9773,9778,9783],{"type":21,"tag":455,"props":9774,"children":9775},{"style":1349},[9776],{"type":26,"value":9777},"    await",{"type":21,"tag":455,"props":9779,"children":9780},{"style":1365},[9781],{"type":26,"value":9782}," deleteAll",{"type":21,"tag":455,"props":9784,"children":9785},{"style":486},[9786],{"type":26,"value":3398},{"type":21,"tag":455,"props":9788,"children":9789},{"class":457,"line":1996},[9790],{"type":21,"tag":455,"props":9791,"children":9792},{"style":486},[9793],{"type":26,"value":4389},{"type":21,"tag":455,"props":9795,"children":9796},{"class":457,"line":4487},[9797,9801,9806,9810,9814,9818,9822],{"type":21,"tag":455,"props":9798,"children":9799},{"style":486},[9800],{"type":26,"value":9568},{"type":21,"tag":455,"props":9802,"children":9803},{"style":1365},[9804],{"type":26,"value":9805},"teardown",{"type":21,"tag":455,"props":9807,"children":9808},{"style":486},[9809],{"type":26,"value":489},{"type":21,"tag":455,"props":9811,"children":9812},{"style":1349},[9813],{"type":26,"value":4063},{"type":21,"tag":455,"props":9815,"children":9816},{"style":486},[9817],{"type":26,"value":9586},{"type":21,"tag":455,"props":9819,"children":9820},{"style":1349},[9821],{"type":26,"value":4589},{"type":21,"tag":455,"props":9823,"children":9824},{"style":486},[9825],{"type":26,"value":2470},{"type":21,"tag":455,"props":9827,"children":9828},{"class":457,"line":4495},[9829],{"type":21,"tag":455,"props":9830,"children":9831},{"style":462},[9832],{"type":26,"value":9833},"    // Shut down the app and stop the database when tests complete.\n",{"type":21,"tag":455,"props":9835,"children":9836},{"class":457,"line":4509},[9837,9841,9845,9850],{"type":21,"tag":455,"props":9838,"children":9839},{"style":1349},[9840],{"type":26,"value":9777},{"type":21,"tag":455,"props":9842,"children":9843},{"style":486},[9844],{"type":26,"value":3863},{"type":21,"tag":455,"props":9846,"children":9847},{"style":1365},[9848],{"type":26,"value":9849},"close",{"type":21,"tag":455,"props":9851,"children":9852},{"style":486},[9853],{"type":26,"value":3398},{"type":21,"tag":455,"props":9855,"children":9856},{"class":457,"line":4561},[9857,9861,9865,9869],{"type":21,"tag":455,"props":9858,"children":9859},{"style":1349},[9860],{"type":26,"value":9777},{"type":21,"tag":455,"props":9862,"children":9863},{"style":486},[9864],{"type":26,"value":9202},{"type":21,"tag":455,"props":9866,"children":9867},{"style":1365},[9868],{"type":26,"value":9246},{"type":21,"tag":455,"props":9870,"children":9871},{"style":486},[9872],{"type":26,"value":3398},{"type":21,"tag":455,"props":9874,"children":9875},{"class":457,"line":4596},[9876],{"type":21,"tag":455,"props":9877,"children":9878},{"style":486},[9879],{"type":26,"value":4389},{"type":21,"tag":455,"props":9881,"children":9882},{"class":457,"line":4627},[9883],{"type":21,"tag":455,"props":9884,"children":9885},{"emptyLinePlaceholder":471},[9886],{"type":26,"value":474},{"type":21,"tag":455,"props":9888,"children":9889},{"class":457,"line":5942},[9890],{"type":21,"tag":455,"props":9891,"children":9892},{"style":462},[9893],{"type":26,"value":9894},"  // Define a test for POST /users\n",{"type":21,"tag":455,"props":9896,"children":9897},{"class":457,"line":5965},[9898,9902,9907,9911,9915,9920,9924,9928,9932,9936],{"type":21,"tag":455,"props":9899,"children":9900},{"style":1349},[9901],{"type":26,"value":4397},{"type":21,"tag":455,"props":9903,"children":9904},{"style":486},[9905],{"type":26,"value":9906}," t.",{"type":21,"tag":455,"props":9908,"children":9909},{"style":1365},[9910],{"type":26,"value":5788},{"type":21,"tag":455,"props":9912,"children":9913},{"style":486},[9914],{"type":26,"value":489},{"type":21,"tag":455,"props":9916,"children":9917},{"style":492},[9918],{"type":26,"value":9919},"'POST /users'",{"type":21,"tag":455,"props":9921,"children":9922},{"style":486},[9923],{"type":26,"value":2228},{"type":21,"tag":455,"props":9925,"children":9926},{"style":1349},[9927],{"type":26,"value":4063},{"type":21,"tag":455,"props":9929,"children":9930},{"style":4527},[9931],{"type":26,"value":9532},{"type":21,"tag":455,"props":9933,"children":9934},{"style":1349},[9935],{"type":26,"value":4535},{"type":21,"tag":455,"props":9937,"children":9938},{"style":486},[9939],{"type":26,"value":2470},{"type":21,"tag":455,"props":9941,"children":9942},{"class":457,"line":5983},[9943],{"type":21,"tag":455,"props":9944,"children":9945},{"style":462},[9946],{"type":26,"value":9947},"    // Here is the Fastify inject() utility that's being leveraged for testing:\n",{"type":21,"tag":455,"props":9949,"children":9950},{"class":457,"line":6002},[9951],{"type":21,"tag":455,"props":9952,"children":9953},{"style":462},[9954],{"type":26,"value":9955},"    // A POST request is being simulated without a Webserver active, but it\n",{"type":21,"tag":455,"props":9957,"children":9958},{"class":457,"line":6011},[9959],{"type":21,"tag":455,"props":9960,"children":9961},{"style":462},[9962],{"type":26,"value":9963},"    // otherwise functions the same way that a true POST request would. The\n",{"type":21,"tag":455,"props":9965,"children":9966},{"class":457,"line":6019},[9967],{"type":21,"tag":455,"props":9968,"children":9969},{"style":462},[9970],{"type":26,"value":9971},"    // return value is a Promise that resolves to the Response object.\n",{"type":21,"tag":455,"props":9973,"children":9974},{"class":457,"line":6028},[9975,9980,9985,9989,9993,9997,10002],{"type":21,"tag":455,"props":9976,"children":9977},{"style":1349},[9978],{"type":26,"value":9979},"    const",{"type":21,"tag":455,"props":9981,"children":9982},{"style":480},[9983],{"type":26,"value":9984}," response",{"type":21,"tag":455,"props":9986,"children":9987},{"style":1349},[9988],{"type":26,"value":2131},{"type":21,"tag":455,"props":9990,"children":9991},{"style":1349},[9992],{"type":26,"value":8498},{"type":21,"tag":455,"props":9994,"children":9995},{"style":486},[9996],{"type":26,"value":3863},{"type":21,"tag":455,"props":9998,"children":9999},{"style":1365},[10000],{"type":26,"value":10001},"inject",{"type":21,"tag":455,"props":10003,"children":10004},{"style":486},[10005],{"type":26,"value":7075},{"type":21,"tag":455,"props":10007,"children":10008},{"class":457,"line":6085},[10009,10014,10019],{"type":21,"tag":455,"props":10010,"children":10011},{"style":486},[10012],{"type":26,"value":10013},"      method: ",{"type":21,"tag":455,"props":10015,"children":10016},{"style":492},[10017],{"type":26,"value":10018},"'POST'",{"type":21,"tag":455,"props":10020,"children":10021},{"style":486},[10022],{"type":26,"value":2483},{"type":21,"tag":455,"props":10024,"children":10025},{"class":457,"line":6097},[10026,10031,10036],{"type":21,"tag":455,"props":10027,"children":10028},{"style":486},[10029],{"type":26,"value":10030},"      url: ",{"type":21,"tag":455,"props":10032,"children":10033},{"style":492},[10034],{"type":26,"value":10035},"'/api/users'",{"type":21,"tag":455,"props":10037,"children":10038},{"style":486},[10039],{"type":26,"value":2483},{"type":21,"tag":455,"props":10041,"children":10042},{"class":457,"line":6117},[10043],{"type":21,"tag":455,"props":10044,"children":10045},{"style":486},[10046],{"type":26,"value":10047},"      headers: {\n",{"type":21,"tag":455,"props":10049,"children":10050},{"class":457,"line":6134},[10051,10056,10060,10065],{"type":21,"tag":455,"props":10052,"children":10053},{"style":492},[10054],{"type":26,"value":10055},"        'Content-Type'",{"type":21,"tag":455,"props":10057,"children":10058},{"style":486},[10059],{"type":26,"value":7129},{"type":21,"tag":455,"props":10061,"children":10062},{"style":492},[10063],{"type":26,"value":10064},"'application/json'",{"type":21,"tag":455,"props":10066,"children":10067},{"style":486},[10068],{"type":26,"value":2483},{"type":21,"tag":455,"props":10070,"children":10071},{"class":457,"line":6142},[10072,10077,10081,10086],{"type":21,"tag":455,"props":10073,"children":10074},{"style":492},[10075],{"type":26,"value":10076},"        'X-Requested-With'",{"type":21,"tag":455,"props":10078,"children":10079},{"style":486},[10080],{"type":26,"value":7129},{"type":21,"tag":455,"props":10082,"children":10083},{"style":492},[10084],{"type":26,"value":10085},"''",{"type":21,"tag":455,"props":10087,"children":10088},{"style":486},[10089],{"type":26,"value":2483},{"type":21,"tag":455,"props":10091,"children":10093},{"class":457,"line":10092},44,[10094],{"type":21,"tag":455,"props":10095,"children":10096},{"style":486},[10097],{"type":26,"value":5846},{"type":21,"tag":455,"props":10099,"children":10101},{"class":457,"line":10100},45,[10102],{"type":21,"tag":455,"props":10103,"children":10104},{"style":486},[10105],{"type":26,"value":10106},"      payload: {\n",{"type":21,"tag":455,"props":10108,"children":10110},{"class":457,"line":10109},46,[10111],{"type":21,"tag":455,"props":10112,"children":10113},{"style":486},[10114],{"type":26,"value":10115},"        user: {\n",{"type":21,"tag":455,"props":10117,"children":10119},{"class":457,"line":10118},47,[10120,10125,10130],{"type":21,"tag":455,"props":10121,"children":10122},{"style":486},[10123],{"type":26,"value":10124},"          email: ",{"type":21,"tag":455,"props":10126,"children":10127},{"style":492},[10128],{"type":26,"value":10129},"'john@jacob.com'",{"type":21,"tag":455,"props":10131,"children":10132},{"style":486},[10133],{"type":26,"value":2483},{"type":21,"tag":455,"props":10135,"children":10137},{"class":457,"line":10136},48,[10138,10143,10148],{"type":21,"tag":455,"props":10139,"children":10140},{"style":486},[10141],{"type":26,"value":10142},"          password: ",{"type":21,"tag":455,"props":10144,"children":10145},{"style":492},[10146],{"type":26,"value":10147},"'johnnyjacob'",{"type":21,"tag":455,"props":10149,"children":10150},{"style":486},[10151],{"type":26,"value":2483},{"type":21,"tag":455,"props":10153,"children":10155},{"class":457,"line":10154},49,[10156,10161,10166],{"type":21,"tag":455,"props":10157,"children":10158},{"style":486},[10159],{"type":26,"value":10160},"          username: ",{"type":21,"tag":455,"props":10162,"children":10163},{"style":492},[10164],{"type":26,"value":10165},"'johnjacob'",{"type":21,"tag":455,"props":10167,"children":10168},{"style":486},[10169],{"type":26,"value":2483},{"type":21,"tag":455,"props":10171,"children":10173},{"class":457,"line":10172},50,[10174],{"type":21,"tag":455,"props":10175,"children":10176},{"style":486},[10177],{"type":26,"value":7949},{"type":21,"tag":455,"props":10179,"children":10181},{"class":457,"line":10180},51,[10182],{"type":21,"tag":455,"props":10183,"children":10184},{"style":486},[10185],{"type":26,"value":5846},{"type":21,"tag":455,"props":10187,"children":10189},{"class":457,"line":10188},52,[10190],{"type":21,"tag":455,"props":10191,"children":10192},{"style":486},[10193],{"type":26,"value":10194},"    })\n",{"type":21,"tag":455,"props":10196,"children":10198},{"class":457,"line":10197},53,[10199],{"type":21,"tag":455,"props":10200,"children":10201},{"emptyLinePlaceholder":471},[10202],{"type":26,"value":474},{"type":21,"tag":455,"props":10204,"children":10206},{"class":457,"line":10205},54,[10207],{"type":21,"tag":455,"props":10208,"children":10209},{"style":462},[10210],{"type":26,"value":10211},"    // With the Response in hand, whatever necessary test assertions can\n",{"type":21,"tag":455,"props":10213,"children":10215},{"class":457,"line":10214},55,[10216],{"type":21,"tag":455,"props":10217,"children":10218},{"style":462},[10219],{"type":26,"value":10220},"    // be made.\n",{"type":21,"tag":455,"props":10222,"children":10224},{"class":457,"line":10223},56,[10225,10230,10235,10240,10245,10249,10254],{"type":21,"tag":455,"props":10226,"children":10227},{"style":486},[10228],{"type":26,"value":10229},"    t.",{"type":21,"tag":455,"props":10231,"children":10232},{"style":1365},[10233],{"type":26,"value":10234},"equal",{"type":21,"tag":455,"props":10236,"children":10237},{"style":486},[10238],{"type":26,"value":10239},"(response.statusCode, ",{"type":21,"tag":455,"props":10241,"children":10242},{"style":480},[10243],{"type":26,"value":10244},"200",{"type":21,"tag":455,"props":10246,"children":10247},{"style":486},[10248],{"type":26,"value":2228},{"type":21,"tag":455,"props":10250,"children":10251},{"style":492},[10252],{"type":26,"value":10253},"'returns a status code of 200'",{"type":21,"tag":455,"props":10255,"children":10256},{"style":486},[10257],{"type":26,"value":2171},{"type":21,"tag":455,"props":10259,"children":10261},{"class":457,"line":10260},57,[10262,10266,10271,10275,10280,10284],{"type":21,"tag":455,"props":10263,"children":10264},{"style":1349},[10265],{"type":26,"value":9979},{"type":21,"tag":455,"props":10267,"children":10268},{"style":480},[10269],{"type":26,"value":10270}," body",{"type":21,"tag":455,"props":10272,"children":10273},{"style":1349},[10274],{"type":26,"value":2131},{"type":21,"tag":455,"props":10276,"children":10277},{"style":486},[10278],{"type":26,"value":10279}," response.",{"type":21,"tag":455,"props":10281,"children":10282},{"style":1365},[10283],{"type":26,"value":3544},{"type":21,"tag":455,"props":10285,"children":10286},{"style":486},[10287],{"type":26,"value":3398},{"type":21,"tag":455,"props":10289,"children":10291},{"class":457,"line":10290},58,[10292,10296,10301,10306,10311,10315,10320],{"type":21,"tag":455,"props":10293,"children":10294},{"style":486},[10295],{"type":26,"value":10229},{"type":21,"tag":455,"props":10297,"children":10298},{"style":1365},[10299],{"type":26,"value":10300},"hasProp",{"type":21,"tag":455,"props":10302,"children":10303},{"style":486},[10304],{"type":26,"value":10305},"(body, ",{"type":21,"tag":455,"props":10307,"children":10308},{"style":492},[10309],{"type":26,"value":10310},"'user'",{"type":21,"tag":455,"props":10312,"children":10313},{"style":486},[10314],{"type":26,"value":2228},{"type":21,"tag":455,"props":10316,"children":10317},{"style":492},[10318],{"type":26,"value":10319},"'Response has \"user\" property\"'",{"type":21,"tag":455,"props":10321,"children":10322},{"style":486},[10323],{"type":26,"value":2171},{"type":21,"tag":455,"props":10325,"children":10327},{"class":457,"line":10326},59,[10328,10332,10336,10341,10346,10350,10355],{"type":21,"tag":455,"props":10329,"children":10330},{"style":486},[10331],{"type":26,"value":10229},{"type":21,"tag":455,"props":10333,"children":10334},{"style":1365},[10335],{"type":26,"value":10300},{"type":21,"tag":455,"props":10337,"children":10338},{"style":486},[10339],{"type":26,"value":10340},"(body.user, ",{"type":21,"tag":455,"props":10342,"children":10343},{"style":492},[10344],{"type":26,"value":10345},"'email'",{"type":21,"tag":455,"props":10347,"children":10348},{"style":486},[10349],{"type":26,"value":2228},{"type":21,"tag":455,"props":10351,"children":10352},{"style":492},[10353],{"type":26,"value":10354},"'User has \"email\" property\"'",{"type":21,"tag":455,"props":10356,"children":10357},{"style":486},[10358],{"type":26,"value":2171},{"type":21,"tag":455,"props":10360,"children":10362},{"class":457,"line":10361},60,[10363,10367,10371,10375,10380,10384,10389],{"type":21,"tag":455,"props":10364,"children":10365},{"style":486},[10366],{"type":26,"value":10229},{"type":21,"tag":455,"props":10368,"children":10369},{"style":1365},[10370],{"type":26,"value":10300},{"type":21,"tag":455,"props":10372,"children":10373},{"style":486},[10374],{"type":26,"value":10340},{"type":21,"tag":455,"props":10376,"children":10377},{"style":492},[10378],{"type":26,"value":10379},"'username'",{"type":21,"tag":455,"props":10381,"children":10382},{"style":486},[10383],{"type":26,"value":2228},{"type":21,"tag":455,"props":10385,"children":10386},{"style":492},[10387],{"type":26,"value":10388},"'User has \"username\" property\"'",{"type":21,"tag":455,"props":10390,"children":10391},{"style":486},[10392],{"type":26,"value":2171},{"type":21,"tag":455,"props":10394,"children":10396},{"class":457,"line":10395},61,[10397,10401,10405,10409,10414,10418,10423],{"type":21,"tag":455,"props":10398,"children":10399},{"style":486},[10400],{"type":26,"value":10229},{"type":21,"tag":455,"props":10402,"children":10403},{"style":1365},[10404],{"type":26,"value":10300},{"type":21,"tag":455,"props":10406,"children":10407},{"style":486},[10408],{"type":26,"value":10340},{"type":21,"tag":455,"props":10410,"children":10411},{"style":492},[10412],{"type":26,"value":10413},"'token'",{"type":21,"tag":455,"props":10415,"children":10416},{"style":486},[10417],{"type":26,"value":2228},{"type":21,"tag":455,"props":10419,"children":10420},{"style":492},[10421],{"type":26,"value":10422},"'User has \"token\" property\"'",{"type":21,"tag":455,"props":10424,"children":10425},{"style":486},[10426],{"type":26,"value":2171},{"type":21,"tag":455,"props":10428,"children":10430},{"class":457,"line":10429},62,[10431],{"type":21,"tag":455,"props":10432,"children":10433},{"style":486},[10434],{"type":26,"value":4389},{"type":21,"tag":455,"props":10436,"children":10438},{"class":457,"line":10437},63,[10439],{"type":21,"tag":455,"props":10440,"children":10441},{"style":486},[10442],{"type":26,"value":3952},{"type":21,"tag":56,"props":10444,"children":10446},{"id":10445},"conclusions",[10447],{"type":26,"value":10448},"Conclusions",{"type":21,"tag":22,"props":10450,"children":10451},{},[10452,10454,10459],{"type":26,"value":10453},"Completing this migration took me several hours, some of which was ramping up on ",{"type":21,"tag":246,"props":10455,"children":10457},{"className":10456},[],[10458],{"type":26,"value":2893},{"type":26,"value":10460}," and making changes to the app structure, but a lot of which was working through each migrated API endpoint. Consequently, I think the time it takes to complete a migration is going to scale linearly with the size of a project.",{"type":21,"tag":22,"props":10462,"children":10463},{},[10464],{"type":26,"value":10465},"While I do think Fastify is a better choice than Express as a lightweight Web framework for Node.js, the cost of migrating relative to the benefits makes it such that I would only recommend doing it in specific scenarios, for example for small or early-stage projects, or when a major refactor is already going to take place. That said, I found the process itself to be smooth and reliable, and with good pre-existing test coverage I feel it's about as safe to do as any major refactor can be.",{"type":21,"tag":22,"props":10467,"children":10468},{},[10469,10471,10476],{"type":26,"value":10470},"I hope this example is helpful for anyone considering making the switch to Fastify; the full source code is available on GitHub ",{"type":21,"tag":322,"props":10472,"children":10474},{"href":2865,"rel":10473},[326],[10475],{"type":26,"value":2869},{"type":26,"value":551},{"type":21,"tag":1279,"props":10478,"children":10479},{},[10480],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":10482},[10483,10484,10485,10486,10491,10492,10493],{"id":2874,"depth":336,"text":2877},{"id":2935,"depth":336,"text":2938},{"id":3069,"depth":336,"text":3072},{"id":5246,"depth":336,"text":5249,"children":10487},[10488,10489,10490],{"id":5252,"depth":333,"text":5255},{"id":6321,"depth":333,"text":6324},{"id":6876,"depth":333,"text":6879},{"id":8656,"depth":336,"text":8659},{"id":8716,"depth":336,"text":8719},{"id":10445,"depth":336,"text":10448},"content:phendry:2022-07-28:MigratingFromExpressToFastifyPart2.md","phendry/2022-07-28/MigratingFromExpressToFastifyPart2.md","phendry/2022-07-28/MigratingFromExpressToFastifyPart2",{"user":2795,"name":2796},{"_path":10499,"_dir":10500,"_draft":7,"_partial":7,"_locale":8,"title":10501,"description":10502,"image":10503,"tags":10504,"publishDate":10505,"excerpt":10502,"body":10506,"_type":343,"_id":10976,"_source":345,"_file":10977,"_stem":10978,"_extension":348,"author":10979},"/phendry/2022-07-21/migratingfromexpresstofastifypart1","2022-07-21","Migrating from Express to Fastify, Part 1","Express.js has for years been the dominant lightweight Web framework for Node.js, but over time its development has stalled, with its latest major version (5.0) still in pre-release nearly eight years after its first alpha release. There's a lot to be said for this sort of stability in a foundational dependency for a project, but it's worth assessing whether the added features of competing frameworks are worth making a switch. In this article we'll be looking at Fastify in particular, to understand what it has to offer compared to Express and how difficult it is to migrate an existing Express project.","/phendry/2022-07-21/img/Migrating from Express to Fastify, Part 1.png",[2804,15],"2023-12-01",{"type":18,"children":10507,"toc":10962},[10508,10525,10531,10536,10541,10547,10560,10566,10580,10600,10649,10655,10686,10692,10706,10712,10717,10742,10747,10769,10775,10780,10816,10822,10854,10859,10898,10904,10909,10914,10930,10942],{"type":21,"tag":22,"props":10509,"children":10510},{},[10511,10516,10518,10523],{"type":21,"tag":322,"props":10512,"children":10514},{"href":2824,"rel":10513},[326],[10515],{"type":26,"value":2828},{"type":26,"value":10517}," has for years been the dominant lightweight Web framework for Node.js, but over time its development has stalled, with its latest major version (5.0) still in pre-release nearly eight years after its first alpha release. There's a lot to be said for this sort of stability in a foundational dependency for a project, but it's worth assessing whether the added features of competing frameworks are worth making a switch. In this article we'll be looking at ",{"type":21,"tag":322,"props":10519,"children":10521},{"href":2815,"rel":10520},[326],[10522],{"type":26,"value":2819},{"type":26,"value":10524}," in particular, to understand what it has to offer compared to Express and how difficult it is to migrate an existing Express project.",{"type":21,"tag":56,"props":10526,"children":10528},{"id":10527},"why-fastify",[10529],{"type":26,"value":10530},"Why Fastify?",{"type":21,"tag":22,"props":10532,"children":10533},{},[10534],{"type":26,"value":10535},"Like Express, Fastify is lightweight and unopinionated, it's very popular (which bodes well for the longevity of the project), it has good documentation, and it provides comparable features (routing, templating, middleware, etc).",{"type":21,"tag":22,"props":10537,"children":10538},{},[10539],{"type":26,"value":10540},"It has considerably more to offer, however:",{"type":21,"tag":80,"props":10542,"children":10544},{"id":10543},"a-committed-long-term-support-schedule",[10545],{"type":26,"value":10546},"A committed long-term support schedule",{"type":21,"tag":22,"props":10548,"children":10549},{},[10550,10551,10558],{"type":26,"value":8737},{"type":21,"tag":322,"props":10552,"children":10555},{"href":10553,"rel":10554},"https://www.fastify.io/docs/latest/Reference/LTS/",[326],[10556],{"type":26,"value":10557},"LTS documentation",{"type":26,"value":10559}," makes it easy to see the end-of-life date and supported Node versions of a given release. For a foundational dependency like a Web framework, upgrades have the potential to be time-consuming and error-prone, so being able to do so on a predictable schedule is key.",{"type":21,"tag":80,"props":10561,"children":10563},{"id":10562},"quality-major-version-upgrade-guides",[10564],{"type":26,"value":10565},"Quality major version upgrade guides",{"type":21,"tag":22,"props":10567,"children":10568},{},[10569,10571,10578],{"type":26,"value":10570},"The project has a history of providing comprehensive ",{"type":21,"tag":322,"props":10572,"children":10575},{"href":10573,"rel":10574},"https://www.fastify.io/docs/latest/Guides/Migration-Guide-V4/",[326],[10576],{"type":26,"value":10577},"migration guides",{"type":26,"value":10579},", which provides a lot of reassurance that future upgrades can be done smoothly, systematically and without introducing bugs.",{"type":21,"tag":80,"props":10581,"children":10583},{"id":10582},"built-in-asyncawait-support",[10584,10586,10591,10592,10598],{"type":26,"value":10585},"Built-in ",{"type":21,"tag":246,"props":10587,"children":10589},{"className":10588},[],[10590],{"type":26,"value":4063},{"type":26,"value":5754},{"type":21,"tag":246,"props":10593,"children":10595},{"className":10594},[],[10596],{"type":26,"value":10597},"await",{"type":26,"value":10599}," support",{"type":21,"tag":22,"props":10601,"children":10602},{},[10603,10605,10621,10623,10629,10630,10635,10637,10648],{"type":26,"value":10604},"While it's a small feature, Fastify's route handlers support ",{"type":21,"tag":322,"props":10606,"children":10609},{"href":10607,"rel":10608},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function",[326],[10610,10615,10616],{"type":21,"tag":246,"props":10611,"children":10613},{"className":10612},[],[10614],{"type":26,"value":4063},{"type":26,"value":5754},{"type":21,"tag":246,"props":10617,"children":10619},{"className":10618},[],[10620],{"type":26,"value":10597},{"type":26,"value":10622}," syntax by default, catching errors and turning them into HTTP 500 responses. In Express, async errors are unhandled unless handled manually with a ",{"type":21,"tag":246,"props":10624,"children":10626},{"className":10625},[],[10627],{"type":26,"value":10628},"try",{"type":26,"value":5754},{"type":21,"tag":246,"props":10631,"children":10633},{"className":10632},[],[10634],{"type":26,"value":4638},{"type":26,"value":10636}," or with an added dependency like ",{"type":21,"tag":322,"props":10638,"children":10641},{"href":10639,"rel":10640},"https://github.com/Abazhenov/express-async-handler",[326],[10642],{"type":21,"tag":246,"props":10643,"children":10645},{"className":10644},[],[10646],{"type":26,"value":10647},"express-async-handler",{"type":26,"value":551},{"type":21,"tag":80,"props":10650,"children":10652},{"id":10651},"built-in-request-validation",[10653],{"type":26,"value":10654},"Built-in request validation",{"type":21,"tag":22,"props":10656,"children":10657},{},[10658,10660,10667,10669,10676,10678,10684],{"type":26,"value":10659},"Something that's often missing from Express projects is comprehensive request validation, and this is a source of bugs and security vulnerabilities. Fastify has a ",{"type":21,"tag":322,"props":10661,"children":10664},{"href":10662,"rel":10663},"https://www.fastify.io/docs/latest/Reference/Validation-and-Serialization/",[326],[10665],{"type":26,"value":10666},"robust validation system",{"type":26,"value":10668}," based on the ",{"type":21,"tag":322,"props":10670,"children":10673},{"href":10671,"rel":10672},"https://json-schema.org/",[326],[10674],{"type":26,"value":10675},"JSON Schema",{"type":26,"value":10677}," specification, making validation as easy as adding a ",{"type":21,"tag":246,"props":10679,"children":10681},{"className":10680},[],[10682],{"type":26,"value":10683},"schema",{"type":26,"value":10685}," key to a route's options.",{"type":21,"tag":80,"props":10687,"children":10689},{"id":10688},"large-ecosystem-of-core-plugins",[10690],{"type":26,"value":10691},"Large ecosystem of \"core\" plugins",{"type":21,"tag":22,"props":10693,"children":10694},{},[10695,10697,10704],{"type":26,"value":10696},"Fastify's ",{"type":21,"tag":322,"props":10698,"children":10701},{"href":10699,"rel":10700},"https://www.fastify.io/docs/latest/Guides/Ecosystem/",[326],[10702],{"type":26,"value":10703},"plugin ecosystem",{"type":26,"value":10705}," is comparable to Express, but one thing that sets it apart is that many of these plugins are \"core\" plugins maintained by the Fastify team themselves. A project's Achilles' heel can be a dependency on some community third-party library whose author has dropped off the radar, and yet this risk is not always apparent when developers are choosing to take on a new dependency. Having a large list of core plugins to choose from means having a lot of additional functionality at your disposal without increasing your project's dependence on third parties.",{"type":21,"tag":80,"props":10707,"children":10709},{"id":10708},"built-in-testing-capabilities",[10710],{"type":26,"value":10711},"Built-in testing capabilities",{"type":21,"tag":22,"props":10713,"children":10714},{},[10715],{"type":26,"value":10716},"Express does not provide any functionality intended for testing, which means projects tend to either",{"type":21,"tag":183,"props":10718,"children":10719},{},[10720,10732,10737],{"type":21,"tag":187,"props":10721,"children":10722},{},[10723,10725,10730],{"type":26,"value":10724},"start up a full Webserver and use a tool like ",{"type":21,"tag":322,"props":10726,"children":10728},{"href":3030,"rel":10727},[326],[10729],{"type":26,"value":3034},{"type":26,"value":10731}," to test via real API requests,",{"type":21,"tag":187,"props":10733,"children":10734},{},[10735],{"type":26,"value":10736},"expose route handlers and test them independently of the routing, or",{"type":21,"tag":187,"props":10738,"children":10739},{},[10740],{"type":26,"value":10741},"skip integration tests entirely.",{"type":21,"tag":22,"props":10743,"children":10744},{},[10745],{"type":26,"value":10746},"Integration testing against a running Webserver is slow, and it's brittle given how limited the options are for setting up test data or mocking external services. Testing route handlers is better, but it leaves a gap in test coverage (the untested routing/middleware/validation).",{"type":21,"tag":22,"props":10748,"children":10749},{},[10750,10752,10759,10761,10767],{"type":26,"value":10751},"Fastify, on the other hand, provides ",{"type":21,"tag":322,"props":10753,"children":10756},{"href":10754,"rel":10755},"https://www.fastify.io/docs/latest/Guides/Testing/",[326],[10757],{"type":26,"value":10758},"testing utilities",{"type":26,"value":10760}," which allow for mock requests to be processed using a ",{"type":21,"tag":246,"props":10762,"children":10764},{"className":10763},[],[10765],{"type":26,"value":10766},".inject()",{"type":26,"value":10768}," method, without spinning up a Webserver. This means tests are fast, and they allow for arbitrary setup/teardown logic (like opening database connections and loading test data), while still being test framework independent.",{"type":21,"tag":80,"props":10770,"children":10772},{"id":10771},"typescript-compatibility",[10773],{"type":26,"value":10774},"TypeScript compatibility",{"type":21,"tag":22,"props":10776,"children":10777},{},[10778],{"type":26,"value":10779},"There are TypeScript typings for Express, and Fastify is similarly a JS project with added TypeScript typings (as opposed to being designed from the ground up to leverage a type system).",{"type":21,"tag":22,"props":10781,"children":10782},{},[10783,10785,10792,10794,10800,10801,10807,10808,10814],{"type":26,"value":10784},"However, Fastify also provides ",{"type":21,"tag":322,"props":10786,"children":10789},{"href":10787,"rel":10788},"https://www.fastify.io/docs/latest/Reference/TypeScript/",[326],[10790],{"type":26,"value":10791},"official documentation",{"type":26,"value":10793}," for usage in TypeScript, which in particular shows its really great capability (via an added library) of building TypeScript types out of request validation schemas. What this means is that when using TypeScript, the ",{"type":21,"tag":246,"props":10795,"children":10797},{"className":10796},[],[10798],{"type":26,"value":10799},"request.params",{"type":26,"value":2228},{"type":21,"tag":246,"props":10802,"children":10804},{"className":10803},[],[10805],{"type":26,"value":10806},"request.query",{"type":26,"value":386},{"type":21,"tag":246,"props":10809,"children":10811},{"className":10810},[],[10812],{"type":26,"value":10813},"request.body",{"type":26,"value":10815}," fields will have types matching their validated content, ensuring that the validation code and the application code are in sync.",{"type":21,"tag":80,"props":10817,"children":10819},{"id":10818},"and-more",[10820],{"type":26,"value":10821},"...and more",{"type":21,"tag":2953,"props":10823,"children":10824},{},[10825,10830,10842],{"type":21,"tag":187,"props":10826,"children":10827},{},[10828],{"type":26,"value":10829},"Built-in JSON support such that route handlers don't need to encode/decode JSON manually, which is a nice convenience",{"type":21,"tag":187,"props":10831,"children":10832},{},[10833,10835],{"type":26,"value":10834},"Built-in logging via ",{"type":21,"tag":322,"props":10836,"children":10839},{"href":10837,"rel":10838},"https://github.com/pinojs/pino",[326],[10840],{"type":26,"value":10841},"pino",{"type":21,"tag":187,"props":10843,"children":10844},{},[10845,10847],{"type":26,"value":10846},"Speed, with Fastify performancing much better than Express in ",{"type":21,"tag":322,"props":10848,"children":10851},{"href":10849,"rel":10850},"https://www.fastify.io/benchmarks/",[326],[10852],{"type":26,"value":10853},"benchmarks",{"type":21,"tag":22,"props":10855,"children":10856},{},[10857],{"type":26,"value":10858},"There are many other quality JavaScript Web frameworks to consider, but without going into a full comparison, I don't think they fill the role of Express quite so well as Fastify does:",{"type":21,"tag":2953,"props":10860,"children":10861},{},[10862,10874,10886],{"type":21,"tag":187,"props":10863,"children":10864},{},[10865,10872],{"type":21,"tag":322,"props":10866,"children":10869},{"href":10867,"rel":10868},"https://nestjs.com/",[326],[10870],{"type":26,"value":10871},"NestJS",{"type":26,"value":10873}," is great in its own right as a heavyweight, opinionated framework (which can optionally be based on Fastify), but it's not a good fit for small projects or ones which need a lot of flexibility.",{"type":21,"tag":187,"props":10875,"children":10876},{},[10877,10884],{"type":21,"tag":322,"props":10878,"children":10881},{"href":10879,"rel":10880},"https://koajs.com/",[326],[10882],{"type":26,"value":10883},"Koa",{"type":26,"value":10885}," is great for being very similar to Express while still adding some welcome features, but it doesn't have nearly the feature set of Fastify.",{"type":21,"tag":187,"props":10887,"children":10888},{},[10889,10896],{"type":21,"tag":322,"props":10890,"children":10893},{"href":10891,"rel":10892},"https://adonisjs.com/",[326],[10894],{"type":26,"value":10895},"AdonisJS",{"type":26,"value":10897}," is pretty comparable to Fastify and it's TypeScript-first, but it doesn't have quite the same plugin ecosystem.",{"type":21,"tag":56,"props":10899,"children":10901},{"id":10900},"pros-and-cons-of-migrating-from-express-to-fastify",[10902],{"type":26,"value":10903},"Pros and Cons of Migrating from Express to Fastify",{"type":21,"tag":22,"props":10905,"children":10906},{},[10907],{"type":26,"value":10908},"For a greenfield Node.js project, I would readily choose Fastify over Express; it just gives developers access to so many added features, all in an opt-in fashion so they don't get in the way, in a well-maintained project that feels like it will still be around in five years' time.",{"type":21,"tag":22,"props":10910,"children":10911},{},[10912],{"type":26,"value":10913},"Whether or not to migrate an existing Express project to Fastify, however, is a more difficult question.",{"type":21,"tag":22,"props":10915,"children":10916},{},[10917,10919,10928],{"type":26,"value":10918},"Such a migration doesn't need to happen in a single step; there's a ",{"type":21,"tag":322,"props":10920,"children":10922},{"href":2885,"rel":10921},[326],[10923],{"type":21,"tag":246,"props":10924,"children":10926},{"className":10925},[],[10927],{"type":26,"value":2893},{"type":26,"value":10929}," plugin which allows an entire Express router to be loaded inside of a Fastify application, allowing both to exist simultaneously for a time. The plugin documentation advises against using it as a long-term solution, so it is intended to be part of a full migration and not for maintaining a set of legacy Express routes long-term. Still, Fastify is different enough from Express that a migration is time-consuming, and differences in its request and response objects means that application code in the route handlers needs to be touched as well.",{"type":21,"tag":22,"props":10931,"children":10932},{},[10933,10935,10940],{"type":26,"value":10934},"While Fastify provides a ton of added functionality, almost all that functionality can be tacked on to Express via third-party libraries, so there's little reason why a project would flat-out ",{"type":21,"tag":33,"props":10936,"children":10937},{},[10938],{"type":26,"value":10939},"need",{"type":26,"value":10941}," to migrate. Express gets the job done; unless the project has a long life ahead, good test coverage to cover a migration, and tens of hours to spare to implement it, it's probably best to stick with Express.",{"type":21,"tag":22,"props":10943,"children":10944},{},[10945,10947,10953,10955,10960],{"type":26,"value":10946},"...That said, in order to come to that conlusion I needed to see just how easy (or hard) it was to migrate a project, so that's just what I did. In Part 2 we'll look at the process of converting an ",{"type":21,"tag":322,"props":10948,"children":10950},{"href":2838,"rel":10949},[326],[10951],{"type":26,"value":10952},"Express implementation",{"type":26,"value":10954}," of the ",{"type":21,"tag":322,"props":10956,"children":10958},{"href":2851,"rel":10957},[326],[10959],{"type":26,"value":2855},{"type":26,"value":10961},", to get an idea of what it looks like to migrate a non-trivial application to Fastify.",{"title":8,"searchDepth":333,"depth":333,"links":10963},[10964,10975],{"id":10527,"depth":336,"text":10530,"children":10965},[10966,10967,10968,10970,10971,10972,10973,10974],{"id":10543,"depth":333,"text":10546},{"id":10562,"depth":333,"text":10565},{"id":10582,"depth":333,"text":10969},"Built-in async/await support",{"id":10651,"depth":333,"text":10654},{"id":10688,"depth":333,"text":10691},{"id":10708,"depth":333,"text":10711},{"id":10771,"depth":333,"text":10774},{"id":10818,"depth":333,"text":10821},{"id":10900,"depth":336,"text":10903},"content:phendry:2022-07-21:MigratingFromExpressToFastifyPart1.md","phendry/2022-07-21/MigratingFromExpressToFastifyPart1.md","phendry/2022-07-21/MigratingFromExpressToFastifyPart1",{"user":2795,"name":2796},{"_path":10981,"_dir":10982,"_draft":7,"_partial":7,"_locale":8,"title":10983,"description":10984,"tags":10985,"image":10986,"publishDate":10982,"excerpt":10984,"body":10987,"_type":343,"_id":14337,"_source":345,"_file":14338,"_stem":14339,"_extension":348,"author":14340},"/phendry/2021-10-30/spotthevulnloopsandtermconditions","2021-10-30","Spot the Vulnerability: Loops and Terminating Conditions","In memory-unsafe languages like C, special care must be taken when copying untrusted data, particularly when copying it to another buffer. In this post, we'll spot and mitigate a past vulnerability in Linux's NTP daemon.",[1302,15],"/phendry/2021-10-30/img/vulnerability-2.jpg",{"type":18,"children":10988,"toc":14330},[10989,11010,11016,11021,11034,11227,11274,11279,11285,11290,11295,11349,12979,12984,12990,13016,13050,13055,13360,13392,13571,13576,13708,13713,13950,13991,14004,14016,14022,14034,14281,14286,14292,14326],{"type":21,"tag":22,"props":10990,"children":10991},{},[10992,10993,11000,11002,11009],{"type":26,"value":6526},{"type":21,"tag":322,"props":10994,"children":10997},{"href":10995,"rel":10996},"https://www.abetterinternet.org/docs/memory-safety/",[326],[10998],{"type":26,"value":10999},"memory-unsafe",{"type":26,"value":11001}," languages like C, special care must be taken when copying untrusted data, particularly when copying it to another buffer. In this post, we'll spot and mitigate a past vulnerability in Linux's ",{"type":21,"tag":322,"props":11003,"children":11006},{"href":11004,"rel":11005},"https://linux.die.net/man/8/ntpd",[326],[11007],{"type":26,"value":11008},"NTP daemon",{"type":26,"value":551},{"type":21,"tag":56,"props":11011,"children":11013},{"id":11012},"background-buffer-overflows-and-loop-termination",[11014],{"type":26,"value":11015},"Background: Buffer Overflows and Loop Termination",{"type":21,"tag":22,"props":11017,"children":11018},{},[11019],{"type":26,"value":11020},"A buffer overflow occurs when code which intends to write data to a buffer inadvertently writes beyond that buffer into an adjacent memory location. At best this might just invalidate the program's state and cause a crash or other unintended behaviour, but a malicious exploitation of a buffer overflow can often lead to protected data being overwritten by an attacker, leading to denial-of-service or arbitrary code execution.",{"type":21,"tag":22,"props":11022,"children":11023},{},[11024,11026,11032],{"type":26,"value":11025},"A common cause for buffer overflows is a looping construct whose terminating conditions are either invalid or missing. The simplest example of this is a ",{"type":21,"tag":246,"props":11027,"children":11029},{"className":11028},[],[11030],{"type":26,"value":11031},"strcpy()",{"type":26,"value":11033},"-style function like the following, intended to copy a string between source and destination buffers:",{"type":21,"tag":239,"props":11035,"children":11039},{"className":11036,"code":11037,"language":11038,"meta":8,"style":8},"language-c shiki shiki-themes github-light github-dark","char * copy(char *dst, char *src) {\n    char *dst_start = dst;\n\n    while((*dst++ = *src++) != '\\0');\n\n    return dst_start;\n}\n","c",[11040],{"type":21,"tag":246,"props":11041,"children":11042},{"__ignoreMap":8},[11043,11099,11125,11132,11201,11208,11220],{"type":21,"tag":455,"props":11044,"children":11045},{"class":457,"line":458},[11046,11051,11056,11061,11065,11069,11073,11078,11082,11086,11090,11095],{"type":21,"tag":455,"props":11047,"children":11048},{"style":1349},[11049],{"type":26,"value":11050},"char",{"type":21,"tag":455,"props":11052,"children":11053},{"style":1349},[11054],{"type":26,"value":11055}," *",{"type":21,"tag":455,"props":11057,"children":11058},{"style":1365},[11059],{"type":26,"value":11060}," copy",{"type":21,"tag":455,"props":11062,"children":11063},{"style":486},[11064],{"type":26,"value":489},{"type":21,"tag":455,"props":11066,"children":11067},{"style":1349},[11068],{"type":26,"value":11050},{"type":21,"tag":455,"props":11070,"children":11071},{"style":1349},[11072],{"type":26,"value":11055},{"type":21,"tag":455,"props":11074,"children":11075},{"style":4527},[11076],{"type":26,"value":11077},"dst",{"type":21,"tag":455,"props":11079,"children":11080},{"style":486},[11081],{"type":26,"value":2228},{"type":21,"tag":455,"props":11083,"children":11084},{"style":1349},[11085],{"type":26,"value":11050},{"type":21,"tag":455,"props":11087,"children":11088},{"style":1349},[11089],{"type":26,"value":11055},{"type":21,"tag":455,"props":11091,"children":11092},{"style":4527},[11093],{"type":26,"value":11094},"src",{"type":21,"tag":455,"props":11096,"children":11097},{"style":486},[11098],{"type":26,"value":4879},{"type":21,"tag":455,"props":11100,"children":11101},{"class":457,"line":336},[11102,11107,11111,11116,11120],{"type":21,"tag":455,"props":11103,"children":11104},{"style":1349},[11105],{"type":26,"value":11106},"    char",{"type":21,"tag":455,"props":11108,"children":11109},{"style":1349},[11110],{"type":26,"value":11055},{"type":21,"tag":455,"props":11112,"children":11113},{"style":486},[11114],{"type":26,"value":11115},"dst_start ",{"type":21,"tag":455,"props":11117,"children":11118},{"style":1349},[11119],{"type":26,"value":3193},{"type":21,"tag":455,"props":11121,"children":11122},{"style":486},[11123],{"type":26,"value":11124}," dst;\n",{"type":21,"tag":455,"props":11126,"children":11127},{"class":457,"line":333},[11128],{"type":21,"tag":455,"props":11129,"children":11130},{"emptyLinePlaceholder":471},[11131],{"type":26,"value":474},{"type":21,"tag":455,"props":11133,"children":11134},{"class":457,"line":1424},[11135,11140,11144,11148,11152,11157,11161,11165,11169,11173,11177,11182,11187,11192,11197],{"type":21,"tag":455,"props":11136,"children":11137},{"style":1349},[11138],{"type":26,"value":11139},"    while",{"type":21,"tag":455,"props":11141,"children":11142},{"style":486},[11143],{"type":26,"value":4575},{"type":21,"tag":455,"props":11145,"children":11146},{"style":1349},[11147],{"type":26,"value":2161},{"type":21,"tag":455,"props":11149,"children":11150},{"style":486},[11151],{"type":26,"value":11077},{"type":21,"tag":455,"props":11153,"children":11154},{"style":1349},[11155],{"type":26,"value":11156},"++",{"type":21,"tag":455,"props":11158,"children":11159},{"style":1349},[11160],{"type":26,"value":2131},{"type":21,"tag":455,"props":11162,"children":11163},{"style":1349},[11164],{"type":26,"value":11055},{"type":21,"tag":455,"props":11166,"children":11167},{"style":486},[11168],{"type":26,"value":11094},{"type":21,"tag":455,"props":11170,"children":11171},{"style":1349},[11172],{"type":26,"value":11156},{"type":21,"tag":455,"props":11174,"children":11175},{"style":486},[11176],{"type":26,"value":4584},{"type":21,"tag":455,"props":11178,"children":11179},{"style":1349},[11180],{"type":26,"value":11181},"!=",{"type":21,"tag":455,"props":11183,"children":11184},{"style":492},[11185],{"type":26,"value":11186}," '",{"type":21,"tag":455,"props":11188,"children":11189},{"style":480},[11190],{"type":26,"value":11191},"\\0",{"type":21,"tag":455,"props":11193,"children":11194},{"style":492},[11195],{"type":26,"value":11196},"'",{"type":21,"tag":455,"props":11198,"children":11199},{"style":486},[11200],{"type":26,"value":2171},{"type":21,"tag":455,"props":11202,"children":11203},{"class":457,"line":1443},[11204],{"type":21,"tag":455,"props":11205,"children":11206},{"emptyLinePlaceholder":471},[11207],{"type":26,"value":474},{"type":21,"tag":455,"props":11209,"children":11210},{"class":457,"line":1489},[11211,11215],{"type":21,"tag":455,"props":11212,"children":11213},{"style":1349},[11214],{"type":26,"value":1984},{"type":21,"tag":455,"props":11216,"children":11217},{"style":486},[11218],{"type":26,"value":11219}," dst_start;\n",{"type":21,"tag":455,"props":11221,"children":11222},{"class":457,"line":1506},[11223],{"type":21,"tag":455,"props":11224,"children":11225},{"style":486},[11226],{"type":26,"value":2002},{"type":21,"tag":22,"props":11228,"children":11229},{},[11230,11232,11237,11238,11243,11245,11251,11253,11258,11260,11265,11267,11272],{"type":26,"value":11231},"The above loop copies from ",{"type":21,"tag":246,"props":11233,"children":11235},{"className":11234},[],[11236],{"type":26,"value":11094},{"type":26,"value":988},{"type":21,"tag":246,"props":11239,"children":11241},{"className":11240},[],[11242],{"type":26,"value":11077},{"type":26,"value":11244}," until it sees a ",{"type":21,"tag":246,"props":11246,"children":11248},{"className":11247},[],[11249],{"type":26,"value":11250},"NULL",{"type":26,"value":11252}," character, with no bearing on the size of the ",{"type":21,"tag":246,"props":11254,"children":11256},{"className":11255},[],[11257],{"type":26,"value":11077},{"type":26,"value":11259}," buffer. If ",{"type":21,"tag":246,"props":11261,"children":11263},{"className":11262},[],[11264],{"type":26,"value":11094},{"type":26,"value":11266}," is larger than ",{"type":21,"tag":246,"props":11268,"children":11270},{"className":11269},[],[11271],{"type":26,"value":11077},{"type":26,"value":11273},", this will overrun the buffer.",{"type":21,"tag":22,"props":11275,"children":11276},{},[11277],{"type":26,"value":11278},"While the buffer overflow potential of the above is easy to recognize, it can be much more difficult when complex application logic is mixed in.",{"type":21,"tag":56,"props":11280,"children":11282},{"id":11281},"processing-a-packet-in-ntpd",[11283],{"type":26,"value":11284},"Processing a Packet in NTPD",{"type":21,"tag":22,"props":11286,"children":11287},{},[11288],{"type":26,"value":11289},"What follows is more-or-less exactly the vulnerable NTPD code, presented in its entirety to help demonstrate how tricky it can be to assess complex loops for correctness.",{"type":21,"tag":22,"props":11291,"children":11292},{},[11293],{"type":26,"value":11294},"In the code below,",{"type":21,"tag":2953,"props":11296,"children":11297},{},[11298,11309,11320,11331],{"type":21,"tag":187,"props":11299,"children":11300},{},[11301,11307],{"type":21,"tag":246,"props":11302,"children":11304},{"className":11303},[],[11305],{"type":26,"value":11306},"ctl_getitem()",{"type":26,"value":11308}," is a function which gets the next data item from an incoming NTP packet,",{"type":21,"tag":187,"props":11310,"children":11311},{},[11312,11318],{"type":21,"tag":246,"props":11313,"children":11315},{"className":11314},[],[11316],{"type":26,"value":11317},"buf",{"type":26,"value":11319}," is the 128-character buffer into which packet data is written,",{"type":21,"tag":187,"props":11321,"children":11322},{},[11323,11329],{"type":21,"tag":246,"props":11324,"children":11326},{"className":11325},[],[11327],{"type":26,"value":11328},"cp",{"type":26,"value":11330}," is a pointer into the input data (used during the copy), and",{"type":21,"tag":187,"props":11332,"children":11333},{},[11334,11340,11342,11347],{"type":21,"tag":246,"props":11335,"children":11337},{"className":11336},[],[11338],{"type":26,"value":11339},"tp",{"type":26,"value":11341}," is a pointer into ",{"type":21,"tag":246,"props":11343,"children":11345},{"className":11344},[],[11346],{"type":26,"value":11317},{"type":26,"value":11348}," (also used during the copy).",{"type":21,"tag":239,"props":11350,"children":11352},{"className":11036,"code":11351,"language":11038,"meta":8,"style":8},"static struct ctl_var *\nctl_getitem(\n    struct ctl_var *var_list,\n    char **data\n)\n{\n    register struct ctl_var *v;\n    register char *cp;\n    register char *tp;\n    static struct ctl_var eol = { 0, EOV, };\n    static char buf[128];\n\n    /*\n     * Delete leading commas and white space\n     */\n    while (reqpt \u003C reqend && (*reqpt == ',' || isspace((int)*reqpt))) {\n        reqpt++;\n    }\n\n    if (reqpt >= reqend)\n        return 0;\n\n    if (var_list == (struct c tl_var *)0)\n        return &eol;\n\n    /*\n     * Look for a first character match on the tag. If we find one, see if it is a full match.\n     */\n    v = var_list;\n    cp = reqpt;\n    while (!(v->flags & EOV)) {\n        if (!(v->flags & PADDING) && *cp == *(v->text)) {\n            tp = v->text;\n            while (*tp != '\\0' && *tp != '=' && cp \u003C reqend && *cp == *tp) {\n                cp++;\n                tp++;\n            }\n\n            if ((*tp == '\\0') || (*tp == '=')) {\n                while (cp \u003C reqend && isspace((int)*cp))\n                    cp++;\n\n                if (cp == reqend || *cp == ',') {\n                    buf[0] = '\\0';\n                    *data = buf;\n                    if (cp \u003C reqend)\n                        cp++;\n                    reqpt = cp;\n                    return v;\n                }\n\n                if (*cp == '=') {\n                    cp++;\n                    tp = buf;\n                    while (cp \u003C reqend && isspace((int)*cp))\n                        cp++;\n\n                    while (cp \u003C reqend && *cp != ',')\n                        *tp++ = *cp++;\n                    if (cp \u003C reqend)\n                        cp++;\n                    *tp = '\\0';\n                    while (isspace((int)(*(tp - 1))))\n                        *(--tp) = '\\0';\n\n                    reqpt = cp;\n                    *data = buf;\n                    return v;\n                }\n            }\n            cp = reqpt;\n        }\n        v++;\n    }\n    return v;\n}\n",[11353],{"type":21,"tag":246,"props":11354,"children":11355},{"__ignoreMap":8},[11356,11379,11392,11417,11434,11441,11448,11473,11494,11514,11549,11580,11587,11595,11603,11611,11691,11707,11714,11721,11742,11757,11764,11810,11827,11834,11841,11849,11856,11873,11890,11920,11974,11991,12091,12107,12123,12131,12138,12209,12259,12275,12282,12326,12366,12388,12408,12424,12441,12454,12462,12469,12500,12515,12531,12579,12594,12601,12644,12680,12699,12714,12745,12795,12837,12845,12861,12881,12893,12901,12909,12926,12934,12951,12959,12971],{"type":21,"tag":455,"props":11357,"children":11358},{"class":457,"line":458},[11359,11364,11369,11374],{"type":21,"tag":455,"props":11360,"children":11361},{"style":1349},[11362],{"type":26,"value":11363},"static",{"type":21,"tag":455,"props":11365,"children":11366},{"style":1349},[11367],{"type":26,"value":11368}," struct",{"type":21,"tag":455,"props":11370,"children":11371},{"style":486},[11372],{"type":26,"value":11373}," ctl_var ",{"type":21,"tag":455,"props":11375,"children":11376},{"style":1349},[11377],{"type":26,"value":11378},"*\n",{"type":21,"tag":455,"props":11380,"children":11381},{"class":457,"line":336},[11382,11387],{"type":21,"tag":455,"props":11383,"children":11384},{"style":1365},[11385],{"type":26,"value":11386},"ctl_getitem",{"type":21,"tag":455,"props":11388,"children":11389},{"style":486},[11390],{"type":26,"value":11391},"(\n",{"type":21,"tag":455,"props":11393,"children":11394},{"class":457,"line":333},[11395,11400,11404,11408,11413],{"type":21,"tag":455,"props":11396,"children":11397},{"style":1349},[11398],{"type":26,"value":11399},"    struct",{"type":21,"tag":455,"props":11401,"children":11402},{"style":486},[11403],{"type":26,"value":11373},{"type":21,"tag":455,"props":11405,"children":11406},{"style":1349},[11407],{"type":26,"value":2161},{"type":21,"tag":455,"props":11409,"children":11410},{"style":4527},[11411],{"type":26,"value":11412},"var_list",{"type":21,"tag":455,"props":11414,"children":11415},{"style":486},[11416],{"type":26,"value":2483},{"type":21,"tag":455,"props":11418,"children":11419},{"class":457,"line":1424},[11420,11424,11429],{"type":21,"tag":455,"props":11421,"children":11422},{"style":1349},[11423],{"type":26,"value":11106},{"type":21,"tag":455,"props":11425,"children":11426},{"style":1349},[11427],{"type":26,"value":11428}," **",{"type":21,"tag":455,"props":11430,"children":11431},{"style":486},[11432],{"type":26,"value":11433},"data\n",{"type":21,"tag":455,"props":11435,"children":11436},{"class":457,"line":1443},[11437],{"type":21,"tag":455,"props":11438,"children":11439},{"style":486},[11440],{"type":26,"value":500},{"type":21,"tag":455,"props":11442,"children":11443},{"class":457,"line":1489},[11444],{"type":21,"tag":455,"props":11445,"children":11446},{"style":486},[11447],{"type":26,"value":1394},{"type":21,"tag":455,"props":11449,"children":11450},{"class":457,"line":1506},[11451,11456,11460,11464,11468],{"type":21,"tag":455,"props":11452,"children":11453},{"style":1349},[11454],{"type":26,"value":11455},"    register",{"type":21,"tag":455,"props":11457,"children":11458},{"style":1349},[11459],{"type":26,"value":11368},{"type":21,"tag":455,"props":11461,"children":11462},{"style":486},[11463],{"type":26,"value":11373},{"type":21,"tag":455,"props":11465,"children":11466},{"style":1349},[11467],{"type":26,"value":2161},{"type":21,"tag":455,"props":11469,"children":11470},{"style":486},[11471],{"type":26,"value":11472},"v;\n",{"type":21,"tag":455,"props":11474,"children":11475},{"class":457,"line":1547},[11476,11480,11485,11489],{"type":21,"tag":455,"props":11477,"children":11478},{"style":1349},[11479],{"type":26,"value":11455},{"type":21,"tag":455,"props":11481,"children":11482},{"style":1349},[11483],{"type":26,"value":11484}," char",{"type":21,"tag":455,"props":11486,"children":11487},{"style":1349},[11488],{"type":26,"value":11055},{"type":21,"tag":455,"props":11490,"children":11491},{"style":486},[11492],{"type":26,"value":11493},"cp;\n",{"type":21,"tag":455,"props":11495,"children":11496},{"class":457,"line":1564},[11497,11501,11505,11509],{"type":21,"tag":455,"props":11498,"children":11499},{"style":1349},[11500],{"type":26,"value":11455},{"type":21,"tag":455,"props":11502,"children":11503},{"style":1349},[11504],{"type":26,"value":11484},{"type":21,"tag":455,"props":11506,"children":11507},{"style":1349},[11508],{"type":26,"value":11055},{"type":21,"tag":455,"props":11510,"children":11511},{"style":486},[11512],{"type":26,"value":11513},"tp;\n",{"type":21,"tag":455,"props":11515,"children":11516},{"class":457,"line":1605},[11517,11522,11526,11531,11535,11540,11544],{"type":21,"tag":455,"props":11518,"children":11519},{"style":1349},[11520],{"type":26,"value":11521},"    static",{"type":21,"tag":455,"props":11523,"children":11524},{"style":1349},[11525],{"type":26,"value":11368},{"type":21,"tag":455,"props":11527,"children":11528},{"style":486},[11529],{"type":26,"value":11530}," ctl_var eol ",{"type":21,"tag":455,"props":11532,"children":11533},{"style":1349},[11534],{"type":26,"value":3193},{"type":21,"tag":455,"props":11536,"children":11537},{"style":486},[11538],{"type":26,"value":11539}," { ",{"type":21,"tag":455,"props":11541,"children":11542},{"style":480},[11543],{"type":26,"value":7863},{"type":21,"tag":455,"props":11545,"children":11546},{"style":486},[11547],{"type":26,"value":11548},", EOV, };\n",{"type":21,"tag":455,"props":11550,"children":11551},{"class":457,"line":1622},[11552,11556,11560,11565,11570,11575],{"type":21,"tag":455,"props":11553,"children":11554},{"style":1349},[11555],{"type":26,"value":11521},{"type":21,"tag":455,"props":11557,"children":11558},{"style":1349},[11559],{"type":26,"value":11484},{"type":21,"tag":455,"props":11561,"children":11562},{"style":4527},[11563],{"type":26,"value":11564}," buf",{"type":21,"tag":455,"props":11566,"children":11567},{"style":486},[11568],{"type":26,"value":11569},"[",{"type":21,"tag":455,"props":11571,"children":11572},{"style":480},[11573],{"type":26,"value":11574},"128",{"type":21,"tag":455,"props":11576,"children":11577},{"style":486},[11578],{"type":26,"value":11579},"];\n",{"type":21,"tag":455,"props":11581,"children":11582},{"class":457,"line":1663},[11583],{"type":21,"tag":455,"props":11584,"children":11585},{"emptyLinePlaceholder":471},[11586],{"type":26,"value":474},{"type":21,"tag":455,"props":11588,"children":11589},{"class":457,"line":1680},[11590],{"type":21,"tag":455,"props":11591,"children":11592},{"style":462},[11593],{"type":26,"value":11594},"    /*\n",{"type":21,"tag":455,"props":11596,"children":11597},{"class":457,"line":1721},[11598],{"type":21,"tag":455,"props":11599,"children":11600},{"style":462},[11601],{"type":26,"value":11602},"     * Delete leading commas and white space\n",{"type":21,"tag":455,"props":11604,"children":11605},{"class":457,"line":1738},[11606],{"type":21,"tag":455,"props":11607,"children":11608},{"style":462},[11609],{"type":26,"value":11610},"     */\n",{"type":21,"tag":455,"props":11612,"children":11613},{"class":457,"line":1779},[11614,11618,11623,11628,11633,11638,11642,11646,11651,11655,11660,11664,11669,11673,11677,11682,11686],{"type":21,"tag":455,"props":11615,"children":11616},{"style":1349},[11617],{"type":26,"value":11139},{"type":21,"tag":455,"props":11619,"children":11620},{"style":486},[11621],{"type":26,"value":11622}," (reqpt ",{"type":21,"tag":455,"props":11624,"children":11625},{"style":1349},[11626],{"type":26,"value":11627},"\u003C",{"type":21,"tag":455,"props":11629,"children":11630},{"style":486},[11631],{"type":26,"value":11632}," reqend ",{"type":21,"tag":455,"props":11634,"children":11635},{"style":1349},[11636],{"type":26,"value":11637},"&&",{"type":21,"tag":455,"props":11639,"children":11640},{"style":486},[11641],{"type":26,"value":2136},{"type":21,"tag":455,"props":11643,"children":11644},{"style":1349},[11645],{"type":26,"value":2161},{"type":21,"tag":455,"props":11647,"children":11648},{"style":486},[11649],{"type":26,"value":11650},"reqpt ",{"type":21,"tag":455,"props":11652,"children":11653},{"style":1349},[11654],{"type":26,"value":1412},{"type":21,"tag":455,"props":11656,"children":11657},{"style":492},[11658],{"type":26,"value":11659}," ','",{"type":21,"tag":455,"props":11661,"children":11662},{"style":1349},[11663],{"type":26,"value":3883},{"type":21,"tag":455,"props":11665,"children":11666},{"style":1365},[11667],{"type":26,"value":11668}," isspace",{"type":21,"tag":455,"props":11670,"children":11671},{"style":486},[11672],{"type":26,"value":4575},{"type":21,"tag":455,"props":11674,"children":11675},{"style":1349},[11676],{"type":26,"value":2141},{"type":21,"tag":455,"props":11678,"children":11679},{"style":486},[11680],{"type":26,"value":11681},")",{"type":21,"tag":455,"props":11683,"children":11684},{"style":1349},[11685],{"type":26,"value":2161},{"type":21,"tag":455,"props":11687,"children":11688},{"style":486},[11689],{"type":26,"value":11690},"reqpt))) {\n",{"type":21,"tag":455,"props":11692,"children":11693},{"class":457,"line":1796},[11694,11699,11703],{"type":21,"tag":455,"props":11695,"children":11696},{"style":486},[11697],{"type":26,"value":11698},"        reqpt",{"type":21,"tag":455,"props":11700,"children":11701},{"style":1349},[11702],{"type":26,"value":11156},{"type":21,"tag":455,"props":11704,"children":11705},{"style":486},[11706],{"type":26,"value":1440},{"type":21,"tag":455,"props":11708,"children":11709},{"class":457,"line":1837},[11710],{"type":21,"tag":455,"props":11711,"children":11712},{"style":486},[11713],{"type":26,"value":6008},{"type":21,"tag":455,"props":11715,"children":11716},{"class":457,"line":1854},[11717],{"type":21,"tag":455,"props":11718,"children":11719},{"emptyLinePlaceholder":471},[11720],{"type":26,"value":474},{"type":21,"tag":455,"props":11722,"children":11723},{"class":457,"line":1895},[11724,11728,11732,11737],{"type":21,"tag":455,"props":11725,"children":11726},{"style":1349},[11727],{"type":26,"value":1402},{"type":21,"tag":455,"props":11729,"children":11730},{"style":486},[11731],{"type":26,"value":11622},{"type":21,"tag":455,"props":11733,"children":11734},{"style":1349},[11735],{"type":26,"value":11736},">=",{"type":21,"tag":455,"props":11738,"children":11739},{"style":486},[11740],{"type":26,"value":11741}," reqend)\n",{"type":21,"tag":455,"props":11743,"children":11744},{"class":457,"line":1912},[11745,11749,11753],{"type":21,"tag":455,"props":11746,"children":11747},{"style":1349},[11748],{"type":26,"value":1430},{"type":21,"tag":455,"props":11750,"children":11751},{"style":480},[11752],{"type":26,"value":1417},{"type":21,"tag":455,"props":11754,"children":11755},{"style":486},[11756],{"type":26,"value":1440},{"type":21,"tag":455,"props":11758,"children":11759},{"class":457,"line":1953},[11760],{"type":21,"tag":455,"props":11761,"children":11762},{"emptyLinePlaceholder":471},[11763],{"type":26,"value":474},{"type":21,"tag":455,"props":11765,"children":11766},{"class":457,"line":1970},[11767,11771,11776,11780,11784,11789,11794,11798,11802,11806],{"type":21,"tag":455,"props":11768,"children":11769},{"style":1349},[11770],{"type":26,"value":1402},{"type":21,"tag":455,"props":11772,"children":11773},{"style":486},[11774],{"type":26,"value":11775}," (var_list ",{"type":21,"tag":455,"props":11777,"children":11778},{"style":1349},[11779],{"type":26,"value":1412},{"type":21,"tag":455,"props":11781,"children":11782},{"style":486},[11783],{"type":26,"value":2136},{"type":21,"tag":455,"props":11785,"children":11786},{"style":1349},[11787],{"type":26,"value":11788},"struct",{"type":21,"tag":455,"props":11790,"children":11791},{"style":486},[11792],{"type":26,"value":11793}," c tl_var ",{"type":21,"tag":455,"props":11795,"children":11796},{"style":1349},[11797],{"type":26,"value":2161},{"type":21,"tag":455,"props":11799,"children":11800},{"style":486},[11801],{"type":26,"value":11681},{"type":21,"tag":455,"props":11803,"children":11804},{"style":480},[11805],{"type":26,"value":7863},{"type":21,"tag":455,"props":11807,"children":11808},{"style":486},[11809],{"type":26,"value":500},{"type":21,"tag":455,"props":11811,"children":11812},{"class":457,"line":1978},[11813,11817,11822],{"type":21,"tag":455,"props":11814,"children":11815},{"style":1349},[11816],{"type":26,"value":1430},{"type":21,"tag":455,"props":11818,"children":11819},{"style":1349},[11820],{"type":26,"value":11821}," &",{"type":21,"tag":455,"props":11823,"children":11824},{"style":486},[11825],{"type":26,"value":11826},"eol;\n",{"type":21,"tag":455,"props":11828,"children":11829},{"class":457,"line":1996},[11830],{"type":21,"tag":455,"props":11831,"children":11832},{"emptyLinePlaceholder":471},[11833],{"type":26,"value":474},{"type":21,"tag":455,"props":11835,"children":11836},{"class":457,"line":4487},[11837],{"type":21,"tag":455,"props":11838,"children":11839},{"style":462},[11840],{"type":26,"value":11594},{"type":21,"tag":455,"props":11842,"children":11843},{"class":457,"line":4495},[11844],{"type":21,"tag":455,"props":11845,"children":11846},{"style":462},[11847],{"type":26,"value":11848},"     * Look for a first character match on the tag. If we find one, see if it is a full match.\n",{"type":21,"tag":455,"props":11850,"children":11851},{"class":457,"line":4509},[11852],{"type":21,"tag":455,"props":11853,"children":11854},{"style":462},[11855],{"type":26,"value":11610},{"type":21,"tag":455,"props":11857,"children":11858},{"class":457,"line":4561},[11859,11864,11868],{"type":21,"tag":455,"props":11860,"children":11861},{"style":486},[11862],{"type":26,"value":11863},"    v ",{"type":21,"tag":455,"props":11865,"children":11866},{"style":1349},[11867],{"type":26,"value":3193},{"type":21,"tag":455,"props":11869,"children":11870},{"style":486},[11871],{"type":26,"value":11872}," var_list;\n",{"type":21,"tag":455,"props":11874,"children":11875},{"class":457,"line":4596},[11876,11881,11885],{"type":21,"tag":455,"props":11877,"children":11878},{"style":486},[11879],{"type":26,"value":11880},"    cp ",{"type":21,"tag":455,"props":11882,"children":11883},{"style":1349},[11884],{"type":26,"value":3193},{"type":21,"tag":455,"props":11886,"children":11887},{"style":486},[11888],{"type":26,"value":11889}," reqpt;\n",{"type":21,"tag":455,"props":11891,"children":11892},{"class":457,"line":4627},[11893,11897,11901,11905,11910,11915],{"type":21,"tag":455,"props":11894,"children":11895},{"style":1349},[11896],{"type":26,"value":11139},{"type":21,"tag":455,"props":11898,"children":11899},{"style":486},[11900],{"type":26,"value":2136},{"type":21,"tag":455,"props":11902,"children":11903},{"style":1349},[11904],{"type":26,"value":1272},{"type":21,"tag":455,"props":11906,"children":11907},{"style":486},[11908],{"type":26,"value":11909},"(v->flags ",{"type":21,"tag":455,"props":11911,"children":11912},{"style":1349},[11913],{"type":26,"value":11914},"&",{"type":21,"tag":455,"props":11916,"children":11917},{"style":486},[11918],{"type":26,"value":11919}," EOV)) {\n",{"type":21,"tag":455,"props":11921,"children":11922},{"class":457,"line":5942},[11923,11927,11931,11935,11939,11943,11948,11952,11956,11961,11965,11969],{"type":21,"tag":455,"props":11924,"children":11925},{"style":1349},[11926],{"type":26,"value":5627},{"type":21,"tag":455,"props":11928,"children":11929},{"style":486},[11930],{"type":26,"value":2136},{"type":21,"tag":455,"props":11932,"children":11933},{"style":1349},[11934],{"type":26,"value":1272},{"type":21,"tag":455,"props":11936,"children":11937},{"style":486},[11938],{"type":26,"value":11909},{"type":21,"tag":455,"props":11940,"children":11941},{"style":1349},[11942],{"type":26,"value":11914},{"type":21,"tag":455,"props":11944,"children":11945},{"style":486},[11946],{"type":26,"value":11947}," PADDING) ",{"type":21,"tag":455,"props":11949,"children":11950},{"style":1349},[11951],{"type":26,"value":11637},{"type":21,"tag":455,"props":11953,"children":11954},{"style":1349},[11955],{"type":26,"value":11055},{"type":21,"tag":455,"props":11957,"children":11958},{"style":486},[11959],{"type":26,"value":11960},"cp ",{"type":21,"tag":455,"props":11962,"children":11963},{"style":1349},[11964],{"type":26,"value":1412},{"type":21,"tag":455,"props":11966,"children":11967},{"style":1349},[11968],{"type":26,"value":11055},{"type":21,"tag":455,"props":11970,"children":11971},{"style":486},[11972],{"type":26,"value":11973},"(v->text)) {\n",{"type":21,"tag":455,"props":11975,"children":11976},{"class":457,"line":5965},[11977,11982,11986],{"type":21,"tag":455,"props":11978,"children":11979},{"style":486},[11980],{"type":26,"value":11981},"            tp ",{"type":21,"tag":455,"props":11983,"children":11984},{"style":1349},[11985],{"type":26,"value":3193},{"type":21,"tag":455,"props":11987,"children":11988},{"style":486},[11989],{"type":26,"value":11990}," v->text;\n",{"type":21,"tag":455,"props":11992,"children":11993},{"class":457,"line":5983},[11994,11999,12003,12007,12012,12016,12020,12024,12028,12032,12036,12040,12044,12049,12053,12058,12062,12066,12070,12074,12078,12082,12086],{"type":21,"tag":455,"props":11995,"children":11996},{"style":1349},[11997],{"type":26,"value":11998},"            while",{"type":21,"tag":455,"props":12000,"children":12001},{"style":486},[12002],{"type":26,"value":2136},{"type":21,"tag":455,"props":12004,"children":12005},{"style":1349},[12006],{"type":26,"value":2161},{"type":21,"tag":455,"props":12008,"children":12009},{"style":486},[12010],{"type":26,"value":12011},"tp ",{"type":21,"tag":455,"props":12013,"children":12014},{"style":1349},[12015],{"type":26,"value":11181},{"type":21,"tag":455,"props":12017,"children":12018},{"style":492},[12019],{"type":26,"value":11186},{"type":21,"tag":455,"props":12021,"children":12022},{"style":480},[12023],{"type":26,"value":11191},{"type":21,"tag":455,"props":12025,"children":12026},{"style":492},[12027],{"type":26,"value":11196},{"type":21,"tag":455,"props":12029,"children":12030},{"style":1349},[12031],{"type":26,"value":1467},{"type":21,"tag":455,"props":12033,"children":12034},{"style":1349},[12035],{"type":26,"value":11055},{"type":21,"tag":455,"props":12037,"children":12038},{"style":486},[12039],{"type":26,"value":12011},{"type":21,"tag":455,"props":12041,"children":12042},{"style":1349},[12043],{"type":26,"value":11181},{"type":21,"tag":455,"props":12045,"children":12046},{"style":492},[12047],{"type":26,"value":12048}," '='",{"type":21,"tag":455,"props":12050,"children":12051},{"style":1349},[12052],{"type":26,"value":1467},{"type":21,"tag":455,"props":12054,"children":12055},{"style":486},[12056],{"type":26,"value":12057}," cp ",{"type":21,"tag":455,"props":12059,"children":12060},{"style":1349},[12061],{"type":26,"value":11627},{"type":21,"tag":455,"props":12063,"children":12064},{"style":486},[12065],{"type":26,"value":11632},{"type":21,"tag":455,"props":12067,"children":12068},{"style":1349},[12069],{"type":26,"value":11637},{"type":21,"tag":455,"props":12071,"children":12072},{"style":1349},[12073],{"type":26,"value":11055},{"type":21,"tag":455,"props":12075,"children":12076},{"style":486},[12077],{"type":26,"value":11960},{"type":21,"tag":455,"props":12079,"children":12080},{"style":1349},[12081],{"type":26,"value":1412},{"type":21,"tag":455,"props":12083,"children":12084},{"style":1349},[12085],{"type":26,"value":11055},{"type":21,"tag":455,"props":12087,"children":12088},{"style":486},[12089],{"type":26,"value":12090},"tp) {\n",{"type":21,"tag":455,"props":12092,"children":12093},{"class":457,"line":6002},[12094,12099,12103],{"type":21,"tag":455,"props":12095,"children":12096},{"style":486},[12097],{"type":26,"value":12098},"                cp",{"type":21,"tag":455,"props":12100,"children":12101},{"style":1349},[12102],{"type":26,"value":11156},{"type":21,"tag":455,"props":12104,"children":12105},{"style":486},[12106],{"type":26,"value":1440},{"type":21,"tag":455,"props":12108,"children":12109},{"class":457,"line":6011},[12110,12115,12119],{"type":21,"tag":455,"props":12111,"children":12112},{"style":486},[12113],{"type":26,"value":12114},"                tp",{"type":21,"tag":455,"props":12116,"children":12117},{"style":1349},[12118],{"type":26,"value":11156},{"type":21,"tag":455,"props":12120,"children":12121},{"style":486},[12122],{"type":26,"value":1440},{"type":21,"tag":455,"props":12124,"children":12125},{"class":457,"line":6019},[12126],{"type":21,"tag":455,"props":12127,"children":12128},{"style":486},[12129],{"type":26,"value":12130},"            }\n",{"type":21,"tag":455,"props":12132,"children":12133},{"class":457,"line":6028},[12134],{"type":21,"tag":455,"props":12135,"children":12136},{"emptyLinePlaceholder":471},[12137],{"type":26,"value":474},{"type":21,"tag":455,"props":12139,"children":12140},{"class":457,"line":6085},[12141,12146,12151,12155,12159,12163,12167,12171,12175,12179,12184,12188,12192,12196,12200,12204],{"type":21,"tag":455,"props":12142,"children":12143},{"style":1349},[12144],{"type":26,"value":12145},"            if",{"type":21,"tag":455,"props":12147,"children":12148},{"style":486},[12149],{"type":26,"value":12150}," ((",{"type":21,"tag":455,"props":12152,"children":12153},{"style":1349},[12154],{"type":26,"value":2161},{"type":21,"tag":455,"props":12156,"children":12157},{"style":486},[12158],{"type":26,"value":12011},{"type":21,"tag":455,"props":12160,"children":12161},{"style":1349},[12162],{"type":26,"value":1412},{"type":21,"tag":455,"props":12164,"children":12165},{"style":492},[12166],{"type":26,"value":11186},{"type":21,"tag":455,"props":12168,"children":12169},{"style":480},[12170],{"type":26,"value":11191},{"type":21,"tag":455,"props":12172,"children":12173},{"style":492},[12174],{"type":26,"value":11196},{"type":21,"tag":455,"props":12176,"children":12177},{"style":486},[12178],{"type":26,"value":4584},{"type":21,"tag":455,"props":12180,"children":12181},{"style":1349},[12182],{"type":26,"value":12183},"||",{"type":21,"tag":455,"props":12185,"children":12186},{"style":486},[12187],{"type":26,"value":2136},{"type":21,"tag":455,"props":12189,"children":12190},{"style":1349},[12191],{"type":26,"value":2161},{"type":21,"tag":455,"props":12193,"children":12194},{"style":486},[12195],{"type":26,"value":12011},{"type":21,"tag":455,"props":12197,"children":12198},{"style":1349},[12199],{"type":26,"value":1412},{"type":21,"tag":455,"props":12201,"children":12202},{"style":492},[12203],{"type":26,"value":12048},{"type":21,"tag":455,"props":12205,"children":12206},{"style":486},[12207],{"type":26,"value":12208},")) {\n",{"type":21,"tag":455,"props":12210,"children":12211},{"class":457,"line":6097},[12212,12217,12222,12226,12230,12234,12238,12242,12246,12250,12254],{"type":21,"tag":455,"props":12213,"children":12214},{"style":1349},[12215],{"type":26,"value":12216},"                while",{"type":21,"tag":455,"props":12218,"children":12219},{"style":486},[12220],{"type":26,"value":12221}," (cp ",{"type":21,"tag":455,"props":12223,"children":12224},{"style":1349},[12225],{"type":26,"value":11627},{"type":21,"tag":455,"props":12227,"children":12228},{"style":486},[12229],{"type":26,"value":11632},{"type":21,"tag":455,"props":12231,"children":12232},{"style":1349},[12233],{"type":26,"value":11637},{"type":21,"tag":455,"props":12235,"children":12236},{"style":1365},[12237],{"type":26,"value":11668},{"type":21,"tag":455,"props":12239,"children":12240},{"style":486},[12241],{"type":26,"value":4575},{"type":21,"tag":455,"props":12243,"children":12244},{"style":1349},[12245],{"type":26,"value":2141},{"type":21,"tag":455,"props":12247,"children":12248},{"style":486},[12249],{"type":26,"value":11681},{"type":21,"tag":455,"props":12251,"children":12252},{"style":1349},[12253],{"type":26,"value":2161},{"type":21,"tag":455,"props":12255,"children":12256},{"style":486},[12257],{"type":26,"value":12258},"cp))\n",{"type":21,"tag":455,"props":12260,"children":12261},{"class":457,"line":6117},[12262,12267,12271],{"type":21,"tag":455,"props":12263,"children":12264},{"style":486},[12265],{"type":26,"value":12266},"                    cp",{"type":21,"tag":455,"props":12268,"children":12269},{"style":1349},[12270],{"type":26,"value":11156},{"type":21,"tag":455,"props":12272,"children":12273},{"style":486},[12274],{"type":26,"value":1440},{"type":21,"tag":455,"props":12276,"children":12277},{"class":457,"line":6134},[12278],{"type":21,"tag":455,"props":12279,"children":12280},{"emptyLinePlaceholder":471},[12281],{"type":26,"value":474},{"type":21,"tag":455,"props":12283,"children":12284},{"class":457,"line":6142},[12285,12290,12294,12298,12302,12306,12310,12314,12318,12322],{"type":21,"tag":455,"props":12286,"children":12287},{"style":1349},[12288],{"type":26,"value":12289},"                if",{"type":21,"tag":455,"props":12291,"children":12292},{"style":486},[12293],{"type":26,"value":12221},{"type":21,"tag":455,"props":12295,"children":12296},{"style":1349},[12297],{"type":26,"value":1412},{"type":21,"tag":455,"props":12299,"children":12300},{"style":486},[12301],{"type":26,"value":11632},{"type":21,"tag":455,"props":12303,"children":12304},{"style":1349},[12305],{"type":26,"value":12183},{"type":21,"tag":455,"props":12307,"children":12308},{"style":1349},[12309],{"type":26,"value":11055},{"type":21,"tag":455,"props":12311,"children":12312},{"style":486},[12313],{"type":26,"value":11960},{"type":21,"tag":455,"props":12315,"children":12316},{"style":1349},[12317],{"type":26,"value":1412},{"type":21,"tag":455,"props":12319,"children":12320},{"style":492},[12321],{"type":26,"value":11659},{"type":21,"tag":455,"props":12323,"children":12324},{"style":486},[12325],{"type":26,"value":4879},{"type":21,"tag":455,"props":12327,"children":12328},{"class":457,"line":10092},[12329,12334,12338,12342,12346,12350,12354,12358,12362],{"type":21,"tag":455,"props":12330,"children":12331},{"style":4527},[12332],{"type":26,"value":12333},"                    buf",{"type":21,"tag":455,"props":12335,"children":12336},{"style":486},[12337],{"type":26,"value":11569},{"type":21,"tag":455,"props":12339,"children":12340},{"style":480},[12341],{"type":26,"value":7863},{"type":21,"tag":455,"props":12343,"children":12344},{"style":486},[12345],{"type":26,"value":5718},{"type":21,"tag":455,"props":12347,"children":12348},{"style":1349},[12349],{"type":26,"value":3193},{"type":21,"tag":455,"props":12351,"children":12352},{"style":492},[12353],{"type":26,"value":11186},{"type":21,"tag":455,"props":12355,"children":12356},{"style":480},[12357],{"type":26,"value":11191},{"type":21,"tag":455,"props":12359,"children":12360},{"style":492},[12361],{"type":26,"value":11196},{"type":21,"tag":455,"props":12363,"children":12364},{"style":486},[12365],{"type":26,"value":1440},{"type":21,"tag":455,"props":12367,"children":12368},{"class":457,"line":10100},[12369,12374,12379,12383],{"type":21,"tag":455,"props":12370,"children":12371},{"style":1349},[12372],{"type":26,"value":12373},"                    *",{"type":21,"tag":455,"props":12375,"children":12376},{"style":486},[12377],{"type":26,"value":12378},"data ",{"type":21,"tag":455,"props":12380,"children":12381},{"style":1349},[12382],{"type":26,"value":3193},{"type":21,"tag":455,"props":12384,"children":12385},{"style":486},[12386],{"type":26,"value":12387}," buf;\n",{"type":21,"tag":455,"props":12389,"children":12390},{"class":457,"line":10109},[12391,12396,12400,12404],{"type":21,"tag":455,"props":12392,"children":12393},{"style":1349},[12394],{"type":26,"value":12395},"                    if",{"type":21,"tag":455,"props":12397,"children":12398},{"style":486},[12399],{"type":26,"value":12221},{"type":21,"tag":455,"props":12401,"children":12402},{"style":1349},[12403],{"type":26,"value":11627},{"type":21,"tag":455,"props":12405,"children":12406},{"style":486},[12407],{"type":26,"value":11741},{"type":21,"tag":455,"props":12409,"children":12410},{"class":457,"line":10118},[12411,12416,12420],{"type":21,"tag":455,"props":12412,"children":12413},{"style":486},[12414],{"type":26,"value":12415},"                        cp",{"type":21,"tag":455,"props":12417,"children":12418},{"style":1349},[12419],{"type":26,"value":11156},{"type":21,"tag":455,"props":12421,"children":12422},{"style":486},[12423],{"type":26,"value":1440},{"type":21,"tag":455,"props":12425,"children":12426},{"class":457,"line":10136},[12427,12432,12436],{"type":21,"tag":455,"props":12428,"children":12429},{"style":486},[12430],{"type":26,"value":12431},"                    reqpt ",{"type":21,"tag":455,"props":12433,"children":12434},{"style":1349},[12435],{"type":26,"value":3193},{"type":21,"tag":455,"props":12437,"children":12438},{"style":486},[12439],{"type":26,"value":12440}," cp;\n",{"type":21,"tag":455,"props":12442,"children":12443},{"class":457,"line":10154},[12444,12449],{"type":21,"tag":455,"props":12445,"children":12446},{"style":1349},[12447],{"type":26,"value":12448},"                    return",{"type":21,"tag":455,"props":12450,"children":12451},{"style":486},[12452],{"type":26,"value":12453}," v;\n",{"type":21,"tag":455,"props":12455,"children":12456},{"class":457,"line":10172},[12457],{"type":21,"tag":455,"props":12458,"children":12459},{"style":486},[12460],{"type":26,"value":12461},"                }\n",{"type":21,"tag":455,"props":12463,"children":12464},{"class":457,"line":10180},[12465],{"type":21,"tag":455,"props":12466,"children":12467},{"emptyLinePlaceholder":471},[12468],{"type":26,"value":474},{"type":21,"tag":455,"props":12470,"children":12471},{"class":457,"line":10188},[12472,12476,12480,12484,12488,12492,12496],{"type":21,"tag":455,"props":12473,"children":12474},{"style":1349},[12475],{"type":26,"value":12289},{"type":21,"tag":455,"props":12477,"children":12478},{"style":486},[12479],{"type":26,"value":2136},{"type":21,"tag":455,"props":12481,"children":12482},{"style":1349},[12483],{"type":26,"value":2161},{"type":21,"tag":455,"props":12485,"children":12486},{"style":486},[12487],{"type":26,"value":11960},{"type":21,"tag":455,"props":12489,"children":12490},{"style":1349},[12491],{"type":26,"value":1412},{"type":21,"tag":455,"props":12493,"children":12494},{"style":492},[12495],{"type":26,"value":12048},{"type":21,"tag":455,"props":12497,"children":12498},{"style":486},[12499],{"type":26,"value":4879},{"type":21,"tag":455,"props":12501,"children":12502},{"class":457,"line":10197},[12503,12507,12511],{"type":21,"tag":455,"props":12504,"children":12505},{"style":486},[12506],{"type":26,"value":12266},{"type":21,"tag":455,"props":12508,"children":12509},{"style":1349},[12510],{"type":26,"value":11156},{"type":21,"tag":455,"props":12512,"children":12513},{"style":486},[12514],{"type":26,"value":1440},{"type":21,"tag":455,"props":12516,"children":12517},{"class":457,"line":10205},[12518,12523,12527],{"type":21,"tag":455,"props":12519,"children":12520},{"style":486},[12521],{"type":26,"value":12522},"                    tp ",{"type":21,"tag":455,"props":12524,"children":12525},{"style":1349},[12526],{"type":26,"value":3193},{"type":21,"tag":455,"props":12528,"children":12529},{"style":486},[12530],{"type":26,"value":12387},{"type":21,"tag":455,"props":12532,"children":12533},{"class":457,"line":10214},[12534,12539,12543,12547,12551,12555,12559,12563,12567,12571,12575],{"type":21,"tag":455,"props":12535,"children":12536},{"style":1349},[12537],{"type":26,"value":12538},"                    while",{"type":21,"tag":455,"props":12540,"children":12541},{"style":486},[12542],{"type":26,"value":12221},{"type":21,"tag":455,"props":12544,"children":12545},{"style":1349},[12546],{"type":26,"value":11627},{"type":21,"tag":455,"props":12548,"children":12549},{"style":486},[12550],{"type":26,"value":11632},{"type":21,"tag":455,"props":12552,"children":12553},{"style":1349},[12554],{"type":26,"value":11637},{"type":21,"tag":455,"props":12556,"children":12557},{"style":1365},[12558],{"type":26,"value":11668},{"type":21,"tag":455,"props":12560,"children":12561},{"style":486},[12562],{"type":26,"value":4575},{"type":21,"tag":455,"props":12564,"children":12565},{"style":1349},[12566],{"type":26,"value":2141},{"type":21,"tag":455,"props":12568,"children":12569},{"style":486},[12570],{"type":26,"value":11681},{"type":21,"tag":455,"props":12572,"children":12573},{"style":1349},[12574],{"type":26,"value":2161},{"type":21,"tag":455,"props":12576,"children":12577},{"style":486},[12578],{"type":26,"value":12258},{"type":21,"tag":455,"props":12580,"children":12581},{"class":457,"line":10223},[12582,12586,12590],{"type":21,"tag":455,"props":12583,"children":12584},{"style":486},[12585],{"type":26,"value":12415},{"type":21,"tag":455,"props":12587,"children":12588},{"style":1349},[12589],{"type":26,"value":11156},{"type":21,"tag":455,"props":12591,"children":12592},{"style":486},[12593],{"type":26,"value":1440},{"type":21,"tag":455,"props":12595,"children":12596},{"class":457,"line":10260},[12597],{"type":21,"tag":455,"props":12598,"children":12599},{"emptyLinePlaceholder":471},[12600],{"type":26,"value":474},{"type":21,"tag":455,"props":12602,"children":12603},{"class":457,"line":10290},[12604,12608,12612,12616,12620,12624,12628,12632,12636,12640],{"type":21,"tag":455,"props":12605,"children":12606},{"style":1349},[12607],{"type":26,"value":12538},{"type":21,"tag":455,"props":12609,"children":12610},{"style":486},[12611],{"type":26,"value":12221},{"type":21,"tag":455,"props":12613,"children":12614},{"style":1349},[12615],{"type":26,"value":11627},{"type":21,"tag":455,"props":12617,"children":12618},{"style":486},[12619],{"type":26,"value":11632},{"type":21,"tag":455,"props":12621,"children":12622},{"style":1349},[12623],{"type":26,"value":11637},{"type":21,"tag":455,"props":12625,"children":12626},{"style":1349},[12627],{"type":26,"value":11055},{"type":21,"tag":455,"props":12629,"children":12630},{"style":486},[12631],{"type":26,"value":11960},{"type":21,"tag":455,"props":12633,"children":12634},{"style":1349},[12635],{"type":26,"value":11181},{"type":21,"tag":455,"props":12637,"children":12638},{"style":492},[12639],{"type":26,"value":11659},{"type":21,"tag":455,"props":12641,"children":12642},{"style":486},[12643],{"type":26,"value":500},{"type":21,"tag":455,"props":12645,"children":12646},{"class":457,"line":10326},[12647,12652,12656,12660,12664,12668,12672,12676],{"type":21,"tag":455,"props":12648,"children":12649},{"style":1349},[12650],{"type":26,"value":12651},"                        *",{"type":21,"tag":455,"props":12653,"children":12654},{"style":486},[12655],{"type":26,"value":11339},{"type":21,"tag":455,"props":12657,"children":12658},{"style":1349},[12659],{"type":26,"value":11156},{"type":21,"tag":455,"props":12661,"children":12662},{"style":1349},[12663],{"type":26,"value":2131},{"type":21,"tag":455,"props":12665,"children":12666},{"style":1349},[12667],{"type":26,"value":11055},{"type":21,"tag":455,"props":12669,"children":12670},{"style":486},[12671],{"type":26,"value":11328},{"type":21,"tag":455,"props":12673,"children":12674},{"style":1349},[12675],{"type":26,"value":11156},{"type":21,"tag":455,"props":12677,"children":12678},{"style":486},[12679],{"type":26,"value":1440},{"type":21,"tag":455,"props":12681,"children":12682},{"class":457,"line":10361},[12683,12687,12691,12695],{"type":21,"tag":455,"props":12684,"children":12685},{"style":1349},[12686],{"type":26,"value":12395},{"type":21,"tag":455,"props":12688,"children":12689},{"style":486},[12690],{"type":26,"value":12221},{"type":21,"tag":455,"props":12692,"children":12693},{"style":1349},[12694],{"type":26,"value":11627},{"type":21,"tag":455,"props":12696,"children":12697},{"style":486},[12698],{"type":26,"value":11741},{"type":21,"tag":455,"props":12700,"children":12701},{"class":457,"line":10395},[12702,12706,12710],{"type":21,"tag":455,"props":12703,"children":12704},{"style":486},[12705],{"type":26,"value":12415},{"type":21,"tag":455,"props":12707,"children":12708},{"style":1349},[12709],{"type":26,"value":11156},{"type":21,"tag":455,"props":12711,"children":12712},{"style":486},[12713],{"type":26,"value":1440},{"type":21,"tag":455,"props":12715,"children":12716},{"class":457,"line":10429},[12717,12721,12725,12729,12733,12737,12741],{"type":21,"tag":455,"props":12718,"children":12719},{"style":1349},[12720],{"type":26,"value":12373},{"type":21,"tag":455,"props":12722,"children":12723},{"style":486},[12724],{"type":26,"value":12011},{"type":21,"tag":455,"props":12726,"children":12727},{"style":1349},[12728],{"type":26,"value":3193},{"type":21,"tag":455,"props":12730,"children":12731},{"style":492},[12732],{"type":26,"value":11186},{"type":21,"tag":455,"props":12734,"children":12735},{"style":480},[12736],{"type":26,"value":11191},{"type":21,"tag":455,"props":12738,"children":12739},{"style":492},[12740],{"type":26,"value":11196},{"type":21,"tag":455,"props":12742,"children":12743},{"style":486},[12744],{"type":26,"value":1440},{"type":21,"tag":455,"props":12746,"children":12747},{"class":457,"line":10437},[12748,12752,12756,12761,12765,12769,12773,12777,12782,12786,12790],{"type":21,"tag":455,"props":12749,"children":12750},{"style":1349},[12751],{"type":26,"value":12538},{"type":21,"tag":455,"props":12753,"children":12754},{"style":486},[12755],{"type":26,"value":2136},{"type":21,"tag":455,"props":12757,"children":12758},{"style":1365},[12759],{"type":26,"value":12760},"isspace",{"type":21,"tag":455,"props":12762,"children":12763},{"style":486},[12764],{"type":26,"value":4575},{"type":21,"tag":455,"props":12766,"children":12767},{"style":1349},[12768],{"type":26,"value":2141},{"type":21,"tag":455,"props":12770,"children":12771},{"style":486},[12772],{"type":26,"value":3478},{"type":21,"tag":455,"props":12774,"children":12775},{"style":1349},[12776],{"type":26,"value":2161},{"type":21,"tag":455,"props":12778,"children":12779},{"style":486},[12780],{"type":26,"value":12781},"(tp ",{"type":21,"tag":455,"props":12783,"children":12784},{"style":1349},[12785],{"type":26,"value":2702},{"type":21,"tag":455,"props":12787,"children":12788},{"style":480},[12789],{"type":26,"value":2707},{"type":21,"tag":455,"props":12791,"children":12792},{"style":486},[12793],{"type":26,"value":12794},"))))\n",{"type":21,"tag":455,"props":12796,"children":12798},{"class":457,"line":12797},64,[12799,12803,12807,12812,12817,12821,12825,12829,12833],{"type":21,"tag":455,"props":12800,"children":12801},{"style":1349},[12802],{"type":26,"value":12651},{"type":21,"tag":455,"props":12804,"children":12805},{"style":486},[12806],{"type":26,"value":489},{"type":21,"tag":455,"props":12808,"children":12809},{"style":1349},[12810],{"type":26,"value":12811},"--",{"type":21,"tag":455,"props":12813,"children":12814},{"style":486},[12815],{"type":26,"value":12816},"tp) ",{"type":21,"tag":455,"props":12818,"children":12819},{"style":1349},[12820],{"type":26,"value":3193},{"type":21,"tag":455,"props":12822,"children":12823},{"style":492},[12824],{"type":26,"value":11186},{"type":21,"tag":455,"props":12826,"children":12827},{"style":480},[12828],{"type":26,"value":11191},{"type":21,"tag":455,"props":12830,"children":12831},{"style":492},[12832],{"type":26,"value":11196},{"type":21,"tag":455,"props":12834,"children":12835},{"style":486},[12836],{"type":26,"value":1440},{"type":21,"tag":455,"props":12838,"children":12840},{"class":457,"line":12839},65,[12841],{"type":21,"tag":455,"props":12842,"children":12843},{"emptyLinePlaceholder":471},[12844],{"type":26,"value":474},{"type":21,"tag":455,"props":12846,"children":12848},{"class":457,"line":12847},66,[12849,12853,12857],{"type":21,"tag":455,"props":12850,"children":12851},{"style":486},[12852],{"type":26,"value":12431},{"type":21,"tag":455,"props":12854,"children":12855},{"style":1349},[12856],{"type":26,"value":3193},{"type":21,"tag":455,"props":12858,"children":12859},{"style":486},[12860],{"type":26,"value":12440},{"type":21,"tag":455,"props":12862,"children":12864},{"class":457,"line":12863},67,[12865,12869,12873,12877],{"type":21,"tag":455,"props":12866,"children":12867},{"style":1349},[12868],{"type":26,"value":12373},{"type":21,"tag":455,"props":12870,"children":12871},{"style":486},[12872],{"type":26,"value":12378},{"type":21,"tag":455,"props":12874,"children":12875},{"style":1349},[12876],{"type":26,"value":3193},{"type":21,"tag":455,"props":12878,"children":12879},{"style":486},[12880],{"type":26,"value":12387},{"type":21,"tag":455,"props":12882,"children":12884},{"class":457,"line":12883},68,[12885,12889],{"type":21,"tag":455,"props":12886,"children":12887},{"style":1349},[12888],{"type":26,"value":12448},{"type":21,"tag":455,"props":12890,"children":12891},{"style":486},[12892],{"type":26,"value":12453},{"type":21,"tag":455,"props":12894,"children":12896},{"class":457,"line":12895},69,[12897],{"type":21,"tag":455,"props":12898,"children":12899},{"style":486},[12900],{"type":26,"value":12461},{"type":21,"tag":455,"props":12902,"children":12904},{"class":457,"line":12903},70,[12905],{"type":21,"tag":455,"props":12906,"children":12907},{"style":486},[12908],{"type":26,"value":12130},{"type":21,"tag":455,"props":12910,"children":12912},{"class":457,"line":12911},71,[12913,12918,12922],{"type":21,"tag":455,"props":12914,"children":12915},{"style":486},[12916],{"type":26,"value":12917},"            cp ",{"type":21,"tag":455,"props":12919,"children":12920},{"style":1349},[12921],{"type":26,"value":3193},{"type":21,"tag":455,"props":12923,"children":12924},{"style":486},[12925],{"type":26,"value":11889},{"type":21,"tag":455,"props":12927,"children":12929},{"class":457,"line":12928},72,[12930],{"type":21,"tag":455,"props":12931,"children":12932},{"style":486},[12933],{"type":26,"value":5680},{"type":21,"tag":455,"props":12935,"children":12937},{"class":457,"line":12936},73,[12938,12943,12947],{"type":21,"tag":455,"props":12939,"children":12940},{"style":486},[12941],{"type":26,"value":12942},"        v",{"type":21,"tag":455,"props":12944,"children":12945},{"style":1349},[12946],{"type":26,"value":11156},{"type":21,"tag":455,"props":12948,"children":12949},{"style":486},[12950],{"type":26,"value":1440},{"type":21,"tag":455,"props":12952,"children":12954},{"class":457,"line":12953},74,[12955],{"type":21,"tag":455,"props":12956,"children":12957},{"style":486},[12958],{"type":26,"value":6008},{"type":21,"tag":455,"props":12960,"children":12962},{"class":457,"line":12961},75,[12963,12967],{"type":21,"tag":455,"props":12964,"children":12965},{"style":1349},[12966],{"type":26,"value":1984},{"type":21,"tag":455,"props":12968,"children":12969},{"style":486},[12970],{"type":26,"value":12453},{"type":21,"tag":455,"props":12972,"children":12974},{"class":457,"line":12973},76,[12975],{"type":21,"tag":455,"props":12976,"children":12977},{"style":486},[12978],{"type":26,"value":2002},{"type":21,"tag":22,"props":12980,"children":12981},{},[12982],{"type":26,"value":12983},"Feel free to pause here and try to identify a problem in this function unassisted, otherwise continue on and we'll break it down in detail.",{"type":21,"tag":56,"props":12985,"children":12987},{"id":12986},"spotting-the-vulnerability",[12988],{"type":26,"value":12989},"Spotting the Vulnerability",{"type":21,"tag":22,"props":12991,"children":12992},{},[12993,12995,13000,13002,13007,13009,13014],{"type":26,"value":12994},"Taking the perspective of an attacker is an effective way of switching from the typical development mindset of \"should this work correctly?\" to the mindset of \"can this work ",{"type":21,"tag":33,"props":12996,"children":12997},{},[12998],{"type":26,"value":12999},"incorrectly?",{"type":26,"value":13001},"\" As an attacker who is ",{"type":21,"tag":33,"props":13003,"children":13004},{},[13005],{"type":26,"value":13006},"trying",{"type":26,"value":13008}," to overrun ",{"type":21,"tag":246,"props":13010,"children":13012},{"className":13011},[],[13013],{"type":26,"value":11317},{"type":26,"value":13015},", your goals would be to",{"type":21,"tag":183,"props":13017,"children":13018},{},[13019,13038],{"type":21,"tag":187,"props":13020,"children":13021},{},[13022,13024,13029,13031,13036],{"type":26,"value":13023},"reach the code where ",{"type":21,"tag":246,"props":13025,"children":13027},{"className":13026},[],[13028],{"type":26,"value":11317},{"type":26,"value":13030}," gets written to via the ",{"type":21,"tag":246,"props":13032,"children":13034},{"className":13033},[],[13035],{"type":26,"value":11339},{"type":26,"value":13037}," pointer, and",{"type":21,"tag":187,"props":13039,"children":13040},{},[13041,13043,13048],{"type":26,"value":13042},"find a means to increment ",{"type":21,"tag":246,"props":13044,"children":13046},{"className":13045},[],[13047],{"type":26,"value":11339},{"type":26,"value":13049}," 128 or more times.",{"type":21,"tag":22,"props":13051,"children":13052},{},[13053],{"type":26,"value":13054},"Starting with this first goal, we first have",{"type":21,"tag":239,"props":13056,"children":13058},{"className":11036,"code":13057,"language":11038,"meta":8,"style":8},"tp = v->text;\nwhile (*tp != '\\0' && *tp != '=' && cp \u003C reqend && *cp == *tp) {\n    cp++;\n    tp++;\n}\n\nif ((*tp == '\\0') || (*tp == '=')) {\n    while (cp \u003C reqend && isspace((int)*cp))\n        cp++;\n",[13059],{"type":21,"tag":246,"props":13060,"children":13061},{"__ignoreMap":8},[13062,13088,13184,13200,13216,13223,13230,13297,13344],{"type":21,"tag":455,"props":13063,"children":13064},{"class":457,"line":458},[13065,13069,13073,13078,13083],{"type":21,"tag":455,"props":13066,"children":13067},{"style":486},[13068],{"type":26,"value":12011},{"type":21,"tag":455,"props":13070,"children":13071},{"style":1349},[13072],{"type":26,"value":3193},{"type":21,"tag":455,"props":13074,"children":13075},{"style":486},[13076],{"type":26,"value":13077}," v",{"type":21,"tag":455,"props":13079,"children":13080},{"style":1349},[13081],{"type":26,"value":13082},"->",{"type":21,"tag":455,"props":13084,"children":13085},{"style":486},[13086],{"type":26,"value":13087},"text;\n",{"type":21,"tag":455,"props":13089,"children":13090},{"class":457,"line":336},[13091,13096,13100,13104,13108,13112,13116,13120,13124,13128,13132,13136,13140,13144,13148,13152,13156,13160,13164,13168,13172,13176,13180],{"type":21,"tag":455,"props":13092,"children":13093},{"style":1349},[13094],{"type":26,"value":13095},"while",{"type":21,"tag":455,"props":13097,"children":13098},{"style":486},[13099],{"type":26,"value":2136},{"type":21,"tag":455,"props":13101,"children":13102},{"style":1349},[13103],{"type":26,"value":2161},{"type":21,"tag":455,"props":13105,"children":13106},{"style":486},[13107],{"type":26,"value":12011},{"type":21,"tag":455,"props":13109,"children":13110},{"style":1349},[13111],{"type":26,"value":11181},{"type":21,"tag":455,"props":13113,"children":13114},{"style":492},[13115],{"type":26,"value":11186},{"type":21,"tag":455,"props":13117,"children":13118},{"style":480},[13119],{"type":26,"value":11191},{"type":21,"tag":455,"props":13121,"children":13122},{"style":492},[13123],{"type":26,"value":11196},{"type":21,"tag":455,"props":13125,"children":13126},{"style":1349},[13127],{"type":26,"value":1467},{"type":21,"tag":455,"props":13129,"children":13130},{"style":1349},[13131],{"type":26,"value":11055},{"type":21,"tag":455,"props":13133,"children":13134},{"style":486},[13135],{"type":26,"value":12011},{"type":21,"tag":455,"props":13137,"children":13138},{"style":1349},[13139],{"type":26,"value":11181},{"type":21,"tag":455,"props":13141,"children":13142},{"style":492},[13143],{"type":26,"value":12048},{"type":21,"tag":455,"props":13145,"children":13146},{"style":1349},[13147],{"type":26,"value":1467},{"type":21,"tag":455,"props":13149,"children":13150},{"style":486},[13151],{"type":26,"value":12057},{"type":21,"tag":455,"props":13153,"children":13154},{"style":1349},[13155],{"type":26,"value":11627},{"type":21,"tag":455,"props":13157,"children":13158},{"style":486},[13159],{"type":26,"value":11632},{"type":21,"tag":455,"props":13161,"children":13162},{"style":1349},[13163],{"type":26,"value":11637},{"type":21,"tag":455,"props":13165,"children":13166},{"style":1349},[13167],{"type":26,"value":11055},{"type":21,"tag":455,"props":13169,"children":13170},{"style":486},[13171],{"type":26,"value":11960},{"type":21,"tag":455,"props":13173,"children":13174},{"style":1349},[13175],{"type":26,"value":1412},{"type":21,"tag":455,"props":13177,"children":13178},{"style":1349},[13179],{"type":26,"value":11055},{"type":21,"tag":455,"props":13181,"children":13182},{"style":486},[13183],{"type":26,"value":12090},{"type":21,"tag":455,"props":13185,"children":13186},{"class":457,"line":333},[13187,13192,13196],{"type":21,"tag":455,"props":13188,"children":13189},{"style":486},[13190],{"type":26,"value":13191},"    cp",{"type":21,"tag":455,"props":13193,"children":13194},{"style":1349},[13195],{"type":26,"value":11156},{"type":21,"tag":455,"props":13197,"children":13198},{"style":486},[13199],{"type":26,"value":1440},{"type":21,"tag":455,"props":13201,"children":13202},{"class":457,"line":1424},[13203,13208,13212],{"type":21,"tag":455,"props":13204,"children":13205},{"style":486},[13206],{"type":26,"value":13207},"    tp",{"type":21,"tag":455,"props":13209,"children":13210},{"style":1349},[13211],{"type":26,"value":11156},{"type":21,"tag":455,"props":13213,"children":13214},{"style":486},[13215],{"type":26,"value":1440},{"type":21,"tag":455,"props":13217,"children":13218},{"class":457,"line":1443},[13219],{"type":21,"tag":455,"props":13220,"children":13221},{"style":486},[13222],{"type":26,"value":2002},{"type":21,"tag":455,"props":13224,"children":13225},{"class":457,"line":1489},[13226],{"type":21,"tag":455,"props":13227,"children":13228},{"emptyLinePlaceholder":471},[13229],{"type":26,"value":474},{"type":21,"tag":455,"props":13231,"children":13232},{"class":457,"line":1506},[13233,13237,13241,13245,13249,13253,13257,13261,13265,13269,13273,13277,13281,13285,13289,13293],{"type":21,"tag":455,"props":13234,"children":13235},{"style":1349},[13236],{"type":26,"value":2041},{"type":21,"tag":455,"props":13238,"children":13239},{"style":486},[13240],{"type":26,"value":12150},{"type":21,"tag":455,"props":13242,"children":13243},{"style":1349},[13244],{"type":26,"value":2161},{"type":21,"tag":455,"props":13246,"children":13247},{"style":486},[13248],{"type":26,"value":12011},{"type":21,"tag":455,"props":13250,"children":13251},{"style":1349},[13252],{"type":26,"value":1412},{"type":21,"tag":455,"props":13254,"children":13255},{"style":492},[13256],{"type":26,"value":11186},{"type":21,"tag":455,"props":13258,"children":13259},{"style":480},[13260],{"type":26,"value":11191},{"type":21,"tag":455,"props":13262,"children":13263},{"style":492},[13264],{"type":26,"value":11196},{"type":21,"tag":455,"props":13266,"children":13267},{"style":486},[13268],{"type":26,"value":4584},{"type":21,"tag":455,"props":13270,"children":13271},{"style":1349},[13272],{"type":26,"value":12183},{"type":21,"tag":455,"props":13274,"children":13275},{"style":486},[13276],{"type":26,"value":2136},{"type":21,"tag":455,"props":13278,"children":13279},{"style":1349},[13280],{"type":26,"value":2161},{"type":21,"tag":455,"props":13282,"children":13283},{"style":486},[13284],{"type":26,"value":12011},{"type":21,"tag":455,"props":13286,"children":13287},{"style":1349},[13288],{"type":26,"value":1412},{"type":21,"tag":455,"props":13290,"children":13291},{"style":492},[13292],{"type":26,"value":12048},{"type":21,"tag":455,"props":13294,"children":13295},{"style":486},[13296],{"type":26,"value":12208},{"type":21,"tag":455,"props":13298,"children":13299},{"class":457,"line":1547},[13300,13304,13308,13312,13316,13320,13324,13328,13332,13336,13340],{"type":21,"tag":455,"props":13301,"children":13302},{"style":1349},[13303],{"type":26,"value":11139},{"type":21,"tag":455,"props":13305,"children":13306},{"style":486},[13307],{"type":26,"value":12221},{"type":21,"tag":455,"props":13309,"children":13310},{"style":1349},[13311],{"type":26,"value":11627},{"type":21,"tag":455,"props":13313,"children":13314},{"style":486},[13315],{"type":26,"value":11632},{"type":21,"tag":455,"props":13317,"children":13318},{"style":1349},[13319],{"type":26,"value":11637},{"type":21,"tag":455,"props":13321,"children":13322},{"style":1365},[13323],{"type":26,"value":11668},{"type":21,"tag":455,"props":13325,"children":13326},{"style":486},[13327],{"type":26,"value":4575},{"type":21,"tag":455,"props":13329,"children":13330},{"style":1349},[13331],{"type":26,"value":2141},{"type":21,"tag":455,"props":13333,"children":13334},{"style":486},[13335],{"type":26,"value":11681},{"type":21,"tag":455,"props":13337,"children":13338},{"style":1349},[13339],{"type":26,"value":2161},{"type":21,"tag":455,"props":13341,"children":13342},{"style":486},[13343],{"type":26,"value":12258},{"type":21,"tag":455,"props":13345,"children":13346},{"class":457,"line":1564},[13347,13352,13356],{"type":21,"tag":455,"props":13348,"children":13349},{"style":486},[13350],{"type":26,"value":13351},"        cp",{"type":21,"tag":455,"props":13353,"children":13354},{"style":1349},[13355],{"type":26,"value":11156},{"type":21,"tag":455,"props":13357,"children":13358},{"style":486},[13359],{"type":26,"value":1440},{"type":21,"tag":22,"props":13361,"children":13362},{},[13363,13365,13370,13372,13377,13379,13384,13385,13390],{"type":26,"value":13364},"While looping through variables definitions in order to look for one which matches the input data, we're initially using ",{"type":21,"tag":246,"props":13366,"children":13368},{"className":13367},[],[13369],{"type":26,"value":11339},{"type":26,"value":13371}," as a pointer into the variable's ",{"type":21,"tag":246,"props":13373,"children":13375},{"className":13374},[],[13376],{"type":26,"value":26},{"type":26,"value":13378}," field. If the input matches that text up until a ",{"type":21,"tag":246,"props":13380,"children":13382},{"className":13381},[],[13383],{"type":26,"value":11250},{"type":26,"value":411},{"type":21,"tag":246,"props":13386,"children":13388},{"className":13387},[],[13389],{"type":26,"value":3193},{"type":26,"value":13391}," character, this block gets executed. Next,",{"type":21,"tag":239,"props":13393,"children":13395},{"className":11036,"code":13394,"language":11038,"meta":8,"style":8},"    if (cp == reqend || *cp == ',') {\n        buf[0] = '\\0';\n        *data = buf;\n        if (cp \u003C reqend)\n            cp++;\n        reqpt = cp;\n        return v;\n    }\n",[13396],{"type":21,"tag":246,"props":13397,"children":13398},{"__ignoreMap":8},[13399,13442,13482,13502,13521,13537,13553,13564],{"type":21,"tag":455,"props":13400,"children":13401},{"class":457,"line":458},[13402,13406,13410,13414,13418,13422,13426,13430,13434,13438],{"type":21,"tag":455,"props":13403,"children":13404},{"style":1349},[13405],{"type":26,"value":1402},{"type":21,"tag":455,"props":13407,"children":13408},{"style":486},[13409],{"type":26,"value":12221},{"type":21,"tag":455,"props":13411,"children":13412},{"style":1349},[13413],{"type":26,"value":1412},{"type":21,"tag":455,"props":13415,"children":13416},{"style":486},[13417],{"type":26,"value":11632},{"type":21,"tag":455,"props":13419,"children":13420},{"style":1349},[13421],{"type":26,"value":12183},{"type":21,"tag":455,"props":13423,"children":13424},{"style":1349},[13425],{"type":26,"value":11055},{"type":21,"tag":455,"props":13427,"children":13428},{"style":486},[13429],{"type":26,"value":11960},{"type":21,"tag":455,"props":13431,"children":13432},{"style":1349},[13433],{"type":26,"value":1412},{"type":21,"tag":455,"props":13435,"children":13436},{"style":492},[13437],{"type":26,"value":11659},{"type":21,"tag":455,"props":13439,"children":13440},{"style":486},[13441],{"type":26,"value":4879},{"type":21,"tag":455,"props":13443,"children":13444},{"class":457,"line":336},[13445,13450,13454,13458,13462,13466,13470,13474,13478],{"type":21,"tag":455,"props":13446,"children":13447},{"style":4527},[13448],{"type":26,"value":13449},"        buf",{"type":21,"tag":455,"props":13451,"children":13452},{"style":486},[13453],{"type":26,"value":11569},{"type":21,"tag":455,"props":13455,"children":13456},{"style":480},[13457],{"type":26,"value":7863},{"type":21,"tag":455,"props":13459,"children":13460},{"style":486},[13461],{"type":26,"value":5718},{"type":21,"tag":455,"props":13463,"children":13464},{"style":1349},[13465],{"type":26,"value":3193},{"type":21,"tag":455,"props":13467,"children":13468},{"style":492},[13469],{"type":26,"value":11186},{"type":21,"tag":455,"props":13471,"children":13472},{"style":480},[13473],{"type":26,"value":11191},{"type":21,"tag":455,"props":13475,"children":13476},{"style":492},[13477],{"type":26,"value":11196},{"type":21,"tag":455,"props":13479,"children":13480},{"style":486},[13481],{"type":26,"value":1440},{"type":21,"tag":455,"props":13483,"children":13484},{"class":457,"line":333},[13485,13490,13494,13498],{"type":21,"tag":455,"props":13486,"children":13487},{"style":1349},[13488],{"type":26,"value":13489},"        *",{"type":21,"tag":455,"props":13491,"children":13492},{"style":486},[13493],{"type":26,"value":12378},{"type":21,"tag":455,"props":13495,"children":13496},{"style":1349},[13497],{"type":26,"value":3193},{"type":21,"tag":455,"props":13499,"children":13500},{"style":486},[13501],{"type":26,"value":12387},{"type":21,"tag":455,"props":13503,"children":13504},{"class":457,"line":1424},[13505,13509,13513,13517],{"type":21,"tag":455,"props":13506,"children":13507},{"style":1349},[13508],{"type":26,"value":5627},{"type":21,"tag":455,"props":13510,"children":13511},{"style":486},[13512],{"type":26,"value":12221},{"type":21,"tag":455,"props":13514,"children":13515},{"style":1349},[13516],{"type":26,"value":11627},{"type":21,"tag":455,"props":13518,"children":13519},{"style":486},[13520],{"type":26,"value":11741},{"type":21,"tag":455,"props":13522,"children":13523},{"class":457,"line":1443},[13524,13529,13533],{"type":21,"tag":455,"props":13525,"children":13526},{"style":486},[13527],{"type":26,"value":13528},"            cp",{"type":21,"tag":455,"props":13530,"children":13531},{"style":1349},[13532],{"type":26,"value":11156},{"type":21,"tag":455,"props":13534,"children":13535},{"style":486},[13536],{"type":26,"value":1440},{"type":21,"tag":455,"props":13538,"children":13539},{"class":457,"line":1489},[13540,13545,13549],{"type":21,"tag":455,"props":13541,"children":13542},{"style":486},[13543],{"type":26,"value":13544},"        reqpt ",{"type":21,"tag":455,"props":13546,"children":13547},{"style":1349},[13548],{"type":26,"value":3193},{"type":21,"tag":455,"props":13550,"children":13551},{"style":486},[13552],{"type":26,"value":12440},{"type":21,"tag":455,"props":13554,"children":13555},{"class":457,"line":1506},[13556,13560],{"type":21,"tag":455,"props":13557,"children":13558},{"style":1349},[13559],{"type":26,"value":1430},{"type":21,"tag":455,"props":13561,"children":13562},{"style":486},[13563],{"type":26,"value":12453},{"type":21,"tag":455,"props":13565,"children":13566},{"class":457,"line":1547},[13567],{"type":21,"tag":455,"props":13568,"children":13569},{"style":486},[13570],{"type":26,"value":6008},{"type":21,"tag":22,"props":13572,"children":13573},{},[13574],{"type":26,"value":13575},"If the input data contains a comma, this block will execute and return from the function. So to continue on, the input must not contain a comma.",{"type":21,"tag":239,"props":13577,"children":13579},{"className":11036,"code":13578,"language":11038,"meta":8,"style":8},"    if (*cp == '=') {\n        cp++;\n        tp = buf;\n        while (cp \u003C reqend && isspace((int)*cp))\n            cp++;\n",[13580],{"type":21,"tag":246,"props":13581,"children":13582},{"__ignoreMap":8},[13583,13614,13629,13645,13693],{"type":21,"tag":455,"props":13584,"children":13585},{"class":457,"line":458},[13586,13590,13594,13598,13602,13606,13610],{"type":21,"tag":455,"props":13587,"children":13588},{"style":1349},[13589],{"type":26,"value":1402},{"type":21,"tag":455,"props":13591,"children":13592},{"style":486},[13593],{"type":26,"value":2136},{"type":21,"tag":455,"props":13595,"children":13596},{"style":1349},[13597],{"type":26,"value":2161},{"type":21,"tag":455,"props":13599,"children":13600},{"style":486},[13601],{"type":26,"value":11960},{"type":21,"tag":455,"props":13603,"children":13604},{"style":1349},[13605],{"type":26,"value":1412},{"type":21,"tag":455,"props":13607,"children":13608},{"style":492},[13609],{"type":26,"value":12048},{"type":21,"tag":455,"props":13611,"children":13612},{"style":486},[13613],{"type":26,"value":4879},{"type":21,"tag":455,"props":13615,"children":13616},{"class":457,"line":336},[13617,13621,13625],{"type":21,"tag":455,"props":13618,"children":13619},{"style":486},[13620],{"type":26,"value":13351},{"type":21,"tag":455,"props":13622,"children":13623},{"style":1349},[13624],{"type":26,"value":11156},{"type":21,"tag":455,"props":13626,"children":13627},{"style":486},[13628],{"type":26,"value":1440},{"type":21,"tag":455,"props":13630,"children":13631},{"class":457,"line":333},[13632,13637,13641],{"type":21,"tag":455,"props":13633,"children":13634},{"style":486},[13635],{"type":26,"value":13636},"        tp ",{"type":21,"tag":455,"props":13638,"children":13639},{"style":1349},[13640],{"type":26,"value":3193},{"type":21,"tag":455,"props":13642,"children":13643},{"style":486},[13644],{"type":26,"value":12387},{"type":21,"tag":455,"props":13646,"children":13647},{"class":457,"line":1424},[13648,13653,13657,13661,13665,13669,13673,13677,13681,13685,13689],{"type":21,"tag":455,"props":13649,"children":13650},{"style":1349},[13651],{"type":26,"value":13652},"        while",{"type":21,"tag":455,"props":13654,"children":13655},{"style":486},[13656],{"type":26,"value":12221},{"type":21,"tag":455,"props":13658,"children":13659},{"style":1349},[13660],{"type":26,"value":11627},{"type":21,"tag":455,"props":13662,"children":13663},{"style":486},[13664],{"type":26,"value":11632},{"type":21,"tag":455,"props":13666,"children":13667},{"style":1349},[13668],{"type":26,"value":11637},{"type":21,"tag":455,"props":13670,"children":13671},{"style":1365},[13672],{"type":26,"value":11668},{"type":21,"tag":455,"props":13674,"children":13675},{"style":486},[13676],{"type":26,"value":4575},{"type":21,"tag":455,"props":13678,"children":13679},{"style":1349},[13680],{"type":26,"value":2141},{"type":21,"tag":455,"props":13682,"children":13683},{"style":486},[13684],{"type":26,"value":11681},{"type":21,"tag":455,"props":13686,"children":13687},{"style":1349},[13688],{"type":26,"value":2161},{"type":21,"tag":455,"props":13690,"children":13691},{"style":486},[13692],{"type":26,"value":12258},{"type":21,"tag":455,"props":13694,"children":13695},{"class":457,"line":1443},[13696,13700,13704],{"type":21,"tag":455,"props":13697,"children":13698},{"style":486},[13699],{"type":26,"value":13528},{"type":21,"tag":455,"props":13701,"children":13702},{"style":1349},[13703],{"type":26,"value":11156},{"type":21,"tag":455,"props":13705,"children":13706},{"style":486},[13707],{"type":26,"value":1440},{"type":21,"tag":22,"props":13709,"children":13710},{},[13711],{"type":26,"value":13712},"So long as the input data contains an equals sign, this block will execute. Finally,",{"type":21,"tag":239,"props":13714,"children":13716},{"className":11036,"code":13715,"language":11038,"meta":8,"style":8},"        while (cp \u003C reqend && *cp != ',')\n            *tp++ = *cp++;\n        if (cp \u003C reqend)\n            cp++;\n        *tp = '\\0';\n        while (isspace((int)(*(tp - 1))))\n            *(--tp) = '\\0';\n",[13717],{"type":21,"tag":246,"props":13718,"children":13719},{"__ignoreMap":8},[13720,13763,13799,13818,13833,13864,13911],{"type":21,"tag":455,"props":13721,"children":13722},{"class":457,"line":458},[13723,13727,13731,13735,13739,13743,13747,13751,13755,13759],{"type":21,"tag":455,"props":13724,"children":13725},{"style":1349},[13726],{"type":26,"value":13652},{"type":21,"tag":455,"props":13728,"children":13729},{"style":486},[13730],{"type":26,"value":12221},{"type":21,"tag":455,"props":13732,"children":13733},{"style":1349},[13734],{"type":26,"value":11627},{"type":21,"tag":455,"props":13736,"children":13737},{"style":486},[13738],{"type":26,"value":11632},{"type":21,"tag":455,"props":13740,"children":13741},{"style":1349},[13742],{"type":26,"value":11637},{"type":21,"tag":455,"props":13744,"children":13745},{"style":1349},[13746],{"type":26,"value":11055},{"type":21,"tag":455,"props":13748,"children":13749},{"style":486},[13750],{"type":26,"value":11960},{"type":21,"tag":455,"props":13752,"children":13753},{"style":1349},[13754],{"type":26,"value":11181},{"type":21,"tag":455,"props":13756,"children":13757},{"style":492},[13758],{"type":26,"value":11659},{"type":21,"tag":455,"props":13760,"children":13761},{"style":486},[13762],{"type":26,"value":500},{"type":21,"tag":455,"props":13764,"children":13765},{"class":457,"line":336},[13766,13771,13775,13779,13783,13787,13791,13795],{"type":21,"tag":455,"props":13767,"children":13768},{"style":1349},[13769],{"type":26,"value":13770},"            *",{"type":21,"tag":455,"props":13772,"children":13773},{"style":486},[13774],{"type":26,"value":11339},{"type":21,"tag":455,"props":13776,"children":13777},{"style":1349},[13778],{"type":26,"value":11156},{"type":21,"tag":455,"props":13780,"children":13781},{"style":1349},[13782],{"type":26,"value":2131},{"type":21,"tag":455,"props":13784,"children":13785},{"style":1349},[13786],{"type":26,"value":11055},{"type":21,"tag":455,"props":13788,"children":13789},{"style":486},[13790],{"type":26,"value":11328},{"type":21,"tag":455,"props":13792,"children":13793},{"style":1349},[13794],{"type":26,"value":11156},{"type":21,"tag":455,"props":13796,"children":13797},{"style":486},[13798],{"type":26,"value":1440},{"type":21,"tag":455,"props":13800,"children":13801},{"class":457,"line":333},[13802,13806,13810,13814],{"type":21,"tag":455,"props":13803,"children":13804},{"style":1349},[13805],{"type":26,"value":5627},{"type":21,"tag":455,"props":13807,"children":13808},{"style":486},[13809],{"type":26,"value":12221},{"type":21,"tag":455,"props":13811,"children":13812},{"style":1349},[13813],{"type":26,"value":11627},{"type":21,"tag":455,"props":13815,"children":13816},{"style":486},[13817],{"type":26,"value":11741},{"type":21,"tag":455,"props":13819,"children":13820},{"class":457,"line":1424},[13821,13825,13829],{"type":21,"tag":455,"props":13822,"children":13823},{"style":486},[13824],{"type":26,"value":13528},{"type":21,"tag":455,"props":13826,"children":13827},{"style":1349},[13828],{"type":26,"value":11156},{"type":21,"tag":455,"props":13830,"children":13831},{"style":486},[13832],{"type":26,"value":1440},{"type":21,"tag":455,"props":13834,"children":13835},{"class":457,"line":1443},[13836,13840,13844,13848,13852,13856,13860],{"type":21,"tag":455,"props":13837,"children":13838},{"style":1349},[13839],{"type":26,"value":13489},{"type":21,"tag":455,"props":13841,"children":13842},{"style":486},[13843],{"type":26,"value":12011},{"type":21,"tag":455,"props":13845,"children":13846},{"style":1349},[13847],{"type":26,"value":3193},{"type":21,"tag":455,"props":13849,"children":13850},{"style":492},[13851],{"type":26,"value":11186},{"type":21,"tag":455,"props":13853,"children":13854},{"style":480},[13855],{"type":26,"value":11191},{"type":21,"tag":455,"props":13857,"children":13858},{"style":492},[13859],{"type":26,"value":11196},{"type":21,"tag":455,"props":13861,"children":13862},{"style":486},[13863],{"type":26,"value":1440},{"type":21,"tag":455,"props":13865,"children":13866},{"class":457,"line":1489},[13867,13871,13875,13879,13883,13887,13891,13895,13899,13903,13907],{"type":21,"tag":455,"props":13868,"children":13869},{"style":1349},[13870],{"type":26,"value":13652},{"type":21,"tag":455,"props":13872,"children":13873},{"style":486},[13874],{"type":26,"value":2136},{"type":21,"tag":455,"props":13876,"children":13877},{"style":1365},[13878],{"type":26,"value":12760},{"type":21,"tag":455,"props":13880,"children":13881},{"style":486},[13882],{"type":26,"value":4575},{"type":21,"tag":455,"props":13884,"children":13885},{"style":1349},[13886],{"type":26,"value":2141},{"type":21,"tag":455,"props":13888,"children":13889},{"style":486},[13890],{"type":26,"value":3478},{"type":21,"tag":455,"props":13892,"children":13893},{"style":1349},[13894],{"type":26,"value":2161},{"type":21,"tag":455,"props":13896,"children":13897},{"style":486},[13898],{"type":26,"value":12781},{"type":21,"tag":455,"props":13900,"children":13901},{"style":1349},[13902],{"type":26,"value":2702},{"type":21,"tag":455,"props":13904,"children":13905},{"style":480},[13906],{"type":26,"value":2707},{"type":21,"tag":455,"props":13908,"children":13909},{"style":486},[13910],{"type":26,"value":12794},{"type":21,"tag":455,"props":13912,"children":13913},{"class":457,"line":1506},[13914,13918,13922,13926,13930,13934,13938,13942,13946],{"type":21,"tag":455,"props":13915,"children":13916},{"style":1349},[13917],{"type":26,"value":13770},{"type":21,"tag":455,"props":13919,"children":13920},{"style":486},[13921],{"type":26,"value":489},{"type":21,"tag":455,"props":13923,"children":13924},{"style":1349},[13925],{"type":26,"value":12811},{"type":21,"tag":455,"props":13927,"children":13928},{"style":486},[13929],{"type":26,"value":12816},{"type":21,"tag":455,"props":13931,"children":13932},{"style":1349},[13933],{"type":26,"value":3193},{"type":21,"tag":455,"props":13935,"children":13936},{"style":492},[13937],{"type":26,"value":11186},{"type":21,"tag":455,"props":13939,"children":13940},{"style":480},[13941],{"type":26,"value":11191},{"type":21,"tag":455,"props":13943,"children":13944},{"style":492},[13945],{"type":26,"value":11196},{"type":21,"tag":455,"props":13947,"children":13948},{"style":486},[13949],{"type":26,"value":1440},{"type":21,"tag":22,"props":13951,"children":13952},{},[13953,13955,13960,13962,13967,13969,13974,13976,13982,13984,13989],{"type":26,"value":13954},"We've reached the code which copies into ",{"type":21,"tag":246,"props":13956,"children":13958},{"className":13957},[],[13959],{"type":26,"value":11317},{"type":26,"value":13961}," (via ",{"type":21,"tag":246,"props":13963,"children":13965},{"className":13964},[],[13966],{"type":26,"value":11339},{"type":26,"value":13968},"). So long as ",{"type":21,"tag":246,"props":13970,"children":13972},{"className":13971},[],[13973],{"type":26,"value":11328},{"type":26,"value":13975}," is not a comma and is less than the end of the input data (",{"type":21,"tag":246,"props":13977,"children":13979},{"className":13978},[],[13980],{"type":26,"value":13981},"reqend",{"type":26,"value":13983},"), the copy continues. Notably, no checks prevent this loop from executing 128+ times and blowing the stack of ",{"type":21,"tag":246,"props":13985,"children":13987},{"className":13986},[],[13988],{"type":26,"value":11317},{"type":26,"value":13990},", since the input data can exceed that size. Reviewing the preconditions we've built up for reaching this point in the code, we only have",{"type":21,"tag":183,"props":13992,"children":13993},{},[13994,13999],{"type":21,"tag":187,"props":13995,"children":13996},{},[13997],{"type":26,"value":13998},"input does not contain a comma, and",{"type":21,"tag":187,"props":14000,"children":14001},{},[14002],{"type":26,"value":14003},"input contains an equals sign.",{"type":21,"tag":22,"props":14005,"children":14006},{},[14007,14009,14014],{"type":26,"value":14008},"This sounds loose enough to be practically exploitable, and it turns out it was, by writing and executing malicious code on the stack beyond ",{"type":21,"tag":246,"props":14010,"children":14012},{"className":14011},[],[14013],{"type":26,"value":11317},{"type":26,"value":14015},". The lack of bounds checking is obvious once it's singled out, but buried among the other checks and validations, it's much harder to find.",{"type":21,"tag":56,"props":14017,"children":14019},{"id":14018},"mitigation-in-ntpd",[14020],{"type":26,"value":14021},"Mitigation in NTPD",{"type":21,"tag":22,"props":14023,"children":14024},{},[14025,14027,14032],{"type":26,"value":14026},"The mitigation applied for this vulnerability was as simple as you might expect: adding another condition to the ",{"type":21,"tag":246,"props":14028,"children":14030},{"className":14029},[],[14031],{"type":26,"value":13095},{"type":26,"value":14033}," loop performing the copy:",{"type":21,"tag":239,"props":14035,"children":14037},{"className":11036,"code":14036,"language":11038,"meta":8,"style":8},"        while (cp \u003C reqend && *cp != ',') {\n            *tp++ = *cp++;\n            if (tp >= buf + sizeof(buf) - 1) {\n#if 0  /* don't syslog for now - DoS potential on filling syslog */\n                msyslog(LOG_WARNING,\n                    \"Attempted \\\"ntpdx\\\" exploit from IP %d.%d.%d.%d:%d (possibly spoofed)\\n\",\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 24) & 0xff,\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 16) & 0xff,\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 8) & 0xff,\n                    (ntohl(rmt_addr->sin_addr.s_addr) >> 0) & 0xff,\n                    ntohs(rmt_addr->sin_port));\n#endif\n                return (0);\n            }\n        }\n",[14038],{"type":21,"tag":246,"props":14039,"children":14040},{"__ignoreMap":8},[14041,14084,14119,14166,14183,14191,14199,14207,14215,14223,14231,14239,14247,14267,14274],{"type":21,"tag":455,"props":14042,"children":14043},{"class":457,"line":458},[14044,14048,14052,14056,14060,14064,14068,14072,14076,14080],{"type":21,"tag":455,"props":14045,"children":14046},{"style":1349},[14047],{"type":26,"value":13652},{"type":21,"tag":455,"props":14049,"children":14050},{"style":486},[14051],{"type":26,"value":12221},{"type":21,"tag":455,"props":14053,"children":14054},{"style":1349},[14055],{"type":26,"value":11627},{"type":21,"tag":455,"props":14057,"children":14058},{"style":486},[14059],{"type":26,"value":11632},{"type":21,"tag":455,"props":14061,"children":14062},{"style":1349},[14063],{"type":26,"value":11637},{"type":21,"tag":455,"props":14065,"children":14066},{"style":1349},[14067],{"type":26,"value":11055},{"type":21,"tag":455,"props":14069,"children":14070},{"style":486},[14071],{"type":26,"value":11960},{"type":21,"tag":455,"props":14073,"children":14074},{"style":1349},[14075],{"type":26,"value":11181},{"type":21,"tag":455,"props":14077,"children":14078},{"style":492},[14079],{"type":26,"value":11659},{"type":21,"tag":455,"props":14081,"children":14082},{"style":486},[14083],{"type":26,"value":4879},{"type":21,"tag":455,"props":14085,"children":14086},{"class":457,"line":336},[14087,14091,14095,14099,14103,14107,14111,14115],{"type":21,"tag":455,"props":14088,"children":14089},{"style":1349},[14090],{"type":26,"value":13770},{"type":21,"tag":455,"props":14092,"children":14093},{"style":486},[14094],{"type":26,"value":11339},{"type":21,"tag":455,"props":14096,"children":14097},{"style":1349},[14098],{"type":26,"value":11156},{"type":21,"tag":455,"props":14100,"children":14101},{"style":1349},[14102],{"type":26,"value":2131},{"type":21,"tag":455,"props":14104,"children":14105},{"style":1349},[14106],{"type":26,"value":11055},{"type":21,"tag":455,"props":14108,"children":14109},{"style":486},[14110],{"type":26,"value":11328},{"type":21,"tag":455,"props":14112,"children":14113},{"style":1349},[14114],{"type":26,"value":11156},{"type":21,"tag":455,"props":14116,"children":14117},{"style":486},[14118],{"type":26,"value":1440},{"type":21,"tag":455,"props":14120,"children":14121},{"class":457,"line":333},[14122,14126,14131,14135,14140,14144,14149,14154,14158,14162],{"type":21,"tag":455,"props":14123,"children":14124},{"style":1349},[14125],{"type":26,"value":12145},{"type":21,"tag":455,"props":14127,"children":14128},{"style":486},[14129],{"type":26,"value":14130}," (tp ",{"type":21,"tag":455,"props":14132,"children":14133},{"style":1349},[14134],{"type":26,"value":11736},{"type":21,"tag":455,"props":14136,"children":14137},{"style":486},[14138],{"type":26,"value":14139}," buf ",{"type":21,"tag":455,"props":14141,"children":14142},{"style":1349},[14143],{"type":26,"value":2206},{"type":21,"tag":455,"props":14145,"children":14146},{"style":1349},[14147],{"type":26,"value":14148}," sizeof",{"type":21,"tag":455,"props":14150,"children":14151},{"style":486},[14152],{"type":26,"value":14153},"(buf) ",{"type":21,"tag":455,"props":14155,"children":14156},{"style":1349},[14157],{"type":26,"value":2702},{"type":21,"tag":455,"props":14159,"children":14160},{"style":480},[14161],{"type":26,"value":2707},{"type":21,"tag":455,"props":14163,"children":14164},{"style":486},[14165],{"type":26,"value":4879},{"type":21,"tag":455,"props":14167,"children":14168},{"class":457,"line":1424},[14169,14174,14178],{"type":21,"tag":455,"props":14170,"children":14171},{"style":1349},[14172],{"type":26,"value":14173},"#if",{"type":21,"tag":455,"props":14175,"children":14176},{"style":480},[14177],{"type":26,"value":1417},{"type":21,"tag":455,"props":14179,"children":14180},{"style":462},[14181],{"type":26,"value":14182},"  /* don't syslog for now - DoS potential on filling syslog */\n",{"type":21,"tag":455,"props":14184,"children":14185},{"class":457,"line":1443},[14186],{"type":21,"tag":455,"props":14187,"children":14188},{"style":462},[14189],{"type":26,"value":14190},"                msyslog(LOG_WARNING,\n",{"type":21,"tag":455,"props":14192,"children":14193},{"class":457,"line":1489},[14194],{"type":21,"tag":455,"props":14195,"children":14196},{"style":462},[14197],{"type":26,"value":14198},"                    \"Attempted \\\"ntpdx\\\" exploit from IP %d.%d.%d.%d:%d (possibly spoofed)\\n\",\n",{"type":21,"tag":455,"props":14200,"children":14201},{"class":457,"line":1506},[14202],{"type":21,"tag":455,"props":14203,"children":14204},{"style":462},[14205],{"type":26,"value":14206},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 24) & 0xff,\n",{"type":21,"tag":455,"props":14208,"children":14209},{"class":457,"line":1547},[14210],{"type":21,"tag":455,"props":14211,"children":14212},{"style":462},[14213],{"type":26,"value":14214},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 16) & 0xff,\n",{"type":21,"tag":455,"props":14216,"children":14217},{"class":457,"line":1564},[14218],{"type":21,"tag":455,"props":14219,"children":14220},{"style":462},[14221],{"type":26,"value":14222},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 8) & 0xff,\n",{"type":21,"tag":455,"props":14224,"children":14225},{"class":457,"line":1605},[14226],{"type":21,"tag":455,"props":14227,"children":14228},{"style":462},[14229],{"type":26,"value":14230},"                    (ntohl(rmt_addr->sin_addr.s_addr) >> 0) & 0xff,\n",{"type":21,"tag":455,"props":14232,"children":14233},{"class":457,"line":1622},[14234],{"type":21,"tag":455,"props":14235,"children":14236},{"style":462},[14237],{"type":26,"value":14238},"                    ntohs(rmt_addr->sin_port));\n",{"type":21,"tag":455,"props":14240,"children":14241},{"class":457,"line":1663},[14242],{"type":21,"tag":455,"props":14243,"children":14244},{"style":1349},[14245],{"type":26,"value":14246},"#endif\n",{"type":21,"tag":455,"props":14248,"children":14249},{"class":457,"line":1680},[14250,14255,14259,14263],{"type":21,"tag":455,"props":14251,"children":14252},{"style":1349},[14253],{"type":26,"value":14254},"                return",{"type":21,"tag":455,"props":14256,"children":14257},{"style":486},[14258],{"type":26,"value":2136},{"type":21,"tag":455,"props":14260,"children":14261},{"style":480},[14262],{"type":26,"value":7863},{"type":21,"tag":455,"props":14264,"children":14265},{"style":486},[14266],{"type":26,"value":2171},{"type":21,"tag":455,"props":14268,"children":14269},{"class":457,"line":1721},[14270],{"type":21,"tag":455,"props":14271,"children":14272},{"style":486},[14273],{"type":26,"value":12130},{"type":21,"tag":455,"props":14275,"children":14276},{"class":457,"line":1738},[14277],{"type":21,"tag":455,"props":14278,"children":14279},{"style":486},[14280],{"type":26,"value":5680},{"type":21,"tag":22,"props":14282,"children":14283},{},[14284],{"type":26,"value":14285},"It worth appreciating how the potential exploitability of the fix was itself taken into consideration, by choosing not to write logs when the size check is triggered. NTP is a UDP-based protocol which means that source IP addresses can be spoofed by the sender, so it was decided that there wasn't enough value in logging potentially-spoofed IPs given that it potentially introduces a resource exhaustion denial-of-service attack (by deliberately filling up the server's logs).",{"type":21,"tag":56,"props":14287,"children":14289},{"id":14288},"takeaways",[14290],{"type":26,"value":14291},"Takeaways",{"type":21,"tag":2953,"props":14293,"children":14294},{},[14295,14300,14305],{"type":21,"tag":187,"props":14296,"children":14297},{},[14298],{"type":26,"value":14299},"Copying untrusted data in memory-unsafe languages is a risky process which must be done  with the utmost care.",{"type":21,"tag":187,"props":14301,"children":14302},{},[14303],{"type":26,"value":14304},"When code which performs a copy is being a reviewed, a useful technique is to step through each code path leading up to the loop and assess whether any of them could cause the copy to overflow the buffer.",{"type":21,"tag":187,"props":14306,"children":14307},{},[14308,14310,14316,14318,14324],{"type":26,"value":14309},"Whenever possible, \"safe\" size-aware copying routines should be used (e.g. ",{"type":21,"tag":246,"props":14311,"children":14313},{"className":14312},[],[14314],{"type":26,"value":14315},"strncpy",{"type":26,"value":14317},") in place of \"unsafe\" routines (e.g. ",{"type":21,"tag":246,"props":14319,"children":14321},{"className":14320},[],[14322],{"type":26,"value":14323},"strcpy",{"type":26,"value":14325},") or ad-hoc code.",{"type":21,"tag":1279,"props":14327,"children":14328},{},[14329],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":14331},[14332,14333,14334,14335,14336],{"id":11012,"depth":336,"text":11015},{"id":11281,"depth":336,"text":11284},{"id":12986,"depth":336,"text":12989},{"id":14018,"depth":336,"text":14021},{"id":14288,"depth":336,"text":14291},"content:phendry:2021-10-30:SpotTheVulnLoopsAndTermConditions.md","phendry/2021-10-30/SpotTheVulnLoopsAndTermConditions.md","phendry/2021-10-30/SpotTheVulnLoopsAndTermConditions",{"user":2795,"name":2796},{"_path":14342,"_dir":14343,"_draft":7,"_partial":7,"_locale":8,"title":14344,"description":14345,"image":14346,"tags":14347,"publishDate":14343,"excerpt":14345,"body":14348,"_type":343,"_id":15132,"_source":345,"_file":15133,"_stem":15134,"_extension":348,"author":15135},"/phendry/2021-07-30/spotthevulndataranges","2021-07-30","Spot the Vulnerability: Data Ranges and Untrusted Input","In 1997, a flaw was discovered in how Linux and Windows handled IP fragmentation, a Denial-of-Service vulnerability which allowed systems to be crashed remotely.","/phendry/2021-07-30/img/vulnerability.jpg",[1302,15],{"type":18,"children":14349,"toc":15125},[14350,14354,14360,14365,14370,14376,14381,14390,14395,14787,14834,14838,14848,14886,14921,14971,14976,14985,14997,15032,15038,15057,15070,15074,15121],{"type":21,"tag":22,"props":14351,"children":14352},{},[14353],{"type":26,"value":14345},{"type":21,"tag":56,"props":14355,"children":14357},{"id":14356},"background-ip-fragmentation",[14358],{"type":26,"value":14359},"Background: IP Fragmentation",{"type":21,"tag":22,"props":14361,"children":14362},{},[14363],{"type":26,"value":14364},"IP Fragmentation occurs when a packet passes through a link whose MTU is smaller than the packet size; it gets fragmented into multiple smaller packets, and subsequently reassembled by the receiver.",{"type":21,"tag":22,"props":14366,"children":14367},{},[14368],{"type":26,"value":14369},"The data in these fragments would typically be non-overlapping, but since partial overlap is permitted by the protocol, IP implementations need to handle that case.",{"type":21,"tag":56,"props":14371,"children":14373},{"id":14372},"re-assembling-fragmented-packets",[14374],{"type":26,"value":14375},"Re-assembling Fragmented Packets",{"type":21,"tag":22,"props":14377,"children":14378},{},[14379],{"type":26,"value":14380},"When Linux reassembled fragmented packets, it would loop over each fragment, copying the payload of each fragment into a newly-allocated buffer. In order to handle the case where a fragment overlaps with the previous fragment, if its start offset was less than the end offset of the previous fragment, then its start offset would be moved forward to eliminate the overlap. Here's a diagram to help visualize this:",{"type":21,"tag":239,"props":14382,"children":14385},{"className":14383,"code":14384,"language":26},[242],"0                    22                40\nv                     v                 v\n|-----Fragment--1-----|\n                  |-----Fragment--2-----|\n                  ^  \\\n                 19   \\\n                       Overlap here\n\nbecomes...\n\n0                    22                40\nv                     v                 v\n|-----Fragment--1-----|\n                      |---Fragment--2---|\n                    \\\n                     \\\n                      Overlap discarded\n",[14386],{"type":21,"tag":246,"props":14387,"children":14388},{"__ignoreMap":8},[14389],{"type":26,"value":14384},{"type":21,"tag":22,"props":14391,"children":14392},{},[14393],{"type":26,"value":14394},"And here's how this was implemented (with only relevant code shown):",{"type":21,"tag":239,"props":14396,"children":14398},{"className":11036,"code":14397,"language":11038,"meta":8,"style":8},"/*\n *      Determine the position of this fragment.\n */\n\nend = offset + ntohs(iph->tot_len) - ihl;\n\n...\n\n/*\n *      We found where to put this one.\n *      Check for overlap with preceding fragment, and, if needed,\n *      align things so that any overlaps are eliminated.\n */\nif (prev != NULL && offset \u003C prev->end)\n{\n        i = prev->end - offset;\n        offset += i;    /* ptr into datagram */\n        ptr += i;       /* ptr into fragment data */\n}\n\n...\n\n/* Fill in the structure. */\nfp->offset = offset;\nfp->end = end;\nfp->len = end - offset;\n",[14399],{"type":21,"tag":246,"props":14400,"children":14401},{"__ignoreMap":8},[14402,14410,14418,14426,14433,14485,14492,14500,14507,14514,14522,14530,14538,14545,14592,14599,14625,14648,14669,14676,14683,14690,14697,14705,14730,14754],{"type":21,"tag":455,"props":14403,"children":14404},{"class":457,"line":458},[14405],{"type":21,"tag":455,"props":14406,"children":14407},{"style":462},[14408],{"type":26,"value":14409},"/*\n",{"type":21,"tag":455,"props":14411,"children":14412},{"class":457,"line":336},[14413],{"type":21,"tag":455,"props":14414,"children":14415},{"style":462},[14416],{"type":26,"value":14417}," *      Determine the position of this fragment.\n",{"type":21,"tag":455,"props":14419,"children":14420},{"class":457,"line":333},[14421],{"type":21,"tag":455,"props":14422,"children":14423},{"style":462},[14424],{"type":26,"value":14425}," */\n",{"type":21,"tag":455,"props":14427,"children":14428},{"class":457,"line":1424},[14429],{"type":21,"tag":455,"props":14430,"children":14431},{"emptyLinePlaceholder":471},[14432],{"type":26,"value":474},{"type":21,"tag":455,"props":14434,"children":14435},{"class":457,"line":1443},[14436,14441,14445,14449,14453,14458,14463,14467,14472,14476,14480],{"type":21,"tag":455,"props":14437,"children":14438},{"style":486},[14439],{"type":26,"value":14440},"end ",{"type":21,"tag":455,"props":14442,"children":14443},{"style":1349},[14444],{"type":26,"value":3193},{"type":21,"tag":455,"props":14446,"children":14447},{"style":486},[14448],{"type":26,"value":7308},{"type":21,"tag":455,"props":14450,"children":14451},{"style":1349},[14452],{"type":26,"value":2206},{"type":21,"tag":455,"props":14454,"children":14455},{"style":1365},[14456],{"type":26,"value":14457}," ntohs",{"type":21,"tag":455,"props":14459,"children":14460},{"style":486},[14461],{"type":26,"value":14462},"(iph",{"type":21,"tag":455,"props":14464,"children":14465},{"style":1349},[14466],{"type":26,"value":13082},{"type":21,"tag":455,"props":14468,"children":14469},{"style":4527},[14470],{"type":26,"value":14471},"tot_len",{"type":21,"tag":455,"props":14473,"children":14474},{"style":486},[14475],{"type":26,"value":4584},{"type":21,"tag":455,"props":14477,"children":14478},{"style":1349},[14479],{"type":26,"value":2702},{"type":21,"tag":455,"props":14481,"children":14482},{"style":486},[14483],{"type":26,"value":14484}," ihl;\n",{"type":21,"tag":455,"props":14486,"children":14487},{"class":457,"line":1489},[14488],{"type":21,"tag":455,"props":14489,"children":14490},{"emptyLinePlaceholder":471},[14491],{"type":26,"value":474},{"type":21,"tag":455,"props":14493,"children":14494},{"class":457,"line":1506},[14495],{"type":21,"tag":455,"props":14496,"children":14497},{"style":486},[14498],{"type":26,"value":14499},"...\n",{"type":21,"tag":455,"props":14501,"children":14502},{"class":457,"line":1547},[14503],{"type":21,"tag":455,"props":14504,"children":14505},{"emptyLinePlaceholder":471},[14506],{"type":26,"value":474},{"type":21,"tag":455,"props":14508,"children":14509},{"class":457,"line":1564},[14510],{"type":21,"tag":455,"props":14511,"children":14512},{"style":462},[14513],{"type":26,"value":14409},{"type":21,"tag":455,"props":14515,"children":14516},{"class":457,"line":1605},[14517],{"type":21,"tag":455,"props":14518,"children":14519},{"style":462},[14520],{"type":26,"value":14521}," *      We found where to put this one.\n",{"type":21,"tag":455,"props":14523,"children":14524},{"class":457,"line":1622},[14525],{"type":21,"tag":455,"props":14526,"children":14527},{"style":462},[14528],{"type":26,"value":14529}," *      Check for overlap with preceding fragment, and, if needed,\n",{"type":21,"tag":455,"props":14531,"children":14532},{"class":457,"line":1663},[14533],{"type":21,"tag":455,"props":14534,"children":14535},{"style":462},[14536],{"type":26,"value":14537}," *      align things so that any overlaps are eliminated.\n",{"type":21,"tag":455,"props":14539,"children":14540},{"class":457,"line":1680},[14541],{"type":21,"tag":455,"props":14542,"children":14543},{"style":462},[14544],{"type":26,"value":14425},{"type":21,"tag":455,"props":14546,"children":14547},{"class":457,"line":1721},[14548,14552,14557,14561,14566,14570,14574,14578,14583,14587],{"type":21,"tag":455,"props":14549,"children":14550},{"style":1349},[14551],{"type":26,"value":2041},{"type":21,"tag":455,"props":14553,"children":14554},{"style":486},[14555],{"type":26,"value":14556}," (prev ",{"type":21,"tag":455,"props":14558,"children":14559},{"style":1349},[14560],{"type":26,"value":11181},{"type":21,"tag":455,"props":14562,"children":14563},{"style":480},[14564],{"type":26,"value":14565}," NULL",{"type":21,"tag":455,"props":14567,"children":14568},{"style":1349},[14569],{"type":26,"value":1467},{"type":21,"tag":455,"props":14571,"children":14572},{"style":486},[14573],{"type":26,"value":7308},{"type":21,"tag":455,"props":14575,"children":14576},{"style":1349},[14577],{"type":26,"value":11627},{"type":21,"tag":455,"props":14579,"children":14580},{"style":486},[14581],{"type":26,"value":14582}," prev",{"type":21,"tag":455,"props":14584,"children":14585},{"style":1349},[14586],{"type":26,"value":13082},{"type":21,"tag":455,"props":14588,"children":14589},{"style":486},[14590],{"type":26,"value":14591},"end)\n",{"type":21,"tag":455,"props":14593,"children":14594},{"class":457,"line":1738},[14595],{"type":21,"tag":455,"props":14596,"children":14597},{"style":486},[14598],{"type":26,"value":1394},{"type":21,"tag":455,"props":14600,"children":14601},{"class":457,"line":1779},[14602,14607,14611,14616,14620],{"type":21,"tag":455,"props":14603,"children":14604},{"style":486},[14605],{"type":26,"value":14606},"        i ",{"type":21,"tag":455,"props":14608,"children":14609},{"style":1349},[14610],{"type":26,"value":3193},{"type":21,"tag":455,"props":14612,"children":14613},{"style":486},[14614],{"type":26,"value":14615}," prev->end ",{"type":21,"tag":455,"props":14617,"children":14618},{"style":1349},[14619],{"type":26,"value":2702},{"type":21,"tag":455,"props":14621,"children":14622},{"style":486},[14623],{"type":26,"value":14624}," offset;\n",{"type":21,"tag":455,"props":14626,"children":14627},{"class":457,"line":1796},[14628,14633,14638,14643],{"type":21,"tag":455,"props":14629,"children":14630},{"style":486},[14631],{"type":26,"value":14632},"        offset ",{"type":21,"tag":455,"props":14634,"children":14635},{"style":1349},[14636],{"type":26,"value":14637},"+=",{"type":21,"tag":455,"props":14639,"children":14640},{"style":486},[14641],{"type":26,"value":14642}," i;",{"type":21,"tag":455,"props":14644,"children":14645},{"style":462},[14646],{"type":26,"value":14647},"    /* ptr into datagram */\n",{"type":21,"tag":455,"props":14649,"children":14650},{"class":457,"line":1837},[14651,14656,14660,14664],{"type":21,"tag":455,"props":14652,"children":14653},{"style":486},[14654],{"type":26,"value":14655},"        ptr ",{"type":21,"tag":455,"props":14657,"children":14658},{"style":1349},[14659],{"type":26,"value":14637},{"type":21,"tag":455,"props":14661,"children":14662},{"style":486},[14663],{"type":26,"value":14642},{"type":21,"tag":455,"props":14665,"children":14666},{"style":462},[14667],{"type":26,"value":14668},"       /* ptr into fragment data */\n",{"type":21,"tag":455,"props":14670,"children":14671},{"class":457,"line":1854},[14672],{"type":21,"tag":455,"props":14673,"children":14674},{"style":486},[14675],{"type":26,"value":2002},{"type":21,"tag":455,"props":14677,"children":14678},{"class":457,"line":1895},[14679],{"type":21,"tag":455,"props":14680,"children":14681},{"emptyLinePlaceholder":471},[14682],{"type":26,"value":474},{"type":21,"tag":455,"props":14684,"children":14685},{"class":457,"line":1912},[14686],{"type":21,"tag":455,"props":14687,"children":14688},{"style":486},[14689],{"type":26,"value":14499},{"type":21,"tag":455,"props":14691,"children":14692},{"class":457,"line":1953},[14693],{"type":21,"tag":455,"props":14694,"children":14695},{"emptyLinePlaceholder":471},[14696],{"type":26,"value":474},{"type":21,"tag":455,"props":14698,"children":14699},{"class":457,"line":1970},[14700],{"type":21,"tag":455,"props":14701,"children":14702},{"style":462},[14703],{"type":26,"value":14704},"/* Fill in the structure. */\n",{"type":21,"tag":455,"props":14706,"children":14707},{"class":457,"line":1978},[14708,14713,14717,14722,14726],{"type":21,"tag":455,"props":14709,"children":14710},{"style":486},[14711],{"type":26,"value":14712},"fp",{"type":21,"tag":455,"props":14714,"children":14715},{"style":1349},[14716],{"type":26,"value":13082},{"type":21,"tag":455,"props":14718,"children":14719},{"style":486},[14720],{"type":26,"value":14721},"offset ",{"type":21,"tag":455,"props":14723,"children":14724},{"style":1349},[14725],{"type":26,"value":3193},{"type":21,"tag":455,"props":14727,"children":14728},{"style":486},[14729],{"type":26,"value":14624},{"type":21,"tag":455,"props":14731,"children":14732},{"class":457,"line":1996},[14733,14737,14741,14745,14749],{"type":21,"tag":455,"props":14734,"children":14735},{"style":486},[14736],{"type":26,"value":14712},{"type":21,"tag":455,"props":14738,"children":14739},{"style":1349},[14740],{"type":26,"value":13082},{"type":21,"tag":455,"props":14742,"children":14743},{"style":486},[14744],{"type":26,"value":14440},{"type":21,"tag":455,"props":14746,"children":14747},{"style":1349},[14748],{"type":26,"value":3193},{"type":21,"tag":455,"props":14750,"children":14751},{"style":486},[14752],{"type":26,"value":14753}," end;\n",{"type":21,"tag":455,"props":14755,"children":14756},{"class":457,"line":4487},[14757,14761,14765,14770,14774,14779,14783],{"type":21,"tag":455,"props":14758,"children":14759},{"style":486},[14760],{"type":26,"value":14712},{"type":21,"tag":455,"props":14762,"children":14763},{"style":1349},[14764],{"type":26,"value":13082},{"type":21,"tag":455,"props":14766,"children":14767},{"style":486},[14768],{"type":26,"value":14769},"len ",{"type":21,"tag":455,"props":14771,"children":14772},{"style":1349},[14773],{"type":26,"value":3193},{"type":21,"tag":455,"props":14775,"children":14776},{"style":486},[14777],{"type":26,"value":14778}," end ",{"type":21,"tag":455,"props":14780,"children":14781},{"style":1349},[14782],{"type":26,"value":2702},{"type":21,"tag":455,"props":14784,"children":14785},{"style":486},[14786],{"type":26,"value":14624},{"type":21,"tag":22,"props":14788,"children":14789},{},[14790,14796,14798,14803,14805,14810,14812,14818,14820,14825,14826,14832],{"type":21,"tag":246,"props":14791,"children":14793},{"className":14792},[],[14794],{"type":26,"value":14795},"offset",{"type":26,"value":14797}," is the fragment's offset in the complete datagram. So if ",{"type":21,"tag":246,"props":14799,"children":14801},{"className":14800},[],[14802],{"type":26,"value":14795},{"type":26,"value":14804}," is before the end of the previous fragment, then the middle ",{"type":21,"tag":246,"props":14806,"children":14808},{"className":14807},[],[14809],{"type":26,"value":2041},{"type":26,"value":14811}," block above sets it to the value of ",{"type":21,"tag":246,"props":14813,"children":14815},{"className":14814},[],[14816],{"type":26,"value":14817},"prev->end",{"type":26,"value":14819},", eliminating the overlap. Finally, length of the fragment is updated based on the new ",{"type":21,"tag":246,"props":14821,"children":14823},{"className":14822},[],[14824],{"type":26,"value":14795},{"type":26,"value":386},{"type":21,"tag":246,"props":14827,"children":14829},{"className":14828},[],[14830],{"type":26,"value":14831},"end",{"type":26,"value":14833}," of the fragment.",{"type":21,"tag":56,"props":14835,"children":14836},{"id":12986},[14837],{"type":26,"value":12989},{"type":21,"tag":22,"props":14839,"children":14840},{},[14841,14843],{"type":26,"value":14842},"Whenever we're using arithmetic to compute a size or length, we need to ask ourselves: ",{"type":21,"tag":33,"props":14844,"children":14845},{},[14846],{"type":26,"value":14847},"can this turn out negative?",{"type":21,"tag":239,"props":14849,"children":14851},{"className":11036,"code":14850,"language":11038,"meta":8,"style":8},"fp->len = end - offset;\n",[14852],{"type":21,"tag":246,"props":14853,"children":14854},{"__ignoreMap":8},[14855],{"type":21,"tag":455,"props":14856,"children":14857},{"class":457,"line":458},[14858,14862,14866,14870,14874,14878,14882],{"type":21,"tag":455,"props":14859,"children":14860},{"style":486},[14861],{"type":26,"value":14712},{"type":21,"tag":455,"props":14863,"children":14864},{"style":1349},[14865],{"type":26,"value":13082},{"type":21,"tag":455,"props":14867,"children":14868},{"style":486},[14869],{"type":26,"value":14769},{"type":21,"tag":455,"props":14871,"children":14872},{"style":1349},[14873],{"type":26,"value":3193},{"type":21,"tag":455,"props":14875,"children":14876},{"style":486},[14877],{"type":26,"value":14778},{"type":21,"tag":455,"props":14879,"children":14880},{"style":1349},[14881],{"type":26,"value":2702},{"type":21,"tag":455,"props":14883,"children":14884},{"style":486},[14885],{"type":26,"value":14624},{"type":21,"tag":22,"props":14887,"children":14888},{},[14889,14891,14896,14898,14904,14906,14911,14913,14919],{"type":26,"value":14890},"At first glance, it seems like the answer is \"no\" for this fragment: ",{"type":21,"tag":246,"props":14892,"children":14894},{"className":14893},[],[14895],{"type":26,"value":14831},{"type":26,"value":14897}," is defined by adding the fragment's payload length (definitely ",{"type":21,"tag":246,"props":14899,"children":14901},{"className":14900},[],[14902],{"type":26,"value":14903},">= 0",{"type":26,"value":14905},") to the value of ",{"type":21,"tag":246,"props":14907,"children":14909},{"className":14908},[],[14910],{"type":26,"value":14795},{"type":26,"value":14912},", so it would seem that ",{"type":21,"tag":246,"props":14914,"children":14916},{"className":14915},[],[14917],{"type":26,"value":14918},"end - offset >= 0",{"type":26,"value":14920}," must be true.",{"type":21,"tag":22,"props":14922,"children":14923},{},[14924,14926,14931,14933,14939,14941,14946,14948,14953,14955,14961,14963,14969],{"type":26,"value":14925},"However, this analysis ignores what might have happened in between the assignment of ",{"type":21,"tag":246,"props":14927,"children":14929},{"className":14928},[],[14930],{"type":26,"value":14831},{"type":26,"value":14932}," and the evaluation of ",{"type":21,"tag":246,"props":14934,"children":14936},{"className":14935},[],[14937],{"type":26,"value":14938},"end - offset",{"type":26,"value":14940},"; in particular, the handling of overlap. ",{"type":21,"tag":246,"props":14942,"children":14944},{"className":14943},[],[14945],{"type":26,"value":14795},{"type":26,"value":14947}," effectively gets set to ",{"type":21,"tag":246,"props":14949,"children":14951},{"className":14950},[],[14952],{"type":26,"value":14817},{"type":26,"value":14954}," (the end of the previous fragment), but still, this fragment should at worst be a duplicate of the previous one, meaning ",{"type":21,"tag":246,"props":14956,"children":14958},{"className":14957},[],[14959],{"type":26,"value":14960},"prev->end == end",{"type":26,"value":14962},", and consequently ",{"type":21,"tag":246,"props":14964,"children":14966},{"className":14965},[],[14967],{"type":26,"value":14968},"end - offset == 0",{"type":26,"value":14970},". So after a second look, this might still look problem-free.",{"type":21,"tag":22,"props":14972,"children":14973},{},[14974],{"type":26,"value":14975},"The dangerous word here is \"should\". Suppose a pair of fragments comes in that look more like this:",{"type":21,"tag":239,"props":14977,"children":14980},{"className":14978,"code":14979,"language":26},[242],"0                    22\nv                     v\n|-----Fragment--1-----|\n    |--Fragment--2--|\n    ^               ^\n    4              20\n",[14981],{"type":21,"tag":246,"props":14982,"children":14983},{"__ignoreMap":8},[14984],{"type":26,"value":14979},{"type":21,"tag":22,"props":14986,"children":14987},{},[14988,14990,14995],{"type":26,"value":14989},"This is obviously wrong and shouldn't happen; fragments might overlap or duplicate on another, but they should never be a strict subset of another fragment. Maybe so, but ",{"type":21,"tag":33,"props":14991,"children":14992},{},[14993],{"type":26,"value":14994},"can they, anyway?",{"type":26,"value":14996}," It turns out the answer is yes, they can, for example if they're deliberately crafted that way by a malicious sender.",{"type":21,"tag":22,"props":14998,"children":14999},{},[15000,15002,15007,15009,15014,15016,15022,15024,15030],{"type":26,"value":15001},"If the length of the second fragment is not enough to \"cover\" the overlap correction, then the updated ",{"type":21,"tag":246,"props":15003,"children":15005},{"className":15004},[],[15006],{"type":26,"value":14795},{"type":26,"value":15008}," of the fragment becomes greater than its ",{"type":21,"tag":246,"props":15010,"children":15012},{"className":15011},[],[15013],{"type":26,"value":14831},{"type":26,"value":15015},", and consequently the computed ",{"type":21,"tag":246,"props":15017,"children":15019},{"className":15018},[],[15020],{"type":26,"value":15021},"fp->len",{"type":26,"value":15023}," is negative. This length eventually gets passed to ",{"type":21,"tag":246,"props":15025,"children":15027},{"className":15026},[],[15028],{"type":26,"value":15029},"memcpy()",{"type":26,"value":15031},", causing a buffer overflow and an extremely large copy that will lock up or crash the system.",{"type":21,"tag":56,"props":15033,"children":15035},{"id":15034},"what-went-wrong",[15036],{"type":26,"value":15037},"What Went Wrong?",{"type":21,"tag":22,"props":15039,"children":15040},{},[15041,15043,15048,15050,15055],{"type":26,"value":15042},"The root cause here was arguably a misplaced trust in untrusted input; it's a very easy mistake to make as a developer, since we naturally expect that all parts of a system ",{"type":21,"tag":33,"props":15044,"children":15045},{},[15046],{"type":26,"value":15047},"want",{"type":26,"value":15049}," to work correctly (even the parts that we don't control). In this case, input is coming from a completely untrusted source, and while there's a whole class of inputs which a robust sender should never send, there's nothing which ",{"type":21,"tag":33,"props":15051,"children":15052},{},[15053],{"type":26,"value":15054},"prevents",{"type":26,"value":15056}," a sender from doing so.",{"type":21,"tag":22,"props":15058,"children":15059},{},[15060,15062,15068],{"type":26,"value":15061},"Given that the input can contain a fragment which is a strict subset of the previous fragment, the implementation needs to guard against this possibility, for example with a ",{"type":21,"tag":246,"props":15063,"children":15065},{"className":15064},[],[15066],{"type":26,"value":15067},"if (offset > end)",{"type":26,"value":15069}," check.",{"type":21,"tag":56,"props":15071,"children":15072},{"id":14288},[15073],{"type":26,"value":14291},{"type":21,"tag":2953,"props":15075,"children":15076},{},[15077,15088,15093],{"type":21,"tag":187,"props":15078,"children":15079},{},[15080,15082,15086],{"type":26,"value":15081},"Whenever untrusted data is being processed, it should be treated with the mindset of an attacker: consider not only the expected format of the input, but rather ",{"type":21,"tag":33,"props":15083,"children":15084},{},[15085],{"type":26,"value":7549},{"type":26,"value":15087}," possible formats in could take.",{"type":21,"tag":187,"props":15089,"children":15090},{},[15091],{"type":26,"value":15092},"Whenever a length or size is being computed, particularly in a memory-unsafe language like C, verify that it's not possible for the value to be negative or otherwise invalid.",{"type":21,"tag":187,"props":15094,"children":15095},{},[15096,15098],{"type":26,"value":15097},"When working with data ranges, carefully consider the edge cases, such as:\n",{"type":21,"tag":2953,"props":15099,"children":15100},{},[15101,15106,15111,15116],{"type":21,"tag":187,"props":15102,"children":15103},{},[15104],{"type":26,"value":15105},"Are the ranges always ordered by their start index?",{"type":21,"tag":187,"props":15107,"children":15108},{},[15109],{"type":26,"value":15110},"Can zero-length ranges exist?",{"type":21,"tag":187,"props":15112,"children":15113},{},[15114],{"type":26,"value":15115},"Must the ranges be contiguous? (Can gaps exist between them?)",{"type":21,"tag":187,"props":15117,"children":15118},{},[15119],{"type":26,"value":15120},"Can the ranges overlap? If so, are all types of overlap handled? (Adjacent, partial overlap, duplicate, strict subset)",{"type":21,"tag":1279,"props":15122,"children":15123},{},[15124],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":15126},[15127,15128,15129,15130,15131],{"id":14356,"depth":336,"text":14359},{"id":14372,"depth":336,"text":14375},{"id":12986,"depth":336,"text":12989},{"id":15034,"depth":336,"text":15037},{"id":14288,"depth":336,"text":14291},"content:phendry:2021-07-30:SpotTheVulnDataRanges.md","phendry/2021-07-30/SpotTheVulnDataRanges.md","phendry/2021-07-30/SpotTheVulnDataRanges",{"user":2795,"name":2796},{"_path":15137,"_dir":15138,"_draft":7,"_partial":7,"_locale":8,"title":15139,"description":15140,"excerpt":15140,"tags":15141,"publishDate":15144,"body":15145,"_type":343,"_id":16600,"_source":345,"_file":16601,"_stem":16602,"_extension":348,"author":16603},"/ckeefer/2017-3/morepwatoya-part2","2017-3","More PWA to Ya! (Progressive Web Apps, Part 2)","Last time, we got into the nitty gritty on how to make your web application into a Progressive Web Application (PWA to it's friends). I promised we'd dig even deeper this time, and show you how to make your web app a little more 'native' on Android - and how to deal with iOS Safari's special snowflake syndrome.",[15142,15143,15],"mobile","pwa","2017-03-01",{"type":18,"children":15146,"toc":16594},[15147,15158,15164,15178,15201,15206,15603,15608,15641,15652,15663,15674,15685,15703,15722,15735,15748,15811,15816,15821,15827,15832,15837,15851,15873,16463,16477,16506,16519,16525,16530,16544,16567,16573,16585,16590],{"type":21,"tag":22,"props":15148,"children":15149},{},[15150,15156],{"type":21,"tag":322,"props":15151,"children":15153},{"href":15152},"/search/user:ckeefer/more/pwa",[15154],{"type":26,"value":15155},"Last time",{"type":26,"value":15157},", we got into the nitty gritty on how to make your web application into a Progressive Web Application (PWA to it's friends). I promised we'd dig even deeper this time, and show you how to make your web app a little more 'native' on Android - and how to deal with iOS Safari's special snowflake syndrome.",{"type":21,"tag":56,"props":15159,"children":15161},{"id":15160},"manifestly-so",[15162],{"type":26,"value":15163},"Manifestly So",{"type":21,"tag":22,"props":15165,"children":15166},{},[15167,15169,15176],{"type":26,"value":15168},"The key to gaining first-class citizen status on Android for our PWA is the ",{"type":21,"tag":322,"props":15170,"children":15173},{"href":15171,"rel":15172},"https://developer.mozilla.org/en-US/docs/Web/Manifest",[326],[15174],{"type":26,"value":15175},"Manifest",{"type":26,"value":15177},". This allows us to declare certain details about our PWA, and how we'd like it to be handled by the browser. We won't go into all the possibilities - see the MDN link before for all of the details - but instead look at a few common scenarios, namely:",{"type":21,"tag":183,"props":15179,"children":15180},{},[15181,15186,15191,15196],{"type":21,"tag":187,"props":15182,"children":15183},{},[15184],{"type":26,"value":15185},"You want to specify a certain application name, and 'friendly' short form of that name for display in settings and on the home screen;",{"type":21,"tag":187,"props":15187,"children":15188},{},[15189],{"type":26,"value":15190},"Your app can be installed to the Home Screen from the web app site, and will automatically prompt the user to do so;",{"type":21,"tag":187,"props":15192,"children":15193},{},[15194],{"type":26,"value":15195},"You want to specify some values for things like background color, theme color, and icons, to control the way the app will look on the home screen, and how it will look when it's loading;",{"type":21,"tag":187,"props":15197,"children":15198},{},[15199],{"type":26,"value":15200},"You want to lock the app into a certain display orientation.",{"type":21,"tag":22,"props":15202,"children":15203},{},[15204],{"type":26,"value":15205},"To that effect, here's an example Manifest, which we'll be dissecting momentarily:",{"type":21,"tag":239,"props":15207,"children":15210},{"className":15208,"code":15209,"language":3544,"meta":8,"style":8},"language-json shiki shiki-themes github-light github-dark","{\n    \"short_name\": \"App Name\",\n    \"name\": \"Long App Name for Settings Or Lock Screen\",\n    \"background_color\": \"#hexcode\",\n    \"theme_color\": \"#hexcode\",\n    \"display\": \"standalone\",\n    \"orientation\": \"portrait\",\n    \"icons\": [\n        {\n            \"src\": \"/static/img/icon180x180.png\",\n            \"type\": \"image/png\",\n            \"sizes\": \"180x180\"\n        },\n        {\n            \"src\": \"/static/img/icon192x192.png\",\n            \"type\": \"image/png\",\n            \"sizes\": \"192x192\"\n        },\n        {\n            \"src\": \"/static/img/icon512x512.png\",\n            \"type\": \"image/png\",\n            \"sizes\": \"512x512\"\n        }\n    ],\n    \"start_url\": \"/\"\n}\n",[15211],{"type":21,"tag":246,"props":15212,"children":15213},{"__ignoreMap":8},[15214,15221,15242,15263,15284,15304,15325,15346,15359,15367,15388,15409,15426,15433,15440,15460,15479,15495,15502,15509,15529,15548,15564,15571,15579,15596],{"type":21,"tag":455,"props":15215,"children":15216},{"class":457,"line":458},[15217],{"type":21,"tag":455,"props":15218,"children":15219},{"style":486},[15220],{"type":26,"value":1394},{"type":21,"tag":455,"props":15222,"children":15223},{"class":457,"line":336},[15224,15229,15233,15238],{"type":21,"tag":455,"props":15225,"children":15226},{"style":480},[15227],{"type":26,"value":15228},"    \"short_name\"",{"type":21,"tag":455,"props":15230,"children":15231},{"style":486},[15232],{"type":26,"value":7129},{"type":21,"tag":455,"props":15234,"children":15235},{"style":492},[15236],{"type":26,"value":15237},"\"App Name\"",{"type":21,"tag":455,"props":15239,"children":15240},{"style":486},[15241],{"type":26,"value":2483},{"type":21,"tag":455,"props":15243,"children":15244},{"class":457,"line":333},[15245,15250,15254,15259],{"type":21,"tag":455,"props":15246,"children":15247},{"style":480},[15248],{"type":26,"value":15249},"    \"name\"",{"type":21,"tag":455,"props":15251,"children":15252},{"style":486},[15253],{"type":26,"value":7129},{"type":21,"tag":455,"props":15255,"children":15256},{"style":492},[15257],{"type":26,"value":15258},"\"Long App Name for Settings Or Lock Screen\"",{"type":21,"tag":455,"props":15260,"children":15261},{"style":486},[15262],{"type":26,"value":2483},{"type":21,"tag":455,"props":15264,"children":15265},{"class":457,"line":1424},[15266,15271,15275,15280],{"type":21,"tag":455,"props":15267,"children":15268},{"style":480},[15269],{"type":26,"value":15270},"    \"background_color\"",{"type":21,"tag":455,"props":15272,"children":15273},{"style":486},[15274],{"type":26,"value":7129},{"type":21,"tag":455,"props":15276,"children":15277},{"style":492},[15278],{"type":26,"value":15279},"\"#hexcode\"",{"type":21,"tag":455,"props":15281,"children":15282},{"style":486},[15283],{"type":26,"value":2483},{"type":21,"tag":455,"props":15285,"children":15286},{"class":457,"line":1443},[15287,15292,15296,15300],{"type":21,"tag":455,"props":15288,"children":15289},{"style":480},[15290],{"type":26,"value":15291},"    \"theme_color\"",{"type":21,"tag":455,"props":15293,"children":15294},{"style":486},[15295],{"type":26,"value":7129},{"type":21,"tag":455,"props":15297,"children":15298},{"style":492},[15299],{"type":26,"value":15279},{"type":21,"tag":455,"props":15301,"children":15302},{"style":486},[15303],{"type":26,"value":2483},{"type":21,"tag":455,"props":15305,"children":15306},{"class":457,"line":1489},[15307,15312,15316,15321],{"type":21,"tag":455,"props":15308,"children":15309},{"style":480},[15310],{"type":26,"value":15311},"    \"display\"",{"type":21,"tag":455,"props":15313,"children":15314},{"style":486},[15315],{"type":26,"value":7129},{"type":21,"tag":455,"props":15317,"children":15318},{"style":492},[15319],{"type":26,"value":15320},"\"standalone\"",{"type":21,"tag":455,"props":15322,"children":15323},{"style":486},[15324],{"type":26,"value":2483},{"type":21,"tag":455,"props":15326,"children":15327},{"class":457,"line":1506},[15328,15333,15337,15342],{"type":21,"tag":455,"props":15329,"children":15330},{"style":480},[15331],{"type":26,"value":15332},"    \"orientation\"",{"type":21,"tag":455,"props":15334,"children":15335},{"style":486},[15336],{"type":26,"value":7129},{"type":21,"tag":455,"props":15338,"children":15339},{"style":492},[15340],{"type":26,"value":15341},"\"portrait\"",{"type":21,"tag":455,"props":15343,"children":15344},{"style":486},[15345],{"type":26,"value":2483},{"type":21,"tag":455,"props":15347,"children":15348},{"class":457,"line":1547},[15349,15354],{"type":21,"tag":455,"props":15350,"children":15351},{"style":480},[15352],{"type":26,"value":15353},"    \"icons\"",{"type":21,"tag":455,"props":15355,"children":15356},{"style":486},[15357],{"type":26,"value":15358},": [\n",{"type":21,"tag":455,"props":15360,"children":15361},{"class":457,"line":1564},[15362],{"type":21,"tag":455,"props":15363,"children":15364},{"style":486},[15365],{"type":26,"value":15366},"        {\n",{"type":21,"tag":455,"props":15368,"children":15369},{"class":457,"line":1605},[15370,15375,15379,15384],{"type":21,"tag":455,"props":15371,"children":15372},{"style":480},[15373],{"type":26,"value":15374},"            \"src\"",{"type":21,"tag":455,"props":15376,"children":15377},{"style":486},[15378],{"type":26,"value":7129},{"type":21,"tag":455,"props":15380,"children":15381},{"style":492},[15382],{"type":26,"value":15383},"\"/static/img/icon180x180.png\"",{"type":21,"tag":455,"props":15385,"children":15386},{"style":486},[15387],{"type":26,"value":2483},{"type":21,"tag":455,"props":15389,"children":15390},{"class":457,"line":1622},[15391,15396,15400,15405],{"type":21,"tag":455,"props":15392,"children":15393},{"style":480},[15394],{"type":26,"value":15395},"            \"type\"",{"type":21,"tag":455,"props":15397,"children":15398},{"style":486},[15399],{"type":26,"value":7129},{"type":21,"tag":455,"props":15401,"children":15402},{"style":492},[15403],{"type":26,"value":15404},"\"image/png\"",{"type":21,"tag":455,"props":15406,"children":15407},{"style":486},[15408],{"type":26,"value":2483},{"type":21,"tag":455,"props":15410,"children":15411},{"class":457,"line":1663},[15412,15417,15421],{"type":21,"tag":455,"props":15413,"children":15414},{"style":480},[15415],{"type":26,"value":15416},"            \"sizes\"",{"type":21,"tag":455,"props":15418,"children":15419},{"style":486},[15420],{"type":26,"value":7129},{"type":21,"tag":455,"props":15422,"children":15423},{"style":492},[15424],{"type":26,"value":15425},"\"180x180\"\n",{"type":21,"tag":455,"props":15427,"children":15428},{"class":457,"line":1680},[15429],{"type":21,"tag":455,"props":15430,"children":15431},{"style":486},[15432],{"type":26,"value":7949},{"type":21,"tag":455,"props":15434,"children":15435},{"class":457,"line":1721},[15436],{"type":21,"tag":455,"props":15437,"children":15438},{"style":486},[15439],{"type":26,"value":15366},{"type":21,"tag":455,"props":15441,"children":15442},{"class":457,"line":1738},[15443,15447,15451,15456],{"type":21,"tag":455,"props":15444,"children":15445},{"style":480},[15446],{"type":26,"value":15374},{"type":21,"tag":455,"props":15448,"children":15449},{"style":486},[15450],{"type":26,"value":7129},{"type":21,"tag":455,"props":15452,"children":15453},{"style":492},[15454],{"type":26,"value":15455},"\"/static/img/icon192x192.png\"",{"type":21,"tag":455,"props":15457,"children":15458},{"style":486},[15459],{"type":26,"value":2483},{"type":21,"tag":455,"props":15461,"children":15462},{"class":457,"line":1779},[15463,15467,15471,15475],{"type":21,"tag":455,"props":15464,"children":15465},{"style":480},[15466],{"type":26,"value":15395},{"type":21,"tag":455,"props":15468,"children":15469},{"style":486},[15470],{"type":26,"value":7129},{"type":21,"tag":455,"props":15472,"children":15473},{"style":492},[15474],{"type":26,"value":15404},{"type":21,"tag":455,"props":15476,"children":15477},{"style":486},[15478],{"type":26,"value":2483},{"type":21,"tag":455,"props":15480,"children":15481},{"class":457,"line":1796},[15482,15486,15490],{"type":21,"tag":455,"props":15483,"children":15484},{"style":480},[15485],{"type":26,"value":15416},{"type":21,"tag":455,"props":15487,"children":15488},{"style":486},[15489],{"type":26,"value":7129},{"type":21,"tag":455,"props":15491,"children":15492},{"style":492},[15493],{"type":26,"value":15494},"\"192x192\"\n",{"type":21,"tag":455,"props":15496,"children":15497},{"class":457,"line":1837},[15498],{"type":21,"tag":455,"props":15499,"children":15500},{"style":486},[15501],{"type":26,"value":7949},{"type":21,"tag":455,"props":15503,"children":15504},{"class":457,"line":1854},[15505],{"type":21,"tag":455,"props":15506,"children":15507},{"style":486},[15508],{"type":26,"value":15366},{"type":21,"tag":455,"props":15510,"children":15511},{"class":457,"line":1895},[15512,15516,15520,15525],{"type":21,"tag":455,"props":15513,"children":15514},{"style":480},[15515],{"type":26,"value":15374},{"type":21,"tag":455,"props":15517,"children":15518},{"style":486},[15519],{"type":26,"value":7129},{"type":21,"tag":455,"props":15521,"children":15522},{"style":492},[15523],{"type":26,"value":15524},"\"/static/img/icon512x512.png\"",{"type":21,"tag":455,"props":15526,"children":15527},{"style":486},[15528],{"type":26,"value":2483},{"type":21,"tag":455,"props":15530,"children":15531},{"class":457,"line":1912},[15532,15536,15540,15544],{"type":21,"tag":455,"props":15533,"children":15534},{"style":480},[15535],{"type":26,"value":15395},{"type":21,"tag":455,"props":15537,"children":15538},{"style":486},[15539],{"type":26,"value":7129},{"type":21,"tag":455,"props":15541,"children":15542},{"style":492},[15543],{"type":26,"value":15404},{"type":21,"tag":455,"props":15545,"children":15546},{"style":486},[15547],{"type":26,"value":2483},{"type":21,"tag":455,"props":15549,"children":15550},{"class":457,"line":1953},[15551,15555,15559],{"type":21,"tag":455,"props":15552,"children":15553},{"style":480},[15554],{"type":26,"value":15416},{"type":21,"tag":455,"props":15556,"children":15557},{"style":486},[15558],{"type":26,"value":7129},{"type":21,"tag":455,"props":15560,"children":15561},{"style":492},[15562],{"type":26,"value":15563},"\"512x512\"\n",{"type":21,"tag":455,"props":15565,"children":15566},{"class":457,"line":1970},[15567],{"type":21,"tag":455,"props":15568,"children":15569},{"style":486},[15570],{"type":26,"value":5680},{"type":21,"tag":455,"props":15572,"children":15573},{"class":457,"line":1978},[15574],{"type":21,"tag":455,"props":15575,"children":15576},{"style":486},[15577],{"type":26,"value":15578},"    ],\n",{"type":21,"tag":455,"props":15580,"children":15581},{"class":457,"line":1996},[15582,15587,15591],{"type":21,"tag":455,"props":15583,"children":15584},{"style":480},[15585],{"type":26,"value":15586},"    \"start_url\"",{"type":21,"tag":455,"props":15588,"children":15589},{"style":486},[15590],{"type":26,"value":7129},{"type":21,"tag":455,"props":15592,"children":15593},{"style":492},[15594],{"type":26,"value":15595},"\"/\"\n",{"type":21,"tag":455,"props":15597,"children":15598},{"class":457,"line":4487},[15599],{"type":21,"tag":455,"props":15600,"children":15601},{"style":486},[15602],{"type":26,"value":2002},{"type":21,"tag":22,"props":15604,"children":15605},{},[15606],{"type":26,"value":15607},"So, let's go through this line-by-line.",{"type":21,"tag":22,"props":15609,"children":15610},{},[15611,15612,15618,15619,15625,15627,15632,15634,15639],{"type":26,"value":8737},{"type":21,"tag":246,"props":15613,"children":15615},{"className":15614},[],[15616],{"type":26,"value":15617},"short_name",{"type":26,"value":386},{"type":21,"tag":246,"props":15620,"children":15622},{"className":15621},[],[15623],{"type":26,"value":15624},"name",{"type":26,"value":15626}," properties are fairly self-explanatory. The ",{"type":21,"tag":246,"props":15628,"children":15630},{"className":15629},[],[15631],{"type":26,"value":15617},{"type":26,"value":15633}," will be displayed anywhere the OS decides the ",{"type":21,"tag":246,"props":15635,"children":15637},{"className":15636},[],[15638],{"type":26,"value":15624},{"type":26,"value":15640}," won't fit. These could, of course, simply be the same string. How the OS deals with short_names that are still too long is up to the OS, but you can expect truncation of your name, so try and keep the short_name pithy.",{"type":21,"tag":22,"props":15642,"children":15643},{},[15644,15650],{"type":21,"tag":246,"props":15645,"children":15647},{"className":15646},[],[15648],{"type":26,"value":15649},"background_color",{"type":26,"value":15651}," is an important one, as this is going to get used by the OS to generate the 'splash screen' that the user sees when they load your application. If you specify icons, the background-color will be displayed behind the chosen icon.",{"type":21,"tag":22,"props":15653,"children":15654},{},[15655,15661],{"type":21,"tag":246,"props":15656,"children":15658},{"className":15657},[],[15659],{"type":26,"value":15660},"theme_color",{"type":26,"value":15662}," is a little more ambiguous, as what's done with it is up to the OS, and might change; right now, specifying it will determine what colour surrounds the application and is used for it in the task switcher.",{"type":21,"tag":22,"props":15664,"children":15665},{},[15666,15672],{"type":21,"tag":246,"props":15667,"children":15669},{"className":15668},[],[15670],{"type":26,"value":15671},"display",{"type":26,"value":15673}," is another big one - it determines how 'app-like' your PWA is going to appear. If you specify 'standalone', as we have here, it should look and feel like a standalone application - the browser chrome should be excluded, but items like the status bar can be displayed. This is suitable for a LoB-type application. There's also 'fullscreen', which gives you every last pixel - perfect for games - 'minimal-ui', which incorporates some of the browser chrome (how much depends on the browser), and 'browser', which leaves all of the browser chrome around your PWA intact, as though it were being viewed in the browser.",{"type":21,"tag":22,"props":15675,"children":15676},{},[15677,15683],{"type":21,"tag":246,"props":15678,"children":15680},{"className":15679},[],[15681],{"type":26,"value":15682},"orientation",{"type":26,"value":15684}," is another important one for an app - it allows you to specify that the application should be locked to 'portrait' or 'landscape' orientation, amongst a host of other possible values that you can see detailed in the MDN doc linked above.",{"type":21,"tag":22,"props":15686,"children":15687},{},[15688,15694,15696,15701],{"type":21,"tag":246,"props":15689,"children":15691},{"className":15690},[],[15692],{"type":26,"value":15693},"icons",{"type":26,"value":15695}," is, as mentioned, the icons that will be used for display on the home screen and when generating the 'splash screen' the user will see when loading the application. You should specify an array of options to handle different screen sizes and the different contexts in which an icon will be used (e.g. task switcher, home screen, splash screen, etc.). Relative URLs are resolved relative to the location of the manifest.json file (more on that in a minute); you'll notice we're using all PNG files for our icons, but they don't have to be... unless you're also planning to use them with iOS (and more on ",{"type":21,"tag":33,"props":15697,"children":15698},{},[15699],{"type":26,"value":15700},"that",{"type":26,"value":15702}," in a minute as well).",{"type":21,"tag":22,"props":15704,"children":15705},{},[15706,15707,15713,15715,15720],{"type":26,"value":6804},{"type":21,"tag":246,"props":15708,"children":15710},{"className":15709},[],[15711],{"type":26,"value":15712},"start_url",{"type":26,"value":15714}," is where we want the PWA to be when it loads from the home screen (as it's possible that the user could have chosen to add the PWA to the home screen from any url on your web app, and without this ",{"type":21,"tag":246,"props":15716,"children":15718},{"className":15717},[],[15719],{"type":26,"value":15712},{"type":26,"value":15721}," specified, that would become the start_url for your PWA).",{"type":21,"tag":22,"props":15723,"children":15724},{},[15725,15727,15733],{"type":26,"value":15726},"All of these details should live in a file called ",{"type":21,"tag":246,"props":15728,"children":15730},{"className":15729},[],[15731],{"type":26,"value":15732},"manifest.json",{"type":26,"value":15734}," and should be somewhere your web server can serve static files from, as you'll be linking to it in the head of your base html file.",{"type":21,"tag":22,"props":15736,"children":15737},{},[15738,15740,15746],{"type":26,"value":15739},"Somewhere in the ",{"type":21,"tag":246,"props":15741,"children":15743},{"className":15742},[],[15744],{"type":26,"value":15745},"\u003Chead>\u003C/head>",{"type":26,"value":15747}," of your html index, you should add the following meta tag:",{"type":21,"tag":239,"props":15749,"children":15753},{"className":15750,"code":15751,"language":15752,"meta":8,"style":8},"language-html shiki shiki-themes github-light github-dark","\u003C!-- PWA Manifest -->\n\u003Clink rel=\"manifest\" href=\"/path/to/manifest.json\">\n","html",[15754],{"type":21,"tag":246,"props":15755,"children":15756},{"__ignoreMap":8},[15757,15765],{"type":21,"tag":455,"props":15758,"children":15759},{"class":457,"line":458},[15760],{"type":21,"tag":455,"props":15761,"children":15762},{"style":462},[15763],{"type":26,"value":15764},"\u003C!-- PWA Manifest -->\n",{"type":21,"tag":455,"props":15766,"children":15767},{"class":457,"line":336},[15768,15772,15778,15783,15787,15792,15797,15801,15806],{"type":21,"tag":455,"props":15769,"children":15770},{"style":486},[15771],{"type":26,"value":11627},{"type":21,"tag":455,"props":15773,"children":15775},{"style":15774},"--shiki-default:#22863A;--shiki-dark:#85E89D",[15776],{"type":26,"value":15777},"link",{"type":21,"tag":455,"props":15779,"children":15780},{"style":1365},[15781],{"type":26,"value":15782}," rel",{"type":21,"tag":455,"props":15784,"children":15785},{"style":486},[15786],{"type":26,"value":3193},{"type":21,"tag":455,"props":15788,"children":15789},{"style":492},[15790],{"type":26,"value":15791},"\"manifest\"",{"type":21,"tag":455,"props":15793,"children":15794},{"style":1365},[15795],{"type":26,"value":15796}," href",{"type":21,"tag":455,"props":15798,"children":15799},{"style":486},[15800],{"type":26,"value":3193},{"type":21,"tag":455,"props":15802,"children":15803},{"style":492},[15804],{"type":26,"value":15805},"\"/path/to/manifest.json\"",{"type":21,"tag":455,"props":15807,"children":15808},{"style":486},[15809],{"type":26,"value":15810},">\n",{"type":21,"tag":22,"props":15812,"children":15813},{},[15814],{"type":26,"value":15815},"That's it for Android, you're ready to go!",{"type":21,"tag":22,"props":15817,"children":15818},{},[15819],{"type":26,"value":15820},"Now, as long as you're adding meta tags, let's look at the slew of meta tags needed for PWA support on iOS.",{"type":21,"tag":56,"props":15822,"children":15824},{"id":15823},"snowflakes-arent-that-special",[15825],{"type":26,"value":15826},"Snowflakes aren't that Special",{"type":21,"tag":22,"props":15828,"children":15829},{},[15830],{"type":26,"value":15831},"So, by now you're probably pretty excited - you've basically got this PWA thing working, right? Well, sure - unless you need to support iOS.",{"type":21,"tag":22,"props":15833,"children":15834},{},[15835],{"type":26,"value":15836},"Because iOS doesn't support Service Worker. Which means it doesn't support PWAs.",{"type":21,"tag":22,"props":15838,"children":15839},{},[15840,15842,15849],{"type":26,"value":15841},"But wait! No need for that wail of despair (or that sixth whiskey) - I haven't lead you down here just to dash all your hopes. ",{"type":21,"tag":322,"props":15843,"children":15846},{"href":15844,"rel":15845},"https://webkit.org/status/#specification-service-workers",[326],[15847],{"type":26,"value":15848},"iOS doesn't support Service Worker yet, but they're working on it",{"type":26,"value":15850},". Whether they'll also support the manifest functionality is uncertain, but Service Workers are really the beating heart of PWAs, so one day soon, all the work you've put in so far will pay off on iOS.",{"type":21,"tag":22,"props":15852,"children":15853},{},[15854,15856,15863,15865,15871],{"type":26,"value":15855},"In the meantime, even though we can have a full-fat PWA, we can get pretty close with iOS's ",{"type":21,"tag":322,"props":15857,"children":15860},{"href":15858,"rel":15859},"https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html",[326],[15861],{"type":26,"value":15862},"Web Clips",{"type":26,"value":15864}," functionality. That link will give you the details, but let's look at an example. All of the below will go into the ",{"type":21,"tag":246,"props":15866,"children":15868},{"className":15867},[],[15869],{"type":26,"value":15870},"\u003Chead \\>",{"type":26,"value":15872}," of your html document.",{"type":21,"tag":239,"props":15874,"children":15876},{"className":15750,"code":15875,"language":15752,"meta":8,"style":8},"\u003C!-- iOS specific headers for 'web app' behaviours and look-and-feel. -->\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" />\n\u003Cmeta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n\u003Cmeta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">\n\u003Cmeta name=\"apple-mobile-web-app-title\" content=\"Your app name\">\n\n\u003C!-- iOS Home Screen Icon -->\n\u003Clink rel=\"apple-touch-icon\" href=\"/static/img/yourAppIcon.png\">\n\n\u003C!-- Splash screens for iOS - these did not work in iOS 9/10, but are now working\n    in iOS 11+ - see https://forums.developer.apple.com/thread/23924. -->\n\n\u003C!-- iPhone 7 Plus & 6/s Plus portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen1242x2208.png' %}\"\n        media=\"(device-width: 414px) and (device-height: 736px)\n                and (-webkit-device-pixel-ratio: 3)\"\n        rel=\"apple-touch-startup-image\">\n\n\u003C!-- iPhone 7 & 6s portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen750x1334.png' %}\"\n        media=\"(device-width: 375px) and (device-height: 667px)\n                and (-webkit-device-pixel-ratio: 2)\"\n        rel=\"apple-touch-startup-image\">\n\n\u003C!-- iPhone 6 portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen750x1294.png' %}\"\n        media=\"(device-width: 375px) and (device-height: 667px)\n                and (-webkit-device-pixel-ratio: 2)\"\n        rel=\"apple-touch-startup-image\">\n\n\u003C!-- iPhone 5, SE portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen640x1136.png' %}\"\n        media=\"(device-width: 320px) and (device-height: 568px)\n                and (-webkit-device-pixel-ratio: 2)\"\n        rel=\"apple-touch-startup-image\">\n",[15877],{"type":21,"tag":246,"props":15878,"children":15879},{"__ignoreMap":8},[15880,15888,15933,15974,16015,16056,16063,16071,16112,16119,16127,16135,16142,16150,16174,16191,16199,16220,16227,16235,16259,16275,16283,16302,16309,16317,16341,16356,16363,16382,16389,16397,16421,16437,16444],{"type":21,"tag":455,"props":15881,"children":15882},{"class":457,"line":458},[15883],{"type":21,"tag":455,"props":15884,"children":15885},{"style":462},[15886],{"type":26,"value":15887},"\u003C!-- iOS specific headers for 'web app' behaviours and look-and-feel. -->\n",{"type":21,"tag":455,"props":15889,"children":15890},{"class":457,"line":336},[15891,15895,15900,15905,15909,15914,15919,15923,15928],{"type":21,"tag":455,"props":15892,"children":15893},{"style":486},[15894],{"type":26,"value":11627},{"type":21,"tag":455,"props":15896,"children":15897},{"style":15774},[15898],{"type":26,"value":15899},"meta",{"type":21,"tag":455,"props":15901,"children":15902},{"style":1365},[15903],{"type":26,"value":15904}," name",{"type":21,"tag":455,"props":15906,"children":15907},{"style":486},[15908],{"type":26,"value":3193},{"type":21,"tag":455,"props":15910,"children":15911},{"style":492},[15912],{"type":26,"value":15913},"\"viewport\"",{"type":21,"tag":455,"props":15915,"children":15916},{"style":1365},[15917],{"type":26,"value":15918}," content",{"type":21,"tag":455,"props":15920,"children":15921},{"style":486},[15922],{"type":26,"value":3193},{"type":21,"tag":455,"props":15924,"children":15925},{"style":492},[15926],{"type":26,"value":15927},"\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\"",{"type":21,"tag":455,"props":15929,"children":15930},{"style":486},[15931],{"type":26,"value":15932}," />\n",{"type":21,"tag":455,"props":15934,"children":15935},{"class":457,"line":333},[15936,15940,15944,15948,15952,15957,15961,15965,15970],{"type":21,"tag":455,"props":15937,"children":15938},{"style":486},[15939],{"type":26,"value":11627},{"type":21,"tag":455,"props":15941,"children":15942},{"style":15774},[15943],{"type":26,"value":15899},{"type":21,"tag":455,"props":15945,"children":15946},{"style":1365},[15947],{"type":26,"value":15904},{"type":21,"tag":455,"props":15949,"children":15950},{"style":486},[15951],{"type":26,"value":3193},{"type":21,"tag":455,"props":15953,"children":15954},{"style":492},[15955],{"type":26,"value":15956},"\"apple-mobile-web-app-capable\"",{"type":21,"tag":455,"props":15958,"children":15959},{"style":1365},[15960],{"type":26,"value":15918},{"type":21,"tag":455,"props":15962,"children":15963},{"style":486},[15964],{"type":26,"value":3193},{"type":21,"tag":455,"props":15966,"children":15967},{"style":492},[15968],{"type":26,"value":15969},"\"yes\"",{"type":21,"tag":455,"props":15971,"children":15972},{"style":486},[15973],{"type":26,"value":15810},{"type":21,"tag":455,"props":15975,"children":15976},{"class":457,"line":1424},[15977,15981,15985,15989,15993,15998,16002,16006,16011],{"type":21,"tag":455,"props":15978,"children":15979},{"style":486},[15980],{"type":26,"value":11627},{"type":21,"tag":455,"props":15982,"children":15983},{"style":15774},[15984],{"type":26,"value":15899},{"type":21,"tag":455,"props":15986,"children":15987},{"style":1365},[15988],{"type":26,"value":15904},{"type":21,"tag":455,"props":15990,"children":15991},{"style":486},[15992],{"type":26,"value":3193},{"type":21,"tag":455,"props":15994,"children":15995},{"style":492},[15996],{"type":26,"value":15997},"\"apple-mobile-web-app-status-bar-style\"",{"type":21,"tag":455,"props":15999,"children":16000},{"style":1365},[16001],{"type":26,"value":15918},{"type":21,"tag":455,"props":16003,"children":16004},{"style":486},[16005],{"type":26,"value":3193},{"type":21,"tag":455,"props":16007,"children":16008},{"style":492},[16009],{"type":26,"value":16010},"\"black-translucent\"",{"type":21,"tag":455,"props":16012,"children":16013},{"style":486},[16014],{"type":26,"value":15810},{"type":21,"tag":455,"props":16016,"children":16017},{"class":457,"line":1443},[16018,16022,16026,16030,16034,16039,16043,16047,16052],{"type":21,"tag":455,"props":16019,"children":16020},{"style":486},[16021],{"type":26,"value":11627},{"type":21,"tag":455,"props":16023,"children":16024},{"style":15774},[16025],{"type":26,"value":15899},{"type":21,"tag":455,"props":16027,"children":16028},{"style":1365},[16029],{"type":26,"value":15904},{"type":21,"tag":455,"props":16031,"children":16032},{"style":486},[16033],{"type":26,"value":3193},{"type":21,"tag":455,"props":16035,"children":16036},{"style":492},[16037],{"type":26,"value":16038},"\"apple-mobile-web-app-title\"",{"type":21,"tag":455,"props":16040,"children":16041},{"style":1365},[16042],{"type":26,"value":15918},{"type":21,"tag":455,"props":16044,"children":16045},{"style":486},[16046],{"type":26,"value":3193},{"type":21,"tag":455,"props":16048,"children":16049},{"style":492},[16050],{"type":26,"value":16051},"\"Your app name\"",{"type":21,"tag":455,"props":16053,"children":16054},{"style":486},[16055],{"type":26,"value":15810},{"type":21,"tag":455,"props":16057,"children":16058},{"class":457,"line":1489},[16059],{"type":21,"tag":455,"props":16060,"children":16061},{"emptyLinePlaceholder":471},[16062],{"type":26,"value":474},{"type":21,"tag":455,"props":16064,"children":16065},{"class":457,"line":1506},[16066],{"type":21,"tag":455,"props":16067,"children":16068},{"style":462},[16069],{"type":26,"value":16070},"\u003C!-- iOS Home Screen Icon -->\n",{"type":21,"tag":455,"props":16072,"children":16073},{"class":457,"line":1547},[16074,16078,16082,16086,16090,16095,16099,16103,16108],{"type":21,"tag":455,"props":16075,"children":16076},{"style":486},[16077],{"type":26,"value":11627},{"type":21,"tag":455,"props":16079,"children":16080},{"style":15774},[16081],{"type":26,"value":15777},{"type":21,"tag":455,"props":16083,"children":16084},{"style":1365},[16085],{"type":26,"value":15782},{"type":21,"tag":455,"props":16087,"children":16088},{"style":486},[16089],{"type":26,"value":3193},{"type":21,"tag":455,"props":16091,"children":16092},{"style":492},[16093],{"type":26,"value":16094},"\"apple-touch-icon\"",{"type":21,"tag":455,"props":16096,"children":16097},{"style":1365},[16098],{"type":26,"value":15796},{"type":21,"tag":455,"props":16100,"children":16101},{"style":486},[16102],{"type":26,"value":3193},{"type":21,"tag":455,"props":16104,"children":16105},{"style":492},[16106],{"type":26,"value":16107},"\"/static/img/yourAppIcon.png\"",{"type":21,"tag":455,"props":16109,"children":16110},{"style":486},[16111],{"type":26,"value":15810},{"type":21,"tag":455,"props":16113,"children":16114},{"class":457,"line":1564},[16115],{"type":21,"tag":455,"props":16116,"children":16117},{"emptyLinePlaceholder":471},[16118],{"type":26,"value":474},{"type":21,"tag":455,"props":16120,"children":16121},{"class":457,"line":1605},[16122],{"type":21,"tag":455,"props":16123,"children":16124},{"style":462},[16125],{"type":26,"value":16126},"\u003C!-- Splash screens for iOS - these did not work in iOS 9/10, but are now working\n",{"type":21,"tag":455,"props":16128,"children":16129},{"class":457,"line":1622},[16130],{"type":21,"tag":455,"props":16131,"children":16132},{"style":462},[16133],{"type":26,"value":16134},"    in iOS 11+ - see https://forums.developer.apple.com/thread/23924. -->\n",{"type":21,"tag":455,"props":16136,"children":16137},{"class":457,"line":1663},[16138],{"type":21,"tag":455,"props":16139,"children":16140},{"emptyLinePlaceholder":471},[16141],{"type":26,"value":474},{"type":21,"tag":455,"props":16143,"children":16144},{"class":457,"line":1680},[16145],{"type":21,"tag":455,"props":16146,"children":16147},{"style":462},[16148],{"type":26,"value":16149},"\u003C!-- iPhone 7 Plus & 6/s Plus portrait startup image -->\n",{"type":21,"tag":455,"props":16151,"children":16152},{"class":457,"line":1721},[16153,16157,16161,16165,16169],{"type":21,"tag":455,"props":16154,"children":16155},{"style":486},[16156],{"type":26,"value":11627},{"type":21,"tag":455,"props":16158,"children":16159},{"style":15774},[16160],{"type":26,"value":15777},{"type":21,"tag":455,"props":16162,"children":16163},{"style":1365},[16164],{"type":26,"value":15796},{"type":21,"tag":455,"props":16166,"children":16167},{"style":486},[16168],{"type":26,"value":3193},{"type":21,"tag":455,"props":16170,"children":16171},{"style":492},[16172],{"type":26,"value":16173},"\"{% static 'img/launchScreen1242x2208.png' %}\"\n",{"type":21,"tag":455,"props":16175,"children":16176},{"class":457,"line":1738},[16177,16182,16186],{"type":21,"tag":455,"props":16178,"children":16179},{"style":1365},[16180],{"type":26,"value":16181},"        media",{"type":21,"tag":455,"props":16183,"children":16184},{"style":486},[16185],{"type":26,"value":3193},{"type":21,"tag":455,"props":16187,"children":16188},{"style":492},[16189],{"type":26,"value":16190},"\"(device-width: 414px) and (device-height: 736px)\n",{"type":21,"tag":455,"props":16192,"children":16193},{"class":457,"line":1779},[16194],{"type":21,"tag":455,"props":16195,"children":16196},{"style":492},[16197],{"type":26,"value":16198},"                and (-webkit-device-pixel-ratio: 3)\"\n",{"type":21,"tag":455,"props":16200,"children":16201},{"class":457,"line":1796},[16202,16207,16211,16216],{"type":21,"tag":455,"props":16203,"children":16204},{"style":1365},[16205],{"type":26,"value":16206},"        rel",{"type":21,"tag":455,"props":16208,"children":16209},{"style":486},[16210],{"type":26,"value":3193},{"type":21,"tag":455,"props":16212,"children":16213},{"style":492},[16214],{"type":26,"value":16215},"\"apple-touch-startup-image\"",{"type":21,"tag":455,"props":16217,"children":16218},{"style":486},[16219],{"type":26,"value":15810},{"type":21,"tag":455,"props":16221,"children":16222},{"class":457,"line":1837},[16223],{"type":21,"tag":455,"props":16224,"children":16225},{"emptyLinePlaceholder":471},[16226],{"type":26,"value":474},{"type":21,"tag":455,"props":16228,"children":16229},{"class":457,"line":1854},[16230],{"type":21,"tag":455,"props":16231,"children":16232},{"style":462},[16233],{"type":26,"value":16234},"\u003C!-- iPhone 7 & 6s portrait startup image -->\n",{"type":21,"tag":455,"props":16236,"children":16237},{"class":457,"line":1895},[16238,16242,16246,16250,16254],{"type":21,"tag":455,"props":16239,"children":16240},{"style":486},[16241],{"type":26,"value":11627},{"type":21,"tag":455,"props":16243,"children":16244},{"style":15774},[16245],{"type":26,"value":15777},{"type":21,"tag":455,"props":16247,"children":16248},{"style":1365},[16249],{"type":26,"value":15796},{"type":21,"tag":455,"props":16251,"children":16252},{"style":486},[16253],{"type":26,"value":3193},{"type":21,"tag":455,"props":16255,"children":16256},{"style":492},[16257],{"type":26,"value":16258},"\"{% static 'img/launchScreen750x1334.png' %}\"\n",{"type":21,"tag":455,"props":16260,"children":16261},{"class":457,"line":1912},[16262,16266,16270],{"type":21,"tag":455,"props":16263,"children":16264},{"style":1365},[16265],{"type":26,"value":16181},{"type":21,"tag":455,"props":16267,"children":16268},{"style":486},[16269],{"type":26,"value":3193},{"type":21,"tag":455,"props":16271,"children":16272},{"style":492},[16273],{"type":26,"value":16274},"\"(device-width: 375px) and (device-height: 667px)\n",{"type":21,"tag":455,"props":16276,"children":16277},{"class":457,"line":1953},[16278],{"type":21,"tag":455,"props":16279,"children":16280},{"style":492},[16281],{"type":26,"value":16282},"                and (-webkit-device-pixel-ratio: 2)\"\n",{"type":21,"tag":455,"props":16284,"children":16285},{"class":457,"line":1970},[16286,16290,16294,16298],{"type":21,"tag":455,"props":16287,"children":16288},{"style":1365},[16289],{"type":26,"value":16206},{"type":21,"tag":455,"props":16291,"children":16292},{"style":486},[16293],{"type":26,"value":3193},{"type":21,"tag":455,"props":16295,"children":16296},{"style":492},[16297],{"type":26,"value":16215},{"type":21,"tag":455,"props":16299,"children":16300},{"style":486},[16301],{"type":26,"value":15810},{"type":21,"tag":455,"props":16303,"children":16304},{"class":457,"line":1978},[16305],{"type":21,"tag":455,"props":16306,"children":16307},{"emptyLinePlaceholder":471},[16308],{"type":26,"value":474},{"type":21,"tag":455,"props":16310,"children":16311},{"class":457,"line":1996},[16312],{"type":21,"tag":455,"props":16313,"children":16314},{"style":462},[16315],{"type":26,"value":16316},"\u003C!-- iPhone 6 portrait startup image -->\n",{"type":21,"tag":455,"props":16318,"children":16319},{"class":457,"line":4487},[16320,16324,16328,16332,16336],{"type":21,"tag":455,"props":16321,"children":16322},{"style":486},[16323],{"type":26,"value":11627},{"type":21,"tag":455,"props":16325,"children":16326},{"style":15774},[16327],{"type":26,"value":15777},{"type":21,"tag":455,"props":16329,"children":16330},{"style":1365},[16331],{"type":26,"value":15796},{"type":21,"tag":455,"props":16333,"children":16334},{"style":486},[16335],{"type":26,"value":3193},{"type":21,"tag":455,"props":16337,"children":16338},{"style":492},[16339],{"type":26,"value":16340},"\"{% static 'img/launchScreen750x1294.png' %}\"\n",{"type":21,"tag":455,"props":16342,"children":16343},{"class":457,"line":4495},[16344,16348,16352],{"type":21,"tag":455,"props":16345,"children":16346},{"style":1365},[16347],{"type":26,"value":16181},{"type":21,"tag":455,"props":16349,"children":16350},{"style":486},[16351],{"type":26,"value":3193},{"type":21,"tag":455,"props":16353,"children":16354},{"style":492},[16355],{"type":26,"value":16274},{"type":21,"tag":455,"props":16357,"children":16358},{"class":457,"line":4509},[16359],{"type":21,"tag":455,"props":16360,"children":16361},{"style":492},[16362],{"type":26,"value":16282},{"type":21,"tag":455,"props":16364,"children":16365},{"class":457,"line":4561},[16366,16370,16374,16378],{"type":21,"tag":455,"props":16367,"children":16368},{"style":1365},[16369],{"type":26,"value":16206},{"type":21,"tag":455,"props":16371,"children":16372},{"style":486},[16373],{"type":26,"value":3193},{"type":21,"tag":455,"props":16375,"children":16376},{"style":492},[16377],{"type":26,"value":16215},{"type":21,"tag":455,"props":16379,"children":16380},{"style":486},[16381],{"type":26,"value":15810},{"type":21,"tag":455,"props":16383,"children":16384},{"class":457,"line":4596},[16385],{"type":21,"tag":455,"props":16386,"children":16387},{"emptyLinePlaceholder":471},[16388],{"type":26,"value":474},{"type":21,"tag":455,"props":16390,"children":16391},{"class":457,"line":4627},[16392],{"type":21,"tag":455,"props":16393,"children":16394},{"style":462},[16395],{"type":26,"value":16396},"\u003C!-- iPhone 5, SE portrait startup image -->\n",{"type":21,"tag":455,"props":16398,"children":16399},{"class":457,"line":5942},[16400,16404,16408,16412,16416],{"type":21,"tag":455,"props":16401,"children":16402},{"style":486},[16403],{"type":26,"value":11627},{"type":21,"tag":455,"props":16405,"children":16406},{"style":15774},[16407],{"type":26,"value":15777},{"type":21,"tag":455,"props":16409,"children":16410},{"style":1365},[16411],{"type":26,"value":15796},{"type":21,"tag":455,"props":16413,"children":16414},{"style":486},[16415],{"type":26,"value":3193},{"type":21,"tag":455,"props":16417,"children":16418},{"style":492},[16419],{"type":26,"value":16420},"\"{% static 'img/launchScreen640x1136.png' %}\"\n",{"type":21,"tag":455,"props":16422,"children":16423},{"class":457,"line":5965},[16424,16428,16432],{"type":21,"tag":455,"props":16425,"children":16426},{"style":1365},[16427],{"type":26,"value":16181},{"type":21,"tag":455,"props":16429,"children":16430},{"style":486},[16431],{"type":26,"value":3193},{"type":21,"tag":455,"props":16433,"children":16434},{"style":492},[16435],{"type":26,"value":16436},"\"(device-width: 320px) and (device-height: 568px)\n",{"type":21,"tag":455,"props":16438,"children":16439},{"class":457,"line":5983},[16440],{"type":21,"tag":455,"props":16441,"children":16442},{"style":492},[16443],{"type":26,"value":16282},{"type":21,"tag":455,"props":16445,"children":16446},{"class":457,"line":6002},[16447,16451,16455,16459],{"type":21,"tag":455,"props":16448,"children":16449},{"style":1365},[16450],{"type":26,"value":16206},{"type":21,"tag":455,"props":16452,"children":16453},{"style":486},[16454],{"type":26,"value":3193},{"type":21,"tag":455,"props":16456,"children":16457},{"style":492},[16458],{"type":26,"value":16215},{"type":21,"tag":455,"props":16460,"children":16461},{"style":486},[16462],{"type":26,"value":15810},{"type":21,"tag":22,"props":16464,"children":16465},{},[16466,16468,16475],{"type":26,"value":16467},"Whew! Of course, a lot of that space is taken up by the various startup images - and if we wanted to display 'landscape' startup images as well, it would be twice as long. The launch screen image size you will want can be drawn from the ",{"type":21,"tag":322,"props":16469,"children":16472},{"href":16470,"rel":16471},"https://developer.apple.com/ios/human-interface-guidelines/icons-and-images/launch-screen/",[326],[16473],{"type":26,"value":16474},"Apple Human Interface Guidelines document",{"type":26,"value":16476}," - the specific media queries to use for each is magic culled from some trial and error - you will likely have to perform some testing of your own.",{"type":21,"tag":22,"props":16478,"children":16479},{},[16480,16482,16488,16490,16496,16498,16504],{"type":26,"value":16481},"Above those, you'll see the meta tags that make our web app 'PWA-like' on iOS - particularly ",{"type":21,"tag":246,"props":16483,"children":16485},{"className":16484},[],[16486],{"type":26,"value":16487},"apple-mobile-web-app-capable",{"type":26,"value":16489},". Include these, and your users can use the Web Clip capability in iOS Safari to 'Save to Home Screen' your site. You'll want your icon specified as well - it can be any square resolution (e.g. 180x180 pixels) - and you'll want to specify the application name with the ",{"type":21,"tag":246,"props":16491,"children":16493},{"className":16492},[],[16494],{"type":26,"value":16495},"apple-mobile-web-app-title",{"type":26,"value":16497}," tag - otherwise the contents of the ",{"type":21,"tag":246,"props":16499,"children":16501},{"className":16500},[],[16502],{"type":26,"value":16503},"title",{"type":26,"value":16505}," tag are used.",{"type":21,"tag":22,"props":16507,"children":16508},{},[16509,16511,16517],{"type":26,"value":16510},"Finally, take a look at the ",{"type":21,"tag":246,"props":16512,"children":16514},{"className":16513},[],[16515],{"type":26,"value":16516},"apple-mobile-web-app-status-bar-style",{"type":26,"value":16518}," tag - we're using it to declare that we want the status bar to overlap our application, rather than push it down. The other options - which you can find in the Web Clip specification link, above - will set it's colour to white or black, and push your content down below it. Unfortunately, there's no way to gain every last pixel yet.",{"type":21,"tag":56,"props":16520,"children":16522},{"id":16521},"cache-this",[16523],{"type":26,"value":16524},"Cache This",{"type":21,"tag":22,"props":16526,"children":16527},{},[16528],{"type":26,"value":16529},"There's a substantial item missing from the iOS support set that we have on Android - caching for offline support. No Service Workers, no offline support. Well, now what?",{"type":21,"tag":22,"props":16531,"children":16532},{},[16533,16535,16542],{"type":26,"value":16534},"Luckily, if this is the make-or-break item, we can make it - using the older ",{"type":21,"tag":322,"props":16536,"children":16539},{"href":16537,"rel":16538},"https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache",[326],[16540],{"type":26,"value":16541},"Application Cache API",{"type":26,"value":16543},". Using the application cache is a topic unto itself, so the only thing I'll say here is that you'll need to be careful to only serve the application manifest to iOS devices (and you'll want to check that you're only serving it to iOS devices without Service Worker support, for future-proofing purposes).",{"type":21,"tag":22,"props":16545,"children":16546},{},[16547,16549,16556,16558,16565],{"type":26,"value":16548},"Application Cache and Service Workers don't play nice together (yet - watch ",{"type":21,"tag":322,"props":16550,"children":16553},{"href":16551,"rel":16552},"https://crbug.com/410665",[326],[16554],{"type":26,"value":16555},"this Chromium bug",{"type":26,"value":16557}," for some details on progress for that project). One day, the ",{"type":21,"tag":322,"props":16559,"children":16562},{"href":16560,"rel":16561},"https://www.w3.org/TR/service-workers/#activation-algorithm",[326],[16563],{"type":26,"value":16564},"W3C suggested behaviour",{"type":26,"value":16566}," is that browsers will ignore and/or garbage-collect the application cache when a service worker is registered and takes over caching, but that day is not yet here, and you don't want two competing technologies caching things and producing unexpected results.",{"type":21,"tag":56,"props":16568,"children":16570},{"id":16569},"are-we-there-yet",[16571],{"type":26,"value":16572},"Are we there Yet?",{"type":21,"tag":22,"props":16574,"children":16575},{},[16576,16578,16583],{"type":26,"value":16577},"This time, we really are - together with ",{"type":21,"tag":322,"props":16579,"children":16580},{"href":8},[16581],{"type":26,"value":16582},"Part 1",{"type":26,"value":16584},", you'll now have a functioning web application that can also be installed to the home screen on Android like a native application, and almost-like-native on iOS; and you're ready for the brave future where iOS becomes fully PWA-capable; AND you have an application that can still be accessed anywhere, by anyone via the Internet.",{"type":21,"tag":22,"props":16586,"children":16587},{},[16588],{"type":26,"value":16589},"Not a bad day's work. Go get yourself a drink.",{"type":21,"tag":1279,"props":16591,"children":16592},{},[16593],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":16595},[16596,16597,16598,16599],{"id":15160,"depth":336,"text":15163},{"id":15823,"depth":336,"text":15826},{"id":16521,"depth":336,"text":16524},{"id":16569,"depth":336,"text":16572},"content:ckeefer:2017-3:MorePWAToYa-Part2.md","ckeefer/2017-3/MorePWAToYa-Part2.md","ckeefer/2017-3/MorePWAToYa-Part2",{"user":16604,"name":16605},"ckeefer","Christopher Keefer",{"_path":16607,"_dir":16608,"_draft":7,"_partial":7,"_locale":8,"title":16609,"description":16610,"excerpt":16610,"publishDate":16611,"tags":16612,"body":16613,"_type":343,"_id":19848,"_source":345,"_file":19849,"_stem":19850,"_extension":348,"author":19851},"/ckeefer/2017-2/morepwatoya-part1","2017-2","More PWA to Ya! (Progressive Web Apps, Part 1)","It's project kickoff time, and you're having a conversation with your client about what form the application will take:","2017-02-01",[15143,15142,15],{"type":18,"children":16614,"toc":19837},[16615,16619,16715,16729,16734,16746,16751,16756,16762,16776,16781,16789,16803,16808,16868,16873,16907,16913,16918,16946,17249,17254,17394,17415,17421,17426,17447,18293,18299,18304,19673,19678,19684,19689,19702,19708,19721,19735,19756,19762,19776,19781,19809,19814,19828,19833],{"type":21,"tag":22,"props":16616,"children":16617},{},[16618],{"type":26,"value":16610},{"type":21,"tag":16620,"props":16621,"children":16622},"blockquote",{},[16623,16633,16643,16652,16661,16670,16679,16688,16697,16706],{"type":21,"tag":22,"props":16624,"children":16625},{},[16626,16631],{"type":21,"tag":33,"props":16627,"children":16628},{},[16629],{"type":26,"value":16630},"Client",{"type":26,"value":16632},": I'm thinking mobile app. Our users will definitely be using this on the go.",{"type":21,"tag":22,"props":16634,"children":16635},{},[16636,16641],{"type":21,"tag":33,"props":16637,"children":16638},{},[16639],{"type":26,"value":16640},"Dev",{"type":26,"value":16642},": Sure, we can do a native mobile-",{"type":21,"tag":22,"props":16644,"children":16645},{},[16646,16650],{"type":21,"tag":33,"props":16647,"children":16648},{},[16649],{"type":26,"value":16630},{"type":26,"value":16651},": Mind you, we'll want a desktop version too. We'll need to use it from the office.",{"type":21,"tag":22,"props":16653,"children":16654},{},[16655,16659],{"type":21,"tag":33,"props":16656,"children":16657},{},[16658],{"type":26,"value":16640},{"type":26,"value":16660},": Okay, well, a responsive web app-",{"type":21,"tag":22,"props":16662,"children":16663},{},[16664,16668],{"type":21,"tag":33,"props":16665,"children":16666},{},[16667],{"type":26,"value":16630},{"type":26,"value":16669},": One of our priorities is definitely ease of access - we'll need the app accessible from the home screen, 'cause who has time for typing in URLs, amirite? We'll also want it to be useable offline, whenever people want to.",{"type":21,"tag":22,"props":16671,"children":16672},{},[16673,16677],{"type":21,"tag":33,"props":16674,"children":16675},{},[16676],{"type":26,"value":16640},{"type":26,"value":16678},": Ye-yeah, no problem, we can wrap your web app in a webview, bundle it up as a native app, and-",{"type":21,"tag":22,"props":16680,"children":16681},{},[16682,16686],{"type":21,"tag":33,"props":16683,"children":16684},{},[16685],{"type":26,"value":16630},{"type":26,"value":16687},": Yeah, cool. So they'll just be able to go to the site and install the app, right?",{"type":21,"tag":22,"props":16689,"children":16690},{},[16691,16695],{"type":21,"tag":33,"props":16692,"children":16693},{},[16694],{"type":26,"value":16640},{"type":26,"value":16696},": Well, no, they'll have to download it from the appropriate App Store.",{"type":21,"tag":22,"props":16698,"children":16699},{},[16700,16704],{"type":21,"tag":33,"props":16701,"children":16702},{},[16703],{"type":26,"value":16630},{"type":26,"value":16705},": Eh, that's a no-go - this is internal only, we can't have it showing up in the app stores. Didn't I make that clear from the start?",{"type":21,"tag":22,"props":16707,"children":16708},{},[16709,16713],{"type":21,"tag":33,"props":16710,"children":16711},{},[16712],{"type":26,"value":16640},{"type":26,"value":16714},": ...",{"type":21,"tag":22,"props":16716,"children":16717},{},[16718,16720,16727],{"type":26,"value":16719},"The term your client was looking for is ",{"type":21,"tag":322,"props":16721,"children":16724},{"href":16722,"rel":16723},"https://developers.google.com/web/progressive-web-apps/",[326],[16725],{"type":26,"value":16726},"Progressive Web App",{"type":26,"value":16728}," - an application that acts like a responsive web app when accessed from the browser on any device, but can be installed to mobile devices like a native application. The link above makes the case for PWAs, so we won't belabour the point - if you're still here, it's because you're convinced it's time to build a PWA.",{"type":21,"tag":22,"props":16730,"children":16731},{},[16732],{"type":26,"value":16733},"Instead, let's dig into the details. We're going to assume you have, or are building, a responsive or mobile-focused web application, and want to convert it to a PWA. Keep in mind that, like wrapping a webapp in a webview, all the heavy lifting is still done by you, the developer, in CSS, HTML and JS - there's no PWA magic to make it look 'native'.",{"type":21,"tag":22,"props":16735,"children":16736},{},[16737,16739,16744],{"type":26,"value":16738},"Well, actually, there is a ",{"type":21,"tag":33,"props":16740,"children":16741},{},[16742],{"type":26,"value":16743},"little",{"type":26,"value":16745}," magic. We'll get to that next time.",{"type":21,"tag":22,"props":16747,"children":16748},{},[16749],{"type":26,"value":16750},"Part 1 will focus on implementing a PWA the standards-compliant way. In Part 2, we'll address the 'little bit of magic' PWA's can have on Android to appear more native, and PWA's on iOS Safari, because it always has to be a special snowflake.",{"type":21,"tag":22,"props":16752,"children":16753},{},[16754],{"type":26,"value":16755},"Let's begin.",{"type":21,"tag":56,"props":16757,"children":16759},{"id":16758},"service-please",[16760],{"type":26,"value":16761},"Service, Please",{"type":21,"tag":22,"props":16763,"children":16764},{},[16765,16767,16774],{"type":26,"value":16766},"Familiar with ",{"type":21,"tag":322,"props":16768,"children":16771},{"href":16769,"rel":16770},"https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API",[326],[16772],{"type":26,"value":16773},"Service Workers",{"type":26,"value":16775},"? If not, get ready to do some reading on them - Service Workers are the clockwork that make PWAs tick.",{"type":21,"tag":22,"props":16777,"children":16778},{},[16779],{"type":26,"value":16780},"Mozilla's summary of Service Workers make it clear how this is so - and also, how complex a Service Worker can be:",{"type":21,"tag":16620,"props":16782,"children":16783},{},[16784],{"type":21,"tag":22,"props":16785,"children":16786},{},[16787],{"type":26,"value":16788},"Service workers essentially act as proxy servers that sit between web applications, and the browser and network (when available). They are intended to (amongst other things) enable the creation of effective offline experiences, intercepting network requests and taking appropriate action based on whether the network is available and updated assets reside on the server. They will also allow access to push notifications and background sync APIs.",{"type":21,"tag":22,"props":16790,"children":16791},{},[16792,16794,16801],{"type":26,"value":16793},"If you're familiar with ",{"type":21,"tag":322,"props":16795,"children":16798},{"href":16796,"rel":16797},"https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers",[326],[16799],{"type":26,"value":16800},"Web Workers",{"type":26,"value":16802},", you're about half-way there - Service Workers run on their own 'thread' (actual implementation details are up to the browser, of course), and have no access to the DOM, like a Web Worker. They have significantly more power than a web worker, however, particularly in terms of interacting with network requests; and, as a result, have more requirements to meet.",{"type":21,"tag":22,"props":16804,"children":16805},{},[16806],{"type":26,"value":16807},"Let's run down the checklist, and then we'll get into some implementation details. For a PWA's Service Worker you will need:",{"type":21,"tag":2953,"props":16809,"children":16810},{},[16811,16823,16842],{"type":21,"tag":187,"props":16812,"children":16813},{},[16814,16816,16821],{"type":26,"value":16815},"A secure context - Service Workers must be run from a TLS-secured domain (https), because they can be Men-in-the-middle on every request from the browser for a given domain. The ",{"type":21,"tag":33,"props":16817,"children":16818},{},[16819],{"type":26,"value":16820},"localhost",{"type":26,"value":16822}," special-case domain is the only exception to this rule, for the sake of development.",{"type":21,"tag":187,"props":16824,"children":16825},{},[16826,16828,16833,16835,16840],{"type":26,"value":16827},"A full list of the items you need to cache (for the caching and serving from cache functionality of a Service Worker, which is a requirement to have your application considered a PWA). Wildcards can't be used - you need to give the full (relative) path of the resource you want cached. If you're familiar with the ",{"type":21,"tag":322,"props":16829,"children":16831},{"href":16537,"rel":16830},[326],[16832],{"type":26,"value":16541},{"type":26,"value":16834}," this restriction will be familiar to you. For your application to be considered a PWA, at ",{"type":21,"tag":33,"props":16836,"children":16837},{},[16838],{"type":26,"value":16839},"least",{"type":26,"value":16841}," the start url must completely load when the user is offline.",{"type":21,"tag":187,"props":16843,"children":16844},{},[16845,16847,16853,16855,16859,16861,16866],{"type":26,"value":16846},"A means of serving the Service Worker from the root of the path that you want the Service Worker to have control over - so, if you want the Service Worker to be able to control and serve resources for your entire application, and your application is at ",{"type":21,"tag":322,"props":16848,"children":16851},{"href":16849,"rel":16850},"https://app.example.com/",[326],[16852],{"type":26,"value":16849},{"type":26,"value":16854},", you will need to be able to serve the Service Worker from ",{"type":21,"tag":33,"props":16856,"children":16857},{},[16858],{"type":26,"value":5754},{"type":26,"value":16860}," (as opposed to, e.g. ",{"type":21,"tag":33,"props":16862,"children":16863},{},[16864],{"type":26,"value":16865},"/static/js/workers/",{"type":26,"value":16867}," - if you serve from there, the only resources the Service Worker will be able to control will be those under that path).",{"type":21,"tag":22,"props":16869,"children":16870},{},[16871],{"type":26,"value":16872},"Additional checklist items for a PWA include:",{"type":21,"tag":2953,"props":16874,"children":16875},{},[16876,16881,16893],{"type":21,"tag":187,"props":16877,"children":16878},{},[16879],{"type":26,"value":16880},"A responsive (or mobile-focused) design.",{"type":21,"tag":187,"props":16882,"children":16883},{},[16884,16886,16891],{"type":26,"value":16885},"Quick initial load - Google, the company behind the original PWA spec (you may have heard of them), ",{"type":21,"tag":2310,"props":16887,"children":16888},{},[16889],{"type":26,"value":16890},"strongly",{"type":26,"value":16892}," suggests that your start url load under 10 seconds on a simulated 3G network - so no loading a half-dozen affiliate advertisements.",{"type":21,"tag":187,"props":16894,"children":16895},{},[16896,16898,16905],{"type":26,"value":16897},"You will want to consider making your application a ",{"type":21,"tag":322,"props":16899,"children":16902},{"href":16900,"rel":16901},"https://en.wikipedia.org/wiki/Single-page_application",[326],[16903],{"type":26,"value":16904},"SPA",{"type":26,"value":16906}," - it's generally a good fit for this use case, especially if it allows you to cache more up-front, or trim down the number of bits your application needs to transfer over the network.",{"type":21,"tag":56,"props":16908,"children":16910},{"id":16909},"looking-for-a-hard-worker-room-and-board-provided",[16911],{"type":26,"value":16912},"Looking for a Hard Worker, Room and Board Provided",{"type":21,"tag":22,"props":16914,"children":16915},{},[16916],{"type":26,"value":16917},"First things first, let's set up our web server to serve that Service Worker (lot of variations on 'serve' in that sentence) from the root of our domain, so we can control all resources and requests therein. We're going to assume you're using Python and Django in the example below, but the principle will always be the same.",{"type":21,"tag":22,"props":16919,"children":16920},{},[16921,16923,16929,16931,16936,16938,16944],{"type":26,"value":16922},"First, we'll create a service worker named ",{"type":21,"tag":246,"props":16924,"children":16926},{"className":16925},[],[16927],{"type":26,"value":16928},"CacheWorker.js",{"type":26,"value":16930}," in our ",{"type":21,"tag":246,"props":16932,"children":16934},{"className":16933},[],[16935],{"type":26,"value":11363},{"type":26,"value":16937}," directory, under a ",{"type":21,"tag":246,"props":16939,"children":16941},{"className":16940},[],[16942],{"type":26,"value":16943},"serviceWorkers",{"type":26,"value":16945}," directory. Then, we can create the view to serve this worker (and any others we might want to serve with potential control over all requests):",{"type":21,"tag":239,"props":16947,"children":16949},{"className":447,"code":16948,"language":449,"meta":8,"style":8},"from django.conf import settings\n\ndef serve_worker(request, worker_name):\n    \"\"\"\n    Serve the requested service worker from the appropriate location in the static files.\n    We need to serve the worker this way in order to allow it access to requests made against the\n    root - whatever /sub/dir the worker ends up getting served from is the only location it will\n    have visibility on, so serving from / is the only way to ensure the worker has visibility on all\n    requests. Only a-zA-Z-_ characters can appear in the service worker name.\n\n    :param request:\n    :param worker_name:\n    :return:\n    \"\"\"\n    worker_path = path.join(settings.STATIC_ROOT, 'serviceWorkers', \"{}.js\".format(worker_name))\n    try:\n        with open(worker_path, 'r') as worker_file:\n            return HttpResponse(worker_file, content_type='application/javascript')\n    except IOError:\n        return HttpResponseNotFound()\n",[16950],{"type":21,"tag":246,"props":16951,"children":16952},{"__ignoreMap":8},[16953,16975,16982,17000,17008,17016,17024,17032,17040,17048,17055,17063,17071,17079,17086,17140,17152,17189,17220,17237],{"type":21,"tag":455,"props":16954,"children":16955},{"class":457,"line":458},[16956,16960,16965,16970],{"type":21,"tag":455,"props":16957,"children":16958},{"style":1349},[16959],{"type":26,"value":1251},{"type":21,"tag":455,"props":16961,"children":16962},{"style":486},[16963],{"type":26,"value":16964}," django.conf ",{"type":21,"tag":455,"props":16966,"children":16967},{"style":1349},[16968],{"type":26,"value":16969},"import",{"type":21,"tag":455,"props":16971,"children":16972},{"style":486},[16973],{"type":26,"value":16974}," settings\n",{"type":21,"tag":455,"props":16976,"children":16977},{"class":457,"line":336},[16978],{"type":21,"tag":455,"props":16979,"children":16980},{"emptyLinePlaceholder":471},[16981],{"type":26,"value":474},{"type":21,"tag":455,"props":16983,"children":16984},{"class":457,"line":333},[16985,16990,16995],{"type":21,"tag":455,"props":16986,"children":16987},{"style":1349},[16988],{"type":26,"value":16989},"def",{"type":21,"tag":455,"props":16991,"children":16992},{"style":1365},[16993],{"type":26,"value":16994}," serve_worker",{"type":21,"tag":455,"props":16996,"children":16997},{"style":486},[16998],{"type":26,"value":16999},"(request, worker_name):\n",{"type":21,"tag":455,"props":17001,"children":17002},{"class":457,"line":1424},[17003],{"type":21,"tag":455,"props":17004,"children":17005},{"style":492},[17006],{"type":26,"value":17007},"    \"\"\"\n",{"type":21,"tag":455,"props":17009,"children":17010},{"class":457,"line":1443},[17011],{"type":21,"tag":455,"props":17012,"children":17013},{"style":492},[17014],{"type":26,"value":17015},"    Serve the requested service worker from the appropriate location in the static files.\n",{"type":21,"tag":455,"props":17017,"children":17018},{"class":457,"line":1489},[17019],{"type":21,"tag":455,"props":17020,"children":17021},{"style":492},[17022],{"type":26,"value":17023},"    We need to serve the worker this way in order to allow it access to requests made against the\n",{"type":21,"tag":455,"props":17025,"children":17026},{"class":457,"line":1506},[17027],{"type":21,"tag":455,"props":17028,"children":17029},{"style":492},[17030],{"type":26,"value":17031},"    root - whatever /sub/dir the worker ends up getting served from is the only location it will\n",{"type":21,"tag":455,"props":17033,"children":17034},{"class":457,"line":1547},[17035],{"type":21,"tag":455,"props":17036,"children":17037},{"style":492},[17038],{"type":26,"value":17039},"    have visibility on, so serving from / is the only way to ensure the worker has visibility on all\n",{"type":21,"tag":455,"props":17041,"children":17042},{"class":457,"line":1564},[17043],{"type":21,"tag":455,"props":17044,"children":17045},{"style":492},[17046],{"type":26,"value":17047},"    requests. Only a-zA-Z-_ characters can appear in the service worker name.\n",{"type":21,"tag":455,"props":17049,"children":17050},{"class":457,"line":1605},[17051],{"type":21,"tag":455,"props":17052,"children":17053},{"emptyLinePlaceholder":471},[17054],{"type":26,"value":474},{"type":21,"tag":455,"props":17056,"children":17057},{"class":457,"line":1622},[17058],{"type":21,"tag":455,"props":17059,"children":17060},{"style":492},[17061],{"type":26,"value":17062},"    :param request:\n",{"type":21,"tag":455,"props":17064,"children":17065},{"class":457,"line":1663},[17066],{"type":21,"tag":455,"props":17067,"children":17068},{"style":492},[17069],{"type":26,"value":17070},"    :param worker_name:\n",{"type":21,"tag":455,"props":17072,"children":17073},{"class":457,"line":1680},[17074],{"type":21,"tag":455,"props":17075,"children":17076},{"style":492},[17077],{"type":26,"value":17078},"    :return:\n",{"type":21,"tag":455,"props":17080,"children":17081},{"class":457,"line":1721},[17082],{"type":21,"tag":455,"props":17083,"children":17084},{"style":492},[17085],{"type":26,"value":17007},{"type":21,"tag":455,"props":17087,"children":17088},{"class":457,"line":1738},[17089,17094,17098,17103,17108,17112,17117,17121,17125,17130,17135],{"type":21,"tag":455,"props":17090,"children":17091},{"style":486},[17092],{"type":26,"value":17093},"    worker_path ",{"type":21,"tag":455,"props":17095,"children":17096},{"style":1349},[17097],{"type":26,"value":3193},{"type":21,"tag":455,"props":17099,"children":17100},{"style":486},[17101],{"type":26,"value":17102}," path.join(settings.",{"type":21,"tag":455,"props":17104,"children":17105},{"style":480},[17106],{"type":26,"value":17107},"STATIC_ROOT",{"type":21,"tag":455,"props":17109,"children":17110},{"style":486},[17111],{"type":26,"value":2228},{"type":21,"tag":455,"props":17113,"children":17114},{"style":492},[17115],{"type":26,"value":17116},"'serviceWorkers'",{"type":21,"tag":455,"props":17118,"children":17119},{"style":486},[17120],{"type":26,"value":2228},{"type":21,"tag":455,"props":17122,"children":17123},{"style":492},[17124],{"type":26,"value":1074},{"type":21,"tag":455,"props":17126,"children":17127},{"style":480},[17128],{"type":26,"value":17129},"{}",{"type":21,"tag":455,"props":17131,"children":17132},{"style":492},[17133],{"type":26,"value":17134},".js\"",{"type":21,"tag":455,"props":17136,"children":17137},{"style":486},[17138],{"type":26,"value":17139},".format(worker_name))\n",{"type":21,"tag":455,"props":17141,"children":17142},{"class":457,"line":1779},[17143,17147],{"type":21,"tag":455,"props":17144,"children":17145},{"style":1349},[17146],{"type":26,"value":5935},{"type":21,"tag":455,"props":17148,"children":17149},{"style":486},[17150],{"type":26,"value":17151},":\n",{"type":21,"tag":455,"props":17153,"children":17154},{"class":457,"line":1796},[17155,17160,17165,17170,17175,17179,17184],{"type":21,"tag":455,"props":17156,"children":17157},{"style":1349},[17158],{"type":26,"value":17159},"        with",{"type":21,"tag":455,"props":17161,"children":17162},{"style":480},[17163],{"type":26,"value":17164}," open",{"type":21,"tag":455,"props":17166,"children":17167},{"style":486},[17168],{"type":26,"value":17169},"(worker_path, ",{"type":21,"tag":455,"props":17171,"children":17172},{"style":492},[17173],{"type":26,"value":17174},"'r'",{"type":21,"tag":455,"props":17176,"children":17177},{"style":486},[17178],{"type":26,"value":4584},{"type":21,"tag":455,"props":17180,"children":17181},{"style":1349},[17182],{"type":26,"value":17183},"as",{"type":21,"tag":455,"props":17185,"children":17186},{"style":486},[17187],{"type":26,"value":17188}," worker_file:\n",{"type":21,"tag":455,"props":17190,"children":17191},{"class":457,"line":1837},[17192,17197,17202,17207,17211,17216],{"type":21,"tag":455,"props":17193,"children":17194},{"style":1349},[17195],{"type":26,"value":17196},"            return",{"type":21,"tag":455,"props":17198,"children":17199},{"style":486},[17200],{"type":26,"value":17201}," HttpResponse(worker_file, ",{"type":21,"tag":455,"props":17203,"children":17204},{"style":4527},[17205],{"type":26,"value":17206},"content_type",{"type":21,"tag":455,"props":17208,"children":17209},{"style":1349},[17210],{"type":26,"value":3193},{"type":21,"tag":455,"props":17212,"children":17213},{"style":492},[17214],{"type":26,"value":17215},"'application/javascript'",{"type":21,"tag":455,"props":17217,"children":17218},{"style":486},[17219],{"type":26,"value":500},{"type":21,"tag":455,"props":17221,"children":17222},{"class":457,"line":1854},[17223,17228,17233],{"type":21,"tag":455,"props":17224,"children":17225},{"style":1349},[17226],{"type":26,"value":17227},"    except",{"type":21,"tag":455,"props":17229,"children":17230},{"style":480},[17231],{"type":26,"value":17232}," IOError",{"type":21,"tag":455,"props":17234,"children":17235},{"style":486},[17236],{"type":26,"value":17151},{"type":21,"tag":455,"props":17238,"children":17239},{"class":457,"line":1895},[17240,17244],{"type":21,"tag":455,"props":17241,"children":17242},{"style":1349},[17243],{"type":26,"value":1430},{"type":21,"tag":455,"props":17245,"children":17246},{"style":486},[17247],{"type":26,"value":17248}," HttpResponseNotFound()\n",{"type":21,"tag":22,"props":17250,"children":17251},{},[17252],{"type":26,"value":17253},"Next, in urls.py, we'll add the route to this view:",{"type":21,"tag":239,"props":17255,"children":17257},{"className":447,"code":17256,"language":449,"meta":8,"style":8},"urlpatterns = [\n    # ...other patterns...\n    url(r'^worker-(?P\u003Cworker_name>[a-zA-Z\\-_]+).js$', views.serve_worker, name='serve_worker'),\n    # ...other patterns...\n]\n",[17258],{"type":21,"tag":246,"props":17259,"children":17260},{"__ignoreMap":8},[17261,17278,17286,17379,17386],{"type":21,"tag":455,"props":17262,"children":17263},{"class":457,"line":458},[17264,17269,17273],{"type":21,"tag":455,"props":17265,"children":17266},{"style":486},[17267],{"type":26,"value":17268},"urlpatterns ",{"type":21,"tag":455,"props":17270,"children":17271},{"style":1349},[17272],{"type":26,"value":3193},{"type":21,"tag":455,"props":17274,"children":17275},{"style":486},[17276],{"type":26,"value":17277}," [\n",{"type":21,"tag":455,"props":17279,"children":17280},{"class":457,"line":336},[17281],{"type":21,"tag":455,"props":17282,"children":17283},{"style":462},[17284],{"type":26,"value":17285},"    # ...other patterns...\n",{"type":21,"tag":455,"props":17287,"children":17288},{"class":457,"line":333},[17289,17294,17299,17303,17307,17312,17316,17321,17326,17332,17337,17341,17345,17349,17353,17357,17362,17366,17370,17375],{"type":21,"tag":455,"props":17290,"children":17291},{"style":486},[17292],{"type":26,"value":17293},"    url(",{"type":21,"tag":455,"props":17295,"children":17296},{"style":1349},[17297],{"type":26,"value":17298},"r",{"type":21,"tag":455,"props":17300,"children":17301},{"style":492},[17302],{"type":26,"value":11196},{"type":21,"tag":455,"props":17304,"children":17305},{"style":480},[17306],{"type":26,"value":5759},{"type":21,"tag":455,"props":17308,"children":17309},{"style":5762},[17310],{"type":26,"value":17311},"worker-",{"type":21,"tag":455,"props":17313,"children":17314},{"style":480},[17315],{"type":26,"value":489},{"type":21,"tag":455,"props":17317,"children":17318},{"style":15774},[17319],{"type":26,"value":17320},"?P\u003Cworker_name>",{"type":21,"tag":455,"props":17322,"children":17323},{"style":480},[17324],{"type":26,"value":17325},"[a-zA-Z",{"type":21,"tag":455,"props":17327,"children":17329},{"style":17328},"--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold",[17330],{"type":26,"value":17331},"\\-",{"type":21,"tag":455,"props":17333,"children":17334},{"style":480},[17335],{"type":26,"value":17336},"_]",{"type":21,"tag":455,"props":17338,"children":17339},{"style":1349},[17340],{"type":26,"value":2206},{"type":21,"tag":455,"props":17342,"children":17343},{"style":480},[17344],{"type":26,"value":5081},{"type":21,"tag":455,"props":17346,"children":17347},{"style":5762},[17348],{"type":26,"value":2804},{"type":21,"tag":455,"props":17350,"children":17351},{"style":480},[17352],{"type":26,"value":5770},{"type":21,"tag":455,"props":17354,"children":17355},{"style":492},[17356],{"type":26,"value":11196},{"type":21,"tag":455,"props":17358,"children":17359},{"style":486},[17360],{"type":26,"value":17361},", views.serve_worker, ",{"type":21,"tag":455,"props":17363,"children":17364},{"style":4527},[17365],{"type":26,"value":15624},{"type":21,"tag":455,"props":17367,"children":17368},{"style":1349},[17369],{"type":26,"value":3193},{"type":21,"tag":455,"props":17371,"children":17372},{"style":492},[17373],{"type":26,"value":17374},"'serve_worker'",{"type":21,"tag":455,"props":17376,"children":17377},{"style":486},[17378],{"type":26,"value":4696},{"type":21,"tag":455,"props":17380,"children":17381},{"class":457,"line":1424},[17382],{"type":21,"tag":455,"props":17383,"children":17384},{"style":462},[17385],{"type":26,"value":17285},{"type":21,"tag":455,"props":17387,"children":17388},{"class":457,"line":1443},[17389],{"type":21,"tag":455,"props":17390,"children":17391},{"style":486},[17392],{"type":26,"value":17393},"]\n",{"type":21,"tag":22,"props":17395,"children":17396},{},[17397,17399,17405,17407,17413],{"type":26,"value":17398},"Now, assuming our domain was ",{"type":21,"tag":246,"props":17400,"children":17402},{"className":17401},[],[17403],{"type":26,"value":17404},"https://app.example.com",{"type":26,"value":17406},", a request to ",{"type":21,"tag":246,"props":17408,"children":17410},{"className":17409},[],[17411],{"type":26,"value":17412},"https://app.example.com/worker-cacheWorker.js",{"type":26,"value":17414}," will return our worker script.",{"type":21,"tag":56,"props":17416,"children":17418},{"id":17417},"putting-your-workers-in-their-place",[17419],{"type":26,"value":17420},"Putting Your Workers in their Place",{"type":21,"tag":22,"props":17422,"children":17423},{},[17424],{"type":26,"value":17425},"Now that we're serving our worker script from the desired location, we need to tell the browser that it should be requesting said worker script, and installing it as a Service Worker.",{"type":21,"tag":22,"props":17427,"children":17428},{},[17429,17431,17438,17440,17445],{"type":26,"value":17430},"To this effect, we will want to use the ",{"type":21,"tag":322,"props":17432,"children":17435},{"href":17433,"rel":17434},"https://developer.mozilla.org/en-US/docs/Web/API/Navigator/serviceWorker",[326],[17436],{"type":26,"value":17437},"ServiceWorkerContainer",{"type":26,"value":17439}," API to register our service worker. Of course, since our PWA is a ",{"type":21,"tag":33,"props":17441,"children":17442},{},[17443],{"type":26,"value":17444},"progressive",{"type":26,"value":17446}," enhancement, we will check to ensure that the browser actually supports Service Workers before we try and install it - your application should have some fallback behaviour when it encounters a browser that doesn't.",{"type":21,"tag":239,"props":17448,"children":17450},{"className":3170,"code":17449,"language":2804,"meta":8,"style":8},"/**\n * Install service workers in those browsers which support them.\n */\n(function(window){\n    var serviceWorkers = {\n        \"IMMEDIATE\": [],\n        \"LOAD\": ['cacheWorker'],\n        \"DELAY\": []\n    };\n\n    /**\n     * Attempt to register the worker, and log either the success or failure to the console.\n     * @param {String} worker\n     */\n    function registerWorker(worker){\n        window.navigator.serviceWorker.register('/worker-'+worker+'.js').then(function(reg){\n            console.log('Registration successful for worker '+worker+', with scope: ' + reg.scope);\n        }, function(error){\n            console.log('Service Worker registration failed for worker: ', worker, error);\n        });\n    }\n\n    /**\n     * Handle messages sent to the main thread by Service Workers.\n     * @param event\n     */\n    function handleMessage(event){\n        console.log(\"TODO: Your app should do something with the event data sent by the worker.\", event.data.message, event.data.data);\n    }\n\n    // Check for ServiceWorker support.\n    if ('serviceWorker' in window.navigator){\n        // Listen for messages broadcasted by any service worker\n        window.navigator.serviceWorker.addEventListener('message', handleMessage);\n\n        /*\n        * For each service worker, consider their priority queue.\n        * Workers in the 'IMMEDIATE' queue are registered as soon as we can - this is useful if,\n        * for example, we need to immediately be able to intercept requests.\n        * Workers within queue 'LOAD' are registered after document load - this is the time to start caching\n        * resources, for example, without contending with the browser for bandwidth.\n        * Workers in queue 'DELAY' are registered after the application lets us know explicitly that now is a\n        * good time. How your application goes about doing this is up to you. This last category\n        * is good for workers that are going to be carrying out long-term activities, like\n        * long-polling a server.\n        */\n        serviceWorkers.IMMEDIATE.forEach(registerWorker);\n\n        window.addEventListener('load', function(){\n            serviceWorkers.LOAD.forEach(registerWorker);\n        });\n\n        window.addEventListener('yourCustomDelayEvent', function(){\n            serviceWorkers.DELAY.forEach(registerWorker);\n        });\n    }\n})(window);\n",[17451],{"type":21,"tag":246,"props":17452,"children":17453},{"__ignoreMap":8},[17454,17462,17470,17477,17501,17522,17535,17558,17571,17579,17586,17594,17602,17625,17632,17658,17725,17772,17797,17822,17830,17837,17844,17851,17859,17875,17882,17907,17933,17940,17947,17955,17980,17988,18014,18021,18029,18037,18045,18053,18061,18069,18077,18085,18093,18101,18109,18136,18143,18176,18201,18208,18215,18247,18271,18278,18285],{"type":21,"tag":455,"props":17455,"children":17456},{"class":457,"line":458},[17457],{"type":21,"tag":455,"props":17458,"children":17459},{"style":462},[17460],{"type":26,"value":17461},"/**\n",{"type":21,"tag":455,"props":17463,"children":17464},{"class":457,"line":336},[17465],{"type":21,"tag":455,"props":17466,"children":17467},{"style":462},[17468],{"type":26,"value":17469}," * Install service workers in those browsers which support them.\n",{"type":21,"tag":455,"props":17471,"children":17472},{"class":457,"line":333},[17473],{"type":21,"tag":455,"props":17474,"children":17475},{"style":462},[17476],{"type":26,"value":14425},{"type":21,"tag":455,"props":17478,"children":17479},{"class":457,"line":1424},[17480,17484,17488,17492,17497],{"type":21,"tag":455,"props":17481,"children":17482},{"style":486},[17483],{"type":26,"value":489},{"type":21,"tag":455,"props":17485,"children":17486},{"style":1349},[17487],{"type":26,"value":3897},{"type":21,"tag":455,"props":17489,"children":17490},{"style":486},[17491],{"type":26,"value":489},{"type":21,"tag":455,"props":17493,"children":17494},{"style":4527},[17495],{"type":26,"value":17496},"window",{"type":21,"tag":455,"props":17498,"children":17499},{"style":486},[17500],{"type":26,"value":7363},{"type":21,"tag":455,"props":17502,"children":17503},{"class":457,"line":1443},[17504,17509,17514,17518],{"type":21,"tag":455,"props":17505,"children":17506},{"style":1349},[17507],{"type":26,"value":17508},"    var",{"type":21,"tag":455,"props":17510,"children":17511},{"style":486},[17512],{"type":26,"value":17513}," serviceWorkers ",{"type":21,"tag":455,"props":17515,"children":17516},{"style":1349},[17517],{"type":26,"value":3193},{"type":21,"tag":455,"props":17519,"children":17520},{"style":486},[17521],{"type":26,"value":2470},{"type":21,"tag":455,"props":17523,"children":17524},{"class":457,"line":1489},[17525,17530],{"type":21,"tag":455,"props":17526,"children":17527},{"style":492},[17528],{"type":26,"value":17529},"        \"IMMEDIATE\"",{"type":21,"tag":455,"props":17531,"children":17532},{"style":486},[17533],{"type":26,"value":17534},": [],\n",{"type":21,"tag":455,"props":17536,"children":17537},{"class":457,"line":1506},[17538,17543,17548,17553],{"type":21,"tag":455,"props":17539,"children":17540},{"style":492},[17541],{"type":26,"value":17542},"        \"LOAD\"",{"type":21,"tag":455,"props":17544,"children":17545},{"style":486},[17546],{"type":26,"value":17547},": [",{"type":21,"tag":455,"props":17549,"children":17550},{"style":492},[17551],{"type":26,"value":17552},"'cacheWorker'",{"type":21,"tag":455,"props":17554,"children":17555},{"style":486},[17556],{"type":26,"value":17557},"],\n",{"type":21,"tag":455,"props":17559,"children":17560},{"class":457,"line":1547},[17561,17566],{"type":21,"tag":455,"props":17562,"children":17563},{"style":492},[17564],{"type":26,"value":17565},"        \"DELAY\"",{"type":21,"tag":455,"props":17567,"children":17568},{"style":486},[17569],{"type":26,"value":17570},": []\n",{"type":21,"tag":455,"props":17572,"children":17573},{"class":457,"line":1564},[17574],{"type":21,"tag":455,"props":17575,"children":17576},{"style":486},[17577],{"type":26,"value":17578},"    };\n",{"type":21,"tag":455,"props":17580,"children":17581},{"class":457,"line":1605},[17582],{"type":21,"tag":455,"props":17583,"children":17584},{"emptyLinePlaceholder":471},[17585],{"type":26,"value":474},{"type":21,"tag":455,"props":17587,"children":17588},{"class":457,"line":1622},[17589],{"type":21,"tag":455,"props":17590,"children":17591},{"style":462},[17592],{"type":26,"value":17593},"    /**\n",{"type":21,"tag":455,"props":17595,"children":17596},{"class":457,"line":1663},[17597],{"type":21,"tag":455,"props":17598,"children":17599},{"style":462},[17600],{"type":26,"value":17601},"     * Attempt to register the worker, and log either the success or failure to the console.\n",{"type":21,"tag":455,"props":17603,"children":17604},{"class":457,"line":1680},[17605,17610,17615,17620],{"type":21,"tag":455,"props":17606,"children":17607},{"style":462},[17608],{"type":26,"value":17609},"     * ",{"type":21,"tag":455,"props":17611,"children":17612},{"style":1349},[17613],{"type":26,"value":17614},"@param",{"type":21,"tag":455,"props":17616,"children":17617},{"style":1365},[17618],{"type":26,"value":17619}," {String}",{"type":21,"tag":455,"props":17621,"children":17622},{"style":486},[17623],{"type":26,"value":17624}," worker\n",{"type":21,"tag":455,"props":17626,"children":17627},{"class":457,"line":1721},[17628],{"type":21,"tag":455,"props":17629,"children":17630},{"style":462},[17631],{"type":26,"value":11610},{"type":21,"tag":455,"props":17633,"children":17634},{"class":457,"line":1738},[17635,17640,17645,17649,17654],{"type":21,"tag":455,"props":17636,"children":17637},{"style":1349},[17638],{"type":26,"value":17639},"    function",{"type":21,"tag":455,"props":17641,"children":17642},{"style":1365},[17643],{"type":26,"value":17644}," registerWorker",{"type":21,"tag":455,"props":17646,"children":17647},{"style":486},[17648],{"type":26,"value":489},{"type":21,"tag":455,"props":17650,"children":17651},{"style":4527},[17652],{"type":26,"value":17653},"worker",{"type":21,"tag":455,"props":17655,"children":17656},{"style":486},[17657],{"type":26,"value":7363},{"type":21,"tag":455,"props":17659,"children":17660},{"class":457,"line":1779},[17661,17666,17670,17674,17679,17683,17687,17691,17696,17700,17704,17708,17712,17716,17721],{"type":21,"tag":455,"props":17662,"children":17663},{"style":486},[17664],{"type":26,"value":17665},"        window.navigator.serviceWorker.",{"type":21,"tag":455,"props":17667,"children":17668},{"style":1365},[17669],{"type":26,"value":4406},{"type":21,"tag":455,"props":17671,"children":17672},{"style":486},[17673],{"type":26,"value":489},{"type":21,"tag":455,"props":17675,"children":17676},{"style":492},[17677],{"type":26,"value":17678},"'/worker-'",{"type":21,"tag":455,"props":17680,"children":17681},{"style":1349},[17682],{"type":26,"value":2206},{"type":21,"tag":455,"props":17684,"children":17685},{"style":486},[17686],{"type":26,"value":17653},{"type":21,"tag":455,"props":17688,"children":17689},{"style":1349},[17690],{"type":26,"value":2206},{"type":21,"tag":455,"props":17692,"children":17693},{"style":492},[17694],{"type":26,"value":17695},"'.js'",{"type":21,"tag":455,"props":17697,"children":17698},{"style":486},[17699],{"type":26,"value":5081},{"type":21,"tag":455,"props":17701,"children":17702},{"style":1365},[17703],{"type":26,"value":4520},{"type":21,"tag":455,"props":17705,"children":17706},{"style":486},[17707],{"type":26,"value":489},{"type":21,"tag":455,"props":17709,"children":17710},{"style":1349},[17711],{"type":26,"value":3897},{"type":21,"tag":455,"props":17713,"children":17714},{"style":486},[17715],{"type":26,"value":489},{"type":21,"tag":455,"props":17717,"children":17718},{"style":4527},[17719],{"type":26,"value":17720},"reg",{"type":21,"tag":455,"props":17722,"children":17723},{"style":486},[17724],{"type":26,"value":7363},{"type":21,"tag":455,"props":17726,"children":17727},{"class":457,"line":1796},[17728,17733,17737,17741,17746,17750,17754,17758,17763,17767],{"type":21,"tag":455,"props":17729,"children":17730},{"style":486},[17731],{"type":26,"value":17732},"            console.",{"type":21,"tag":455,"props":17734,"children":17735},{"style":1365},[17736],{"type":26,"value":3915},{"type":21,"tag":455,"props":17738,"children":17739},{"style":486},[17740],{"type":26,"value":489},{"type":21,"tag":455,"props":17742,"children":17743},{"style":492},[17744],{"type":26,"value":17745},"'Registration successful for worker '",{"type":21,"tag":455,"props":17747,"children":17748},{"style":1349},[17749],{"type":26,"value":2206},{"type":21,"tag":455,"props":17751,"children":17752},{"style":486},[17753],{"type":26,"value":17653},{"type":21,"tag":455,"props":17755,"children":17756},{"style":1349},[17757],{"type":26,"value":2206},{"type":21,"tag":455,"props":17759,"children":17760},{"style":492},[17761],{"type":26,"value":17762},"', with scope: '",{"type":21,"tag":455,"props":17764,"children":17765},{"style":1349},[17766],{"type":26,"value":3929},{"type":21,"tag":455,"props":17768,"children":17769},{"style":486},[17770],{"type":26,"value":17771}," reg.scope);\n",{"type":21,"tag":455,"props":17773,"children":17774},{"class":457,"line":1837},[17775,17780,17784,17788,17793],{"type":21,"tag":455,"props":17776,"children":17777},{"style":486},[17778],{"type":26,"value":17779},"        }, ",{"type":21,"tag":455,"props":17781,"children":17782},{"style":1349},[17783],{"type":26,"value":3897},{"type":21,"tag":455,"props":17785,"children":17786},{"style":486},[17787],{"type":26,"value":489},{"type":21,"tag":455,"props":17789,"children":17790},{"style":4527},[17791],{"type":26,"value":17792},"error",{"type":21,"tag":455,"props":17794,"children":17795},{"style":486},[17796],{"type":26,"value":7363},{"type":21,"tag":455,"props":17798,"children":17799},{"class":457,"line":1854},[17800,17804,17808,17812,17817],{"type":21,"tag":455,"props":17801,"children":17802},{"style":486},[17803],{"type":26,"value":17732},{"type":21,"tag":455,"props":17805,"children":17806},{"style":1365},[17807],{"type":26,"value":3915},{"type":21,"tag":455,"props":17809,"children":17810},{"style":486},[17811],{"type":26,"value":489},{"type":21,"tag":455,"props":17813,"children":17814},{"style":492},[17815],{"type":26,"value":17816},"'Service Worker registration failed for worker: '",{"type":21,"tag":455,"props":17818,"children":17819},{"style":486},[17820],{"type":26,"value":17821},", worker, error);\n",{"type":21,"tag":455,"props":17823,"children":17824},{"class":457,"line":1895},[17825],{"type":21,"tag":455,"props":17826,"children":17827},{"style":486},[17828],{"type":26,"value":17829},"        });\n",{"type":21,"tag":455,"props":17831,"children":17832},{"class":457,"line":1912},[17833],{"type":21,"tag":455,"props":17834,"children":17835},{"style":486},[17836],{"type":26,"value":6008},{"type":21,"tag":455,"props":17838,"children":17839},{"class":457,"line":1953},[17840],{"type":21,"tag":455,"props":17841,"children":17842},{"emptyLinePlaceholder":471},[17843],{"type":26,"value":474},{"type":21,"tag":455,"props":17845,"children":17846},{"class":457,"line":1970},[17847],{"type":21,"tag":455,"props":17848,"children":17849},{"style":462},[17850],{"type":26,"value":17593},{"type":21,"tag":455,"props":17852,"children":17853},{"class":457,"line":1978},[17854],{"type":21,"tag":455,"props":17855,"children":17856},{"style":462},[17857],{"type":26,"value":17858},"     * Handle messages sent to the main thread by Service Workers.\n",{"type":21,"tag":455,"props":17860,"children":17861},{"class":457,"line":1996},[17862,17866,17870],{"type":21,"tag":455,"props":17863,"children":17864},{"style":462},[17865],{"type":26,"value":17609},{"type":21,"tag":455,"props":17867,"children":17868},{"style":1349},[17869],{"type":26,"value":17614},{"type":21,"tag":455,"props":17871,"children":17872},{"style":486},[17873],{"type":26,"value":17874}," event\n",{"type":21,"tag":455,"props":17876,"children":17877},{"class":457,"line":4487},[17878],{"type":21,"tag":455,"props":17879,"children":17880},{"style":462},[17881],{"type":26,"value":11610},{"type":21,"tag":455,"props":17883,"children":17884},{"class":457,"line":4495},[17885,17889,17894,17898,17903],{"type":21,"tag":455,"props":17886,"children":17887},{"style":1349},[17888],{"type":26,"value":17639},{"type":21,"tag":455,"props":17890,"children":17891},{"style":1365},[17892],{"type":26,"value":17893}," handleMessage",{"type":21,"tag":455,"props":17895,"children":17896},{"style":486},[17897],{"type":26,"value":489},{"type":21,"tag":455,"props":17899,"children":17900},{"style":4527},[17901],{"type":26,"value":17902},"event",{"type":21,"tag":455,"props":17904,"children":17905},{"style":486},[17906],{"type":26,"value":7363},{"type":21,"tag":455,"props":17908,"children":17909},{"class":457,"line":4509},[17910,17915,17919,17923,17928],{"type":21,"tag":455,"props":17911,"children":17912},{"style":486},[17913],{"type":26,"value":17914},"        console.",{"type":21,"tag":455,"props":17916,"children":17917},{"style":1365},[17918],{"type":26,"value":3915},{"type":21,"tag":455,"props":17920,"children":17921},{"style":486},[17922],{"type":26,"value":489},{"type":21,"tag":455,"props":17924,"children":17925},{"style":492},[17926],{"type":26,"value":17927},"\"TODO: Your app should do something with the event data sent by the worker.\"",{"type":21,"tag":455,"props":17929,"children":17930},{"style":486},[17931],{"type":26,"value":17932},", event.data.message, event.data.data);\n",{"type":21,"tag":455,"props":17934,"children":17935},{"class":457,"line":4561},[17936],{"type":21,"tag":455,"props":17937,"children":17938},{"style":486},[17939],{"type":26,"value":6008},{"type":21,"tag":455,"props":17941,"children":17942},{"class":457,"line":4596},[17943],{"type":21,"tag":455,"props":17944,"children":17945},{"emptyLinePlaceholder":471},[17946],{"type":26,"value":474},{"type":21,"tag":455,"props":17948,"children":17949},{"class":457,"line":4627},[17950],{"type":21,"tag":455,"props":17951,"children":17952},{"style":462},[17953],{"type":26,"value":17954},"    // Check for ServiceWorker support.\n",{"type":21,"tag":455,"props":17956,"children":17957},{"class":457,"line":5942},[17958,17962,17966,17971,17975],{"type":21,"tag":455,"props":17959,"children":17960},{"style":1349},[17961],{"type":26,"value":1402},{"type":21,"tag":455,"props":17963,"children":17964},{"style":486},[17965],{"type":26,"value":2136},{"type":21,"tag":455,"props":17967,"children":17968},{"style":492},[17969],{"type":26,"value":17970},"'serviceWorker'",{"type":21,"tag":455,"props":17972,"children":17973},{"style":1349},[17974],{"type":26,"value":8476},{"type":21,"tag":455,"props":17976,"children":17977},{"style":486},[17978],{"type":26,"value":17979}," window.navigator){\n",{"type":21,"tag":455,"props":17981,"children":17982},{"class":457,"line":5965},[17983],{"type":21,"tag":455,"props":17984,"children":17985},{"style":462},[17986],{"type":26,"value":17987},"        // Listen for messages broadcasted by any service worker\n",{"type":21,"tag":455,"props":17989,"children":17990},{"class":457,"line":5983},[17991,17995,18000,18004,18009],{"type":21,"tag":455,"props":17992,"children":17993},{"style":486},[17994],{"type":26,"value":17665},{"type":21,"tag":455,"props":17996,"children":17997},{"style":1365},[17998],{"type":26,"value":17999},"addEventListener",{"type":21,"tag":455,"props":18001,"children":18002},{"style":486},[18003],{"type":26,"value":489},{"type":21,"tag":455,"props":18005,"children":18006},{"style":492},[18007],{"type":26,"value":18008},"'message'",{"type":21,"tag":455,"props":18010,"children":18011},{"style":486},[18012],{"type":26,"value":18013},", handleMessage);\n",{"type":21,"tag":455,"props":18015,"children":18016},{"class":457,"line":6002},[18017],{"type":21,"tag":455,"props":18018,"children":18019},{"emptyLinePlaceholder":471},[18020],{"type":26,"value":474},{"type":21,"tag":455,"props":18022,"children":18023},{"class":457,"line":6011},[18024],{"type":21,"tag":455,"props":18025,"children":18026},{"style":462},[18027],{"type":26,"value":18028},"        /*\n",{"type":21,"tag":455,"props":18030,"children":18031},{"class":457,"line":6019},[18032],{"type":21,"tag":455,"props":18033,"children":18034},{"style":462},[18035],{"type":26,"value":18036},"        * For each service worker, consider their priority queue.\n",{"type":21,"tag":455,"props":18038,"children":18039},{"class":457,"line":6028},[18040],{"type":21,"tag":455,"props":18041,"children":18042},{"style":462},[18043],{"type":26,"value":18044},"        * Workers in the 'IMMEDIATE' queue are registered as soon as we can - this is useful if,\n",{"type":21,"tag":455,"props":18046,"children":18047},{"class":457,"line":6085},[18048],{"type":21,"tag":455,"props":18049,"children":18050},{"style":462},[18051],{"type":26,"value":18052},"        * for example, we need to immediately be able to intercept requests.\n",{"type":21,"tag":455,"props":18054,"children":18055},{"class":457,"line":6097},[18056],{"type":21,"tag":455,"props":18057,"children":18058},{"style":462},[18059],{"type":26,"value":18060},"        * Workers within queue 'LOAD' are registered after document load - this is the time to start caching\n",{"type":21,"tag":455,"props":18062,"children":18063},{"class":457,"line":6117},[18064],{"type":21,"tag":455,"props":18065,"children":18066},{"style":462},[18067],{"type":26,"value":18068},"        * resources, for example, without contending with the browser for bandwidth.\n",{"type":21,"tag":455,"props":18070,"children":18071},{"class":457,"line":6134},[18072],{"type":21,"tag":455,"props":18073,"children":18074},{"style":462},[18075],{"type":26,"value":18076},"        * Workers in queue 'DELAY' are registered after the application lets us know explicitly that now is a\n",{"type":21,"tag":455,"props":18078,"children":18079},{"class":457,"line":6142},[18080],{"type":21,"tag":455,"props":18081,"children":18082},{"style":462},[18083],{"type":26,"value":18084},"        * good time. How your application goes about doing this is up to you. This last category\n",{"type":21,"tag":455,"props":18086,"children":18087},{"class":457,"line":10092},[18088],{"type":21,"tag":455,"props":18089,"children":18090},{"style":462},[18091],{"type":26,"value":18092},"        * is good for workers that are going to be carrying out long-term activities, like\n",{"type":21,"tag":455,"props":18094,"children":18095},{"class":457,"line":10100},[18096],{"type":21,"tag":455,"props":18097,"children":18098},{"style":462},[18099],{"type":26,"value":18100},"        * long-polling a server.\n",{"type":21,"tag":455,"props":18102,"children":18103},{"class":457,"line":10109},[18104],{"type":21,"tag":455,"props":18105,"children":18106},{"style":462},[18107],{"type":26,"value":18108},"        */\n",{"type":21,"tag":455,"props":18110,"children":18111},{"class":457,"line":10118},[18112,18117,18122,18126,18131],{"type":21,"tag":455,"props":18113,"children":18114},{"style":486},[18115],{"type":26,"value":18116},"        serviceWorkers.",{"type":21,"tag":455,"props":18118,"children":18119},{"style":480},[18120],{"type":26,"value":18121},"IMMEDIATE",{"type":21,"tag":455,"props":18123,"children":18124},{"style":486},[18125],{"type":26,"value":551},{"type":21,"tag":455,"props":18127,"children":18128},{"style":1365},[18129],{"type":26,"value":18130},"forEach",{"type":21,"tag":455,"props":18132,"children":18133},{"style":486},[18134],{"type":26,"value":18135},"(registerWorker);\n",{"type":21,"tag":455,"props":18137,"children":18138},{"class":457,"line":10136},[18139],{"type":21,"tag":455,"props":18140,"children":18141},{"emptyLinePlaceholder":471},[18142],{"type":26,"value":474},{"type":21,"tag":455,"props":18144,"children":18145},{"class":457,"line":10154},[18146,18151,18155,18159,18164,18168,18172],{"type":21,"tag":455,"props":18147,"children":18148},{"style":486},[18149],{"type":26,"value":18150},"        window.",{"type":21,"tag":455,"props":18152,"children":18153},{"style":1365},[18154],{"type":26,"value":17999},{"type":21,"tag":455,"props":18156,"children":18157},{"style":486},[18158],{"type":26,"value":489},{"type":21,"tag":455,"props":18160,"children":18161},{"style":492},[18162],{"type":26,"value":18163},"'load'",{"type":21,"tag":455,"props":18165,"children":18166},{"style":486},[18167],{"type":26,"value":2228},{"type":21,"tag":455,"props":18169,"children":18170},{"style":1349},[18171],{"type":26,"value":3897},{"type":21,"tag":455,"props":18173,"children":18174},{"style":486},[18175],{"type":26,"value":3902},{"type":21,"tag":455,"props":18177,"children":18178},{"class":457,"line":10172},[18179,18184,18189,18193,18197],{"type":21,"tag":455,"props":18180,"children":18181},{"style":486},[18182],{"type":26,"value":18183},"            serviceWorkers.",{"type":21,"tag":455,"props":18185,"children":18186},{"style":480},[18187],{"type":26,"value":18188},"LOAD",{"type":21,"tag":455,"props":18190,"children":18191},{"style":486},[18192],{"type":26,"value":551},{"type":21,"tag":455,"props":18194,"children":18195},{"style":1365},[18196],{"type":26,"value":18130},{"type":21,"tag":455,"props":18198,"children":18199},{"style":486},[18200],{"type":26,"value":18135},{"type":21,"tag":455,"props":18202,"children":18203},{"class":457,"line":10180},[18204],{"type":21,"tag":455,"props":18205,"children":18206},{"style":486},[18207],{"type":26,"value":17829},{"type":21,"tag":455,"props":18209,"children":18210},{"class":457,"line":10188},[18211],{"type":21,"tag":455,"props":18212,"children":18213},{"emptyLinePlaceholder":471},[18214],{"type":26,"value":474},{"type":21,"tag":455,"props":18216,"children":18217},{"class":457,"line":10197},[18218,18222,18226,18230,18235,18239,18243],{"type":21,"tag":455,"props":18219,"children":18220},{"style":486},[18221],{"type":26,"value":18150},{"type":21,"tag":455,"props":18223,"children":18224},{"style":1365},[18225],{"type":26,"value":17999},{"type":21,"tag":455,"props":18227,"children":18228},{"style":486},[18229],{"type":26,"value":489},{"type":21,"tag":455,"props":18231,"children":18232},{"style":492},[18233],{"type":26,"value":18234},"'yourCustomDelayEvent'",{"type":21,"tag":455,"props":18236,"children":18237},{"style":486},[18238],{"type":26,"value":2228},{"type":21,"tag":455,"props":18240,"children":18241},{"style":1349},[18242],{"type":26,"value":3897},{"type":21,"tag":455,"props":18244,"children":18245},{"style":486},[18246],{"type":26,"value":3902},{"type":21,"tag":455,"props":18248,"children":18249},{"class":457,"line":10205},[18250,18254,18259,18263,18267],{"type":21,"tag":455,"props":18251,"children":18252},{"style":486},[18253],{"type":26,"value":18183},{"type":21,"tag":455,"props":18255,"children":18256},{"style":480},[18257],{"type":26,"value":18258},"DELAY",{"type":21,"tag":455,"props":18260,"children":18261},{"style":486},[18262],{"type":26,"value":551},{"type":21,"tag":455,"props":18264,"children":18265},{"style":1365},[18266],{"type":26,"value":18130},{"type":21,"tag":455,"props":18268,"children":18269},{"style":486},[18270],{"type":26,"value":18135},{"type":21,"tag":455,"props":18272,"children":18273},{"class":457,"line":10214},[18274],{"type":21,"tag":455,"props":18275,"children":18276},{"style":486},[18277],{"type":26,"value":17829},{"type":21,"tag":455,"props":18279,"children":18280},{"class":457,"line":10223},[18281],{"type":21,"tag":455,"props":18282,"children":18283},{"style":486},[18284],{"type":26,"value":6008},{"type":21,"tag":455,"props":18286,"children":18287},{"class":457,"line":10260},[18288],{"type":21,"tag":455,"props":18289,"children":18290},{"style":486},[18291],{"type":26,"value":18292},"})(window);\n",{"type":21,"tag":56,"props":18294,"children":18296},{"id":18295},"cache-me-im-falling",[18297],{"type":26,"value":18298},"Cache Me, I'm Falling",{"type":21,"tag":22,"props":18300,"children":18301},{},[18302],{"type":26,"value":18303},"So, we have our server sending our cacheWorker file along properly, and we have the browser registering the service worker, and downloading and installing our script. That's great - except, our cacheWorker script is empty, so it doesn't do anything. Let's fix that.",{"type":21,"tag":239,"props":18305,"children":18307},{"className":3170,"code":18306,"language":2804,"meta":8,"style":8},"/**\n * Service worker intended for caching and serving files when the application is offline,\n * to meet the requirements for a PWA.\n * @author Christopher Keefer\n */\nvar cacheVersion = 1,\n    staticCache = 'static-cache-v'+cacheVersion,\n    cacheableResources = [\n        // Root - This MUST be in the cacheable resources for a PWA!\n        '/',\n        // Images\n        '/static/img/yourLogo.png',\n        //... any other static image resources your application will need ...\n        // CSS\n        '/static/css/yourapp.min.css',\n        // ... any other styling your app will need, order doesn't matter ....\n        // Fonts\n        '/static/css/fonts/roboto/roboto-regular.woff2',\n        // ... any other fonts ...\n        // JS\n        '/static/js/yourapp.min.js'\n        // ... any other JS - as with the other entries, the order you specify here doesn't matter,\n        // the files will be loaded in the order you indicate in your HTML document. ...\n    ];\n\n/**\n * On install of this worker, add all cacheableResources to the staticCache.\n * Note that workers will be (re-)installed when they have changed (byte-wise comparison)\n * from the last worker encountered with the registered url (see the installation of workers, above),\n * which can be as simple as changing the cacheVersion number to point to a new 'version' of the cache.\n * You will want to update that cacheVersion number each time you change any of the cached resources.\n * Doing so will cause the worker to re-request and re-cache the cacheableResources, which is how we\n * will refresh cached resources for the application.\n */\nself.addEventListener('install', function(event){\n    event.waitUntil(\n        caches.open(staticCache).then(function(cache){\n            return cache.addAll(cacheableResources);\n        }).then(function(){\n            // Take control of the client as soon as we're installed\n            // and the cache has been updated.\n            return self.skipWaiting();\n        })\n    );\n});\n\n/**\n * On activation of this service worker, delete old caches. Note that\n * we return a promise that resolves when all promises returned by\n * the delete calls within it resolve.\n * Once we've deleted the old cache, we need to let the clients know that\n * a new service worker (with a new cache) has taken over, and they'll\n * need to reload in order to get the newly cached resources, via\n * postMessage.\n * @param event\n */\nself.addEventListener('activate', function(event){\n    event.waitUntil(\n        caches.keys().then(function(cacheNames){\n            return Promise.all(\n                cacheNames.map(function(cacheName){\n                    if (cacheName !== staticCache){\n                        return caches.delete(cacheName);\n                    }\n                })\n            );\n        }).then(function(){\n            return self.clients.matchAll().then(function(clients){\n                return Promise.all(clients.map(function(client){\n                    return client.postMessage({message:'needs-reload'});\n                }));\n            });\n        })\n    );\n});\n\n/**\n * Intercept network requests so that we can serve the requested resource from the\n * cache, if we have it, or otherwise defer to the network.\n */\nself.addEventListener('fetch', function(event){\n    // Workaround for Chromium bug that makes ignoring the search\n    // parameter very slow when matching the request against the\n    // cached values: https://bugs.chromium.org/p/chromium/issues/detail?id=682677.\n    // Your application may not need this - or hey, it may even be fixed by the time\n    // you're reading this!\n    var hasSearch = (event.request.url.indexOf('?') !== -1);\n\n    event.respondWith(\n        caches.match(event.request, {\n            ignoreSearch: hasSearch\n        }).then(function(response){\n            return response || fetch(event.request);\n        })\n    );\n});\n",[18308],{"type":21,"tag":246,"props":18309,"children":18310},{"__ignoreMap":8},[18311,18318,18326,18334,18352,18359,18383,18409,18425,18433,18445,18453,18465,18473,18481,18493,18501,18509,18521,18529,18537,18545,18553,18561,18569,18576,18583,18591,18599,18607,18615,18623,18631,18639,18646,18687,18704,18747,18769,18793,18801,18809,18830,18838,18846,18853,18860,18867,18875,18883,18891,18899,18907,18915,18923,18938,18945,18985,19000,19042,19066,19100,19121,19144,19152,19160,19168,19191,19237,19286,19317,19325,19333,19340,19347,19354,19361,19369,19378,19387,19395,19436,19445,19454,19463,19472,19481,19537,19545,19562,19580,19589,19622,19649,19657,19665],{"type":21,"tag":455,"props":18312,"children":18313},{"class":457,"line":458},[18314],{"type":21,"tag":455,"props":18315,"children":18316},{"style":462},[18317],{"type":26,"value":17461},{"type":21,"tag":455,"props":18319,"children":18320},{"class":457,"line":336},[18321],{"type":21,"tag":455,"props":18322,"children":18323},{"style":462},[18324],{"type":26,"value":18325}," * Service worker intended for caching and serving files when the application is offline,\n",{"type":21,"tag":455,"props":18327,"children":18328},{"class":457,"line":333},[18329],{"type":21,"tag":455,"props":18330,"children":18331},{"style":462},[18332],{"type":26,"value":18333}," * to meet the requirements for a PWA.\n",{"type":21,"tag":455,"props":18335,"children":18336},{"class":457,"line":1424},[18337,18342,18347],{"type":21,"tag":455,"props":18338,"children":18339},{"style":462},[18340],{"type":26,"value":18341}," * ",{"type":21,"tag":455,"props":18343,"children":18344},{"style":1349},[18345],{"type":26,"value":18346},"@author",{"type":21,"tag":455,"props":18348,"children":18349},{"style":1365},[18350],{"type":26,"value":18351}," Christopher Keefer\n",{"type":21,"tag":455,"props":18353,"children":18354},{"class":457,"line":1443},[18355],{"type":21,"tag":455,"props":18356,"children":18357},{"style":462},[18358],{"type":26,"value":14425},{"type":21,"tag":455,"props":18360,"children":18361},{"class":457,"line":1489},[18362,18366,18371,18375,18379],{"type":21,"tag":455,"props":18363,"children":18364},{"style":1349},[18365],{"type":26,"value":3183},{"type":21,"tag":455,"props":18367,"children":18368},{"style":486},[18369],{"type":26,"value":18370}," cacheVersion ",{"type":21,"tag":455,"props":18372,"children":18373},{"style":1349},[18374],{"type":26,"value":3193},{"type":21,"tag":455,"props":18376,"children":18377},{"style":480},[18378],{"type":26,"value":2707},{"type":21,"tag":455,"props":18380,"children":18381},{"style":486},[18382],{"type":26,"value":2483},{"type":21,"tag":455,"props":18384,"children":18385},{"class":457,"line":1506},[18386,18391,18395,18400,18404],{"type":21,"tag":455,"props":18387,"children":18388},{"style":486},[18389],{"type":26,"value":18390},"    staticCache ",{"type":21,"tag":455,"props":18392,"children":18393},{"style":1349},[18394],{"type":26,"value":3193},{"type":21,"tag":455,"props":18396,"children":18397},{"style":492},[18398],{"type":26,"value":18399}," 'static-cache-v'",{"type":21,"tag":455,"props":18401,"children":18402},{"style":1349},[18403],{"type":26,"value":2206},{"type":21,"tag":455,"props":18405,"children":18406},{"style":486},[18407],{"type":26,"value":18408},"cacheVersion,\n",{"type":21,"tag":455,"props":18410,"children":18411},{"class":457,"line":1547},[18412,18417,18421],{"type":21,"tag":455,"props":18413,"children":18414},{"style":486},[18415],{"type":26,"value":18416},"    cacheableResources ",{"type":21,"tag":455,"props":18418,"children":18419},{"style":1349},[18420],{"type":26,"value":3193},{"type":21,"tag":455,"props":18422,"children":18423},{"style":486},[18424],{"type":26,"value":17277},{"type":21,"tag":455,"props":18426,"children":18427},{"class":457,"line":1564},[18428],{"type":21,"tag":455,"props":18429,"children":18430},{"style":462},[18431],{"type":26,"value":18432},"        // Root - This MUST be in the cacheable resources for a PWA!\n",{"type":21,"tag":455,"props":18434,"children":18435},{"class":457,"line":1605},[18436,18441],{"type":21,"tag":455,"props":18437,"children":18438},{"style":492},[18439],{"type":26,"value":18440},"        '/'",{"type":21,"tag":455,"props":18442,"children":18443},{"style":486},[18444],{"type":26,"value":2483},{"type":21,"tag":455,"props":18446,"children":18447},{"class":457,"line":1622},[18448],{"type":21,"tag":455,"props":18449,"children":18450},{"style":462},[18451],{"type":26,"value":18452},"        // Images\n",{"type":21,"tag":455,"props":18454,"children":18455},{"class":457,"line":1663},[18456,18461],{"type":21,"tag":455,"props":18457,"children":18458},{"style":492},[18459],{"type":26,"value":18460},"        '/static/img/yourLogo.png'",{"type":21,"tag":455,"props":18462,"children":18463},{"style":486},[18464],{"type":26,"value":2483},{"type":21,"tag":455,"props":18466,"children":18467},{"class":457,"line":1680},[18468],{"type":21,"tag":455,"props":18469,"children":18470},{"style":462},[18471],{"type":26,"value":18472},"        //... any other static image resources your application will need ...\n",{"type":21,"tag":455,"props":18474,"children":18475},{"class":457,"line":1721},[18476],{"type":21,"tag":455,"props":18477,"children":18478},{"style":462},[18479],{"type":26,"value":18480},"        // CSS\n",{"type":21,"tag":455,"props":18482,"children":18483},{"class":457,"line":1738},[18484,18489],{"type":21,"tag":455,"props":18485,"children":18486},{"style":492},[18487],{"type":26,"value":18488},"        '/static/css/yourapp.min.css'",{"type":21,"tag":455,"props":18490,"children":18491},{"style":486},[18492],{"type":26,"value":2483},{"type":21,"tag":455,"props":18494,"children":18495},{"class":457,"line":1779},[18496],{"type":21,"tag":455,"props":18497,"children":18498},{"style":462},[18499],{"type":26,"value":18500},"        // ... any other styling your app will need, order doesn't matter ....\n",{"type":21,"tag":455,"props":18502,"children":18503},{"class":457,"line":1796},[18504],{"type":21,"tag":455,"props":18505,"children":18506},{"style":462},[18507],{"type":26,"value":18508},"        // Fonts\n",{"type":21,"tag":455,"props":18510,"children":18511},{"class":457,"line":1837},[18512,18517],{"type":21,"tag":455,"props":18513,"children":18514},{"style":492},[18515],{"type":26,"value":18516},"        '/static/css/fonts/roboto/roboto-regular.woff2'",{"type":21,"tag":455,"props":18518,"children":18519},{"style":486},[18520],{"type":26,"value":2483},{"type":21,"tag":455,"props":18522,"children":18523},{"class":457,"line":1854},[18524],{"type":21,"tag":455,"props":18525,"children":18526},{"style":462},[18527],{"type":26,"value":18528},"        // ... any other fonts ...\n",{"type":21,"tag":455,"props":18530,"children":18531},{"class":457,"line":1895},[18532],{"type":21,"tag":455,"props":18533,"children":18534},{"style":462},[18535],{"type":26,"value":18536},"        // JS\n",{"type":21,"tag":455,"props":18538,"children":18539},{"class":457,"line":1912},[18540],{"type":21,"tag":455,"props":18541,"children":18542},{"style":492},[18543],{"type":26,"value":18544},"        '/static/js/yourapp.min.js'\n",{"type":21,"tag":455,"props":18546,"children":18547},{"class":457,"line":1953},[18548],{"type":21,"tag":455,"props":18549,"children":18550},{"style":462},[18551],{"type":26,"value":18552},"        // ... any other JS - as with the other entries, the order you specify here doesn't matter,\n",{"type":21,"tag":455,"props":18554,"children":18555},{"class":457,"line":1970},[18556],{"type":21,"tag":455,"props":18557,"children":18558},{"style":462},[18559],{"type":26,"value":18560},"        // the files will be loaded in the order you indicate in your HTML document. ...\n",{"type":21,"tag":455,"props":18562,"children":18563},{"class":457,"line":1978},[18564],{"type":21,"tag":455,"props":18565,"children":18566},{"style":486},[18567],{"type":26,"value":18568},"    ];\n",{"type":21,"tag":455,"props":18570,"children":18571},{"class":457,"line":1996},[18572],{"type":21,"tag":455,"props":18573,"children":18574},{"emptyLinePlaceholder":471},[18575],{"type":26,"value":474},{"type":21,"tag":455,"props":18577,"children":18578},{"class":457,"line":4487},[18579],{"type":21,"tag":455,"props":18580,"children":18581},{"style":462},[18582],{"type":26,"value":17461},{"type":21,"tag":455,"props":18584,"children":18585},{"class":457,"line":4495},[18586],{"type":21,"tag":455,"props":18587,"children":18588},{"style":462},[18589],{"type":26,"value":18590}," * On install of this worker, add all cacheableResources to the staticCache.\n",{"type":21,"tag":455,"props":18592,"children":18593},{"class":457,"line":4509},[18594],{"type":21,"tag":455,"props":18595,"children":18596},{"style":462},[18597],{"type":26,"value":18598}," * Note that workers will be (re-)installed when they have changed (byte-wise comparison)\n",{"type":21,"tag":455,"props":18600,"children":18601},{"class":457,"line":4561},[18602],{"type":21,"tag":455,"props":18603,"children":18604},{"style":462},[18605],{"type":26,"value":18606}," * from the last worker encountered with the registered url (see the installation of workers, above),\n",{"type":21,"tag":455,"props":18608,"children":18609},{"class":457,"line":4596},[18610],{"type":21,"tag":455,"props":18611,"children":18612},{"style":462},[18613],{"type":26,"value":18614}," * which can be as simple as changing the cacheVersion number to point to a new 'version' of the cache.\n",{"type":21,"tag":455,"props":18616,"children":18617},{"class":457,"line":4627},[18618],{"type":21,"tag":455,"props":18619,"children":18620},{"style":462},[18621],{"type":26,"value":18622}," * You will want to update that cacheVersion number each time you change any of the cached resources.\n",{"type":21,"tag":455,"props":18624,"children":18625},{"class":457,"line":5942},[18626],{"type":21,"tag":455,"props":18627,"children":18628},{"style":462},[18629],{"type":26,"value":18630}," * Doing so will cause the worker to re-request and re-cache the cacheableResources, which is how we\n",{"type":21,"tag":455,"props":18632,"children":18633},{"class":457,"line":5965},[18634],{"type":21,"tag":455,"props":18635,"children":18636},{"style":462},[18637],{"type":26,"value":18638}," * will refresh cached resources for the application.\n",{"type":21,"tag":455,"props":18640,"children":18641},{"class":457,"line":5983},[18642],{"type":21,"tag":455,"props":18643,"children":18644},{"style":462},[18645],{"type":26,"value":14425},{"type":21,"tag":455,"props":18647,"children":18648},{"class":457,"line":6002},[18649,18654,18658,18662,18667,18671,18675,18679,18683],{"type":21,"tag":455,"props":18650,"children":18651},{"style":486},[18652],{"type":26,"value":18653},"self.",{"type":21,"tag":455,"props":18655,"children":18656},{"style":1365},[18657],{"type":26,"value":17999},{"type":21,"tag":455,"props":18659,"children":18660},{"style":486},[18661],{"type":26,"value":489},{"type":21,"tag":455,"props":18663,"children":18664},{"style":492},[18665],{"type":26,"value":18666},"'install'",{"type":21,"tag":455,"props":18668,"children":18669},{"style":486},[18670],{"type":26,"value":2228},{"type":21,"tag":455,"props":18672,"children":18673},{"style":1349},[18674],{"type":26,"value":3897},{"type":21,"tag":455,"props":18676,"children":18677},{"style":486},[18678],{"type":26,"value":489},{"type":21,"tag":455,"props":18680,"children":18681},{"style":4527},[18682],{"type":26,"value":17902},{"type":21,"tag":455,"props":18684,"children":18685},{"style":486},[18686],{"type":26,"value":7363},{"type":21,"tag":455,"props":18688,"children":18689},{"class":457,"line":6011},[18690,18695,18700],{"type":21,"tag":455,"props":18691,"children":18692},{"style":486},[18693],{"type":26,"value":18694},"    event.",{"type":21,"tag":455,"props":18696,"children":18697},{"style":1365},[18698],{"type":26,"value":18699},"waitUntil",{"type":21,"tag":455,"props":18701,"children":18702},{"style":486},[18703],{"type":26,"value":11391},{"type":21,"tag":455,"props":18705,"children":18706},{"class":457,"line":6019},[18707,18712,18717,18722,18726,18730,18734,18738,18743],{"type":21,"tag":455,"props":18708,"children":18709},{"style":486},[18710],{"type":26,"value":18711},"        caches.",{"type":21,"tag":455,"props":18713,"children":18714},{"style":1365},[18715],{"type":26,"value":18716},"open",{"type":21,"tag":455,"props":18718,"children":18719},{"style":486},[18720],{"type":26,"value":18721},"(staticCache).",{"type":21,"tag":455,"props":18723,"children":18724},{"style":1365},[18725],{"type":26,"value":4520},{"type":21,"tag":455,"props":18727,"children":18728},{"style":486},[18729],{"type":26,"value":489},{"type":21,"tag":455,"props":18731,"children":18732},{"style":1349},[18733],{"type":26,"value":3897},{"type":21,"tag":455,"props":18735,"children":18736},{"style":486},[18737],{"type":26,"value":489},{"type":21,"tag":455,"props":18739,"children":18740},{"style":4527},[18741],{"type":26,"value":18742},"cache",{"type":21,"tag":455,"props":18744,"children":18745},{"style":486},[18746],{"type":26,"value":7363},{"type":21,"tag":455,"props":18748,"children":18749},{"class":457,"line":6028},[18750,18754,18759,18764],{"type":21,"tag":455,"props":18751,"children":18752},{"style":1349},[18753],{"type":26,"value":17196},{"type":21,"tag":455,"props":18755,"children":18756},{"style":486},[18757],{"type":26,"value":18758}," cache.",{"type":21,"tag":455,"props":18760,"children":18761},{"style":1365},[18762],{"type":26,"value":18763},"addAll",{"type":21,"tag":455,"props":18765,"children":18766},{"style":486},[18767],{"type":26,"value":18768},"(cacheableResources);\n",{"type":21,"tag":455,"props":18770,"children":18771},{"class":457,"line":6085},[18772,18777,18781,18785,18789],{"type":21,"tag":455,"props":18773,"children":18774},{"style":486},[18775],{"type":26,"value":18776},"        }).",{"type":21,"tag":455,"props":18778,"children":18779},{"style":1365},[18780],{"type":26,"value":4520},{"type":21,"tag":455,"props":18782,"children":18783},{"style":486},[18784],{"type":26,"value":489},{"type":21,"tag":455,"props":18786,"children":18787},{"style":1349},[18788],{"type":26,"value":3897},{"type":21,"tag":455,"props":18790,"children":18791},{"style":486},[18792],{"type":26,"value":3902},{"type":21,"tag":455,"props":18794,"children":18795},{"class":457,"line":6097},[18796],{"type":21,"tag":455,"props":18797,"children":18798},{"style":462},[18799],{"type":26,"value":18800},"            // Take control of the client as soon as we're installed\n",{"type":21,"tag":455,"props":18802,"children":18803},{"class":457,"line":6117},[18804],{"type":21,"tag":455,"props":18805,"children":18806},{"style":462},[18807],{"type":26,"value":18808},"            // and the cache has been updated.\n",{"type":21,"tag":455,"props":18810,"children":18811},{"class":457,"line":6134},[18812,18816,18821,18826],{"type":21,"tag":455,"props":18813,"children":18814},{"style":1349},[18815],{"type":26,"value":17196},{"type":21,"tag":455,"props":18817,"children":18818},{"style":486},[18819],{"type":26,"value":18820}," self.",{"type":21,"tag":455,"props":18822,"children":18823},{"style":1365},[18824],{"type":26,"value":18825},"skipWaiting",{"type":21,"tag":455,"props":18827,"children":18828},{"style":486},[18829],{"type":26,"value":3398},{"type":21,"tag":455,"props":18831,"children":18832},{"class":457,"line":6142},[18833],{"type":21,"tag":455,"props":18834,"children":18835},{"style":486},[18836],{"type":26,"value":18837},"        })\n",{"type":21,"tag":455,"props":18839,"children":18840},{"class":457,"line":10092},[18841],{"type":21,"tag":455,"props":18842,"children":18843},{"style":486},[18844],{"type":26,"value":18845},"    );\n",{"type":21,"tag":455,"props":18847,"children":18848},{"class":457,"line":10100},[18849],{"type":21,"tag":455,"props":18850,"children":18851},{"style":486},[18852],{"type":26,"value":3952},{"type":21,"tag":455,"props":18854,"children":18855},{"class":457,"line":10109},[18856],{"type":21,"tag":455,"props":18857,"children":18858},{"emptyLinePlaceholder":471},[18859],{"type":26,"value":474},{"type":21,"tag":455,"props":18861,"children":18862},{"class":457,"line":10118},[18863],{"type":21,"tag":455,"props":18864,"children":18865},{"style":462},[18866],{"type":26,"value":17461},{"type":21,"tag":455,"props":18868,"children":18869},{"class":457,"line":10136},[18870],{"type":21,"tag":455,"props":18871,"children":18872},{"style":462},[18873],{"type":26,"value":18874}," * On activation of this service worker, delete old caches. Note that\n",{"type":21,"tag":455,"props":18876,"children":18877},{"class":457,"line":10154},[18878],{"type":21,"tag":455,"props":18879,"children":18880},{"style":462},[18881],{"type":26,"value":18882}," * we return a promise that resolves when all promises returned by\n",{"type":21,"tag":455,"props":18884,"children":18885},{"class":457,"line":10172},[18886],{"type":21,"tag":455,"props":18887,"children":18888},{"style":462},[18889],{"type":26,"value":18890}," * the delete calls within it resolve.\n",{"type":21,"tag":455,"props":18892,"children":18893},{"class":457,"line":10180},[18894],{"type":21,"tag":455,"props":18895,"children":18896},{"style":462},[18897],{"type":26,"value":18898}," * Once we've deleted the old cache, we need to let the clients know that\n",{"type":21,"tag":455,"props":18900,"children":18901},{"class":457,"line":10188},[18902],{"type":21,"tag":455,"props":18903,"children":18904},{"style":462},[18905],{"type":26,"value":18906}," * a new service worker (with a new cache) has taken over, and they'll\n",{"type":21,"tag":455,"props":18908,"children":18909},{"class":457,"line":10197},[18910],{"type":21,"tag":455,"props":18911,"children":18912},{"style":462},[18913],{"type":26,"value":18914}," * need to reload in order to get the newly cached resources, via\n",{"type":21,"tag":455,"props":18916,"children":18917},{"class":457,"line":10205},[18918],{"type":21,"tag":455,"props":18919,"children":18920},{"style":462},[18921],{"type":26,"value":18922}," * postMessage.\n",{"type":21,"tag":455,"props":18924,"children":18925},{"class":457,"line":10214},[18926,18930,18934],{"type":21,"tag":455,"props":18927,"children":18928},{"style":462},[18929],{"type":26,"value":18341},{"type":21,"tag":455,"props":18931,"children":18932},{"style":1349},[18933],{"type":26,"value":17614},{"type":21,"tag":455,"props":18935,"children":18936},{"style":486},[18937],{"type":26,"value":17874},{"type":21,"tag":455,"props":18939,"children":18940},{"class":457,"line":10223},[18941],{"type":21,"tag":455,"props":18942,"children":18943},{"style":462},[18944],{"type":26,"value":14425},{"type":21,"tag":455,"props":18946,"children":18947},{"class":457,"line":10260},[18948,18952,18956,18960,18965,18969,18973,18977,18981],{"type":21,"tag":455,"props":18949,"children":18950},{"style":486},[18951],{"type":26,"value":18653},{"type":21,"tag":455,"props":18953,"children":18954},{"style":1365},[18955],{"type":26,"value":17999},{"type":21,"tag":455,"props":18957,"children":18958},{"style":486},[18959],{"type":26,"value":489},{"type":21,"tag":455,"props":18961,"children":18962},{"style":492},[18963],{"type":26,"value":18964},"'activate'",{"type":21,"tag":455,"props":18966,"children":18967},{"style":486},[18968],{"type":26,"value":2228},{"type":21,"tag":455,"props":18970,"children":18971},{"style":1349},[18972],{"type":26,"value":3897},{"type":21,"tag":455,"props":18974,"children":18975},{"style":486},[18976],{"type":26,"value":489},{"type":21,"tag":455,"props":18978,"children":18979},{"style":4527},[18980],{"type":26,"value":17902},{"type":21,"tag":455,"props":18982,"children":18983},{"style":486},[18984],{"type":26,"value":7363},{"type":21,"tag":455,"props":18986,"children":18987},{"class":457,"line":10290},[18988,18992,18996],{"type":21,"tag":455,"props":18989,"children":18990},{"style":486},[18991],{"type":26,"value":18694},{"type":21,"tag":455,"props":18993,"children":18994},{"style":1365},[18995],{"type":26,"value":18699},{"type":21,"tag":455,"props":18997,"children":18998},{"style":486},[18999],{"type":26,"value":11391},{"type":21,"tag":455,"props":19001,"children":19002},{"class":457,"line":10326},[19003,19007,19012,19017,19021,19025,19029,19033,19038],{"type":21,"tag":455,"props":19004,"children":19005},{"style":486},[19006],{"type":26,"value":18711},{"type":21,"tag":455,"props":19008,"children":19009},{"style":1365},[19010],{"type":26,"value":19011},"keys",{"type":21,"tag":455,"props":19013,"children":19014},{"style":486},[19015],{"type":26,"value":19016},"().",{"type":21,"tag":455,"props":19018,"children":19019},{"style":1365},[19020],{"type":26,"value":4520},{"type":21,"tag":455,"props":19022,"children":19023},{"style":486},[19024],{"type":26,"value":489},{"type":21,"tag":455,"props":19026,"children":19027},{"style":1349},[19028],{"type":26,"value":3897},{"type":21,"tag":455,"props":19030,"children":19031},{"style":486},[19032],{"type":26,"value":489},{"type":21,"tag":455,"props":19034,"children":19035},{"style":4527},[19036],{"type":26,"value":19037},"cacheNames",{"type":21,"tag":455,"props":19039,"children":19040},{"style":486},[19041],{"type":26,"value":7363},{"type":21,"tag":455,"props":19043,"children":19044},{"class":457,"line":10361},[19045,19049,19054,19058,19062],{"type":21,"tag":455,"props":19046,"children":19047},{"style":1349},[19048],{"type":26,"value":17196},{"type":21,"tag":455,"props":19050,"children":19051},{"style":480},[19052],{"type":26,"value":19053}," Promise",{"type":21,"tag":455,"props":19055,"children":19056},{"style":486},[19057],{"type":26,"value":551},{"type":21,"tag":455,"props":19059,"children":19060},{"style":1365},[19061],{"type":26,"value":7549},{"type":21,"tag":455,"props":19063,"children":19064},{"style":486},[19065],{"type":26,"value":11391},{"type":21,"tag":455,"props":19067,"children":19068},{"class":457,"line":10395},[19069,19074,19079,19083,19087,19091,19096],{"type":21,"tag":455,"props":19070,"children":19071},{"style":486},[19072],{"type":26,"value":19073},"                cacheNames.",{"type":21,"tag":455,"props":19075,"children":19076},{"style":1365},[19077],{"type":26,"value":19078},"map",{"type":21,"tag":455,"props":19080,"children":19081},{"style":486},[19082],{"type":26,"value":489},{"type":21,"tag":455,"props":19084,"children":19085},{"style":1349},[19086],{"type":26,"value":3897},{"type":21,"tag":455,"props":19088,"children":19089},{"style":486},[19090],{"type":26,"value":489},{"type":21,"tag":455,"props":19092,"children":19093},{"style":4527},[19094],{"type":26,"value":19095},"cacheName",{"type":21,"tag":455,"props":19097,"children":19098},{"style":486},[19099],{"type":26,"value":7363},{"type":21,"tag":455,"props":19101,"children":19102},{"class":457,"line":10429},[19103,19107,19112,19116],{"type":21,"tag":455,"props":19104,"children":19105},{"style":1349},[19106],{"type":26,"value":12395},{"type":21,"tag":455,"props":19108,"children":19109},{"style":486},[19110],{"type":26,"value":19111}," (cacheName ",{"type":21,"tag":455,"props":19113,"children":19114},{"style":1349},[19115],{"type":26,"value":7353},{"type":21,"tag":455,"props":19117,"children":19118},{"style":486},[19119],{"type":26,"value":19120}," staticCache){\n",{"type":21,"tag":455,"props":19122,"children":19123},{"class":457,"line":10437},[19124,19129,19134,19139],{"type":21,"tag":455,"props":19125,"children":19126},{"style":1349},[19127],{"type":26,"value":19128},"                        return",{"type":21,"tag":455,"props":19130,"children":19131},{"style":486},[19132],{"type":26,"value":19133}," caches.",{"type":21,"tag":455,"props":19135,"children":19136},{"style":1365},[19137],{"type":26,"value":19138},"delete",{"type":21,"tag":455,"props":19140,"children":19141},{"style":486},[19142],{"type":26,"value":19143},"(cacheName);\n",{"type":21,"tag":455,"props":19145,"children":19146},{"class":457,"line":12797},[19147],{"type":21,"tag":455,"props":19148,"children":19149},{"style":486},[19150],{"type":26,"value":19151},"                    }\n",{"type":21,"tag":455,"props":19153,"children":19154},{"class":457,"line":12839},[19155],{"type":21,"tag":455,"props":19156,"children":19157},{"style":486},[19158],{"type":26,"value":19159},"                })\n",{"type":21,"tag":455,"props":19161,"children":19162},{"class":457,"line":12847},[19163],{"type":21,"tag":455,"props":19164,"children":19165},{"style":486},[19166],{"type":26,"value":19167},"            );\n",{"type":21,"tag":455,"props":19169,"children":19170},{"class":457,"line":12863},[19171,19175,19179,19183,19187],{"type":21,"tag":455,"props":19172,"children":19173},{"style":486},[19174],{"type":26,"value":18776},{"type":21,"tag":455,"props":19176,"children":19177},{"style":1365},[19178],{"type":26,"value":4520},{"type":21,"tag":455,"props":19180,"children":19181},{"style":486},[19182],{"type":26,"value":489},{"type":21,"tag":455,"props":19184,"children":19185},{"style":1349},[19186],{"type":26,"value":3897},{"type":21,"tag":455,"props":19188,"children":19189},{"style":486},[19190],{"type":26,"value":3902},{"type":21,"tag":455,"props":19192,"children":19193},{"class":457,"line":12883},[19194,19198,19203,19208,19212,19216,19220,19224,19228,19233],{"type":21,"tag":455,"props":19195,"children":19196},{"style":1349},[19197],{"type":26,"value":17196},{"type":21,"tag":455,"props":19199,"children":19200},{"style":486},[19201],{"type":26,"value":19202}," self.clients.",{"type":21,"tag":455,"props":19204,"children":19205},{"style":1365},[19206],{"type":26,"value":19207},"matchAll",{"type":21,"tag":455,"props":19209,"children":19210},{"style":486},[19211],{"type":26,"value":19016},{"type":21,"tag":455,"props":19213,"children":19214},{"style":1365},[19215],{"type":26,"value":4520},{"type":21,"tag":455,"props":19217,"children":19218},{"style":486},[19219],{"type":26,"value":489},{"type":21,"tag":455,"props":19221,"children":19222},{"style":1349},[19223],{"type":26,"value":3897},{"type":21,"tag":455,"props":19225,"children":19226},{"style":486},[19227],{"type":26,"value":489},{"type":21,"tag":455,"props":19229,"children":19230},{"style":4527},[19231],{"type":26,"value":19232},"clients",{"type":21,"tag":455,"props":19234,"children":19235},{"style":486},[19236],{"type":26,"value":7363},{"type":21,"tag":455,"props":19238,"children":19239},{"class":457,"line":12895},[19240,19244,19248,19252,19256,19261,19265,19269,19273,19277,19282],{"type":21,"tag":455,"props":19241,"children":19242},{"style":1349},[19243],{"type":26,"value":14254},{"type":21,"tag":455,"props":19245,"children":19246},{"style":480},[19247],{"type":26,"value":19053},{"type":21,"tag":455,"props":19249,"children":19250},{"style":486},[19251],{"type":26,"value":551},{"type":21,"tag":455,"props":19253,"children":19254},{"style":1365},[19255],{"type":26,"value":7549},{"type":21,"tag":455,"props":19257,"children":19258},{"style":486},[19259],{"type":26,"value":19260},"(clients.",{"type":21,"tag":455,"props":19262,"children":19263},{"style":1365},[19264],{"type":26,"value":19078},{"type":21,"tag":455,"props":19266,"children":19267},{"style":486},[19268],{"type":26,"value":489},{"type":21,"tag":455,"props":19270,"children":19271},{"style":1349},[19272],{"type":26,"value":3897},{"type":21,"tag":455,"props":19274,"children":19275},{"style":486},[19276],{"type":26,"value":489},{"type":21,"tag":455,"props":19278,"children":19279},{"style":4527},[19280],{"type":26,"value":19281},"client",{"type":21,"tag":455,"props":19283,"children":19284},{"style":486},[19285],{"type":26,"value":7363},{"type":21,"tag":455,"props":19287,"children":19288},{"class":457,"line":12903},[19289,19293,19298,19303,19308,19313],{"type":21,"tag":455,"props":19290,"children":19291},{"style":1349},[19292],{"type":26,"value":12448},{"type":21,"tag":455,"props":19294,"children":19295},{"style":486},[19296],{"type":26,"value":19297}," client.",{"type":21,"tag":455,"props":19299,"children":19300},{"style":1365},[19301],{"type":26,"value":19302},"postMessage",{"type":21,"tag":455,"props":19304,"children":19305},{"style":486},[19306],{"type":26,"value":19307},"({message:",{"type":21,"tag":455,"props":19309,"children":19310},{"style":492},[19311],{"type":26,"value":19312},"'needs-reload'",{"type":21,"tag":455,"props":19314,"children":19315},{"style":486},[19316],{"type":26,"value":3952},{"type":21,"tag":455,"props":19318,"children":19319},{"class":457,"line":12911},[19320],{"type":21,"tag":455,"props":19321,"children":19322},{"style":486},[19323],{"type":26,"value":19324},"                }));\n",{"type":21,"tag":455,"props":19326,"children":19327},{"class":457,"line":12928},[19328],{"type":21,"tag":455,"props":19329,"children":19330},{"style":486},[19331],{"type":26,"value":19332},"            });\n",{"type":21,"tag":455,"props":19334,"children":19335},{"class":457,"line":12936},[19336],{"type":21,"tag":455,"props":19337,"children":19338},{"style":486},[19339],{"type":26,"value":18837},{"type":21,"tag":455,"props":19341,"children":19342},{"class":457,"line":12953},[19343],{"type":21,"tag":455,"props":19344,"children":19345},{"style":486},[19346],{"type":26,"value":18845},{"type":21,"tag":455,"props":19348,"children":19349},{"class":457,"line":12961},[19350],{"type":21,"tag":455,"props":19351,"children":19352},{"style":486},[19353],{"type":26,"value":3952},{"type":21,"tag":455,"props":19355,"children":19356},{"class":457,"line":12973},[19357],{"type":21,"tag":455,"props":19358,"children":19359},{"emptyLinePlaceholder":471},[19360],{"type":26,"value":474},{"type":21,"tag":455,"props":19362,"children":19364},{"class":457,"line":19363},77,[19365],{"type":21,"tag":455,"props":19366,"children":19367},{"style":462},[19368],{"type":26,"value":17461},{"type":21,"tag":455,"props":19370,"children":19372},{"class":457,"line":19371},78,[19373],{"type":21,"tag":455,"props":19374,"children":19375},{"style":462},[19376],{"type":26,"value":19377}," * Intercept network requests so that we can serve the requested resource from the\n",{"type":21,"tag":455,"props":19379,"children":19381},{"class":457,"line":19380},79,[19382],{"type":21,"tag":455,"props":19383,"children":19384},{"style":462},[19385],{"type":26,"value":19386}," * cache, if we have it, or otherwise defer to the network.\n",{"type":21,"tag":455,"props":19388,"children":19390},{"class":457,"line":19389},80,[19391],{"type":21,"tag":455,"props":19392,"children":19393},{"style":462},[19394],{"type":26,"value":14425},{"type":21,"tag":455,"props":19396,"children":19398},{"class":457,"line":19397},81,[19399,19403,19407,19411,19416,19420,19424,19428,19432],{"type":21,"tag":455,"props":19400,"children":19401},{"style":486},[19402],{"type":26,"value":18653},{"type":21,"tag":455,"props":19404,"children":19405},{"style":1365},[19406],{"type":26,"value":17999},{"type":21,"tag":455,"props":19408,"children":19409},{"style":486},[19410],{"type":26,"value":489},{"type":21,"tag":455,"props":19412,"children":19413},{"style":492},[19414],{"type":26,"value":19415},"'fetch'",{"type":21,"tag":455,"props":19417,"children":19418},{"style":486},[19419],{"type":26,"value":2228},{"type":21,"tag":455,"props":19421,"children":19422},{"style":1349},[19423],{"type":26,"value":3897},{"type":21,"tag":455,"props":19425,"children":19426},{"style":486},[19427],{"type":26,"value":489},{"type":21,"tag":455,"props":19429,"children":19430},{"style":4527},[19431],{"type":26,"value":17902},{"type":21,"tag":455,"props":19433,"children":19434},{"style":486},[19435],{"type":26,"value":7363},{"type":21,"tag":455,"props":19437,"children":19439},{"class":457,"line":19438},82,[19440],{"type":21,"tag":455,"props":19441,"children":19442},{"style":462},[19443],{"type":26,"value":19444},"    // Workaround for Chromium bug that makes ignoring the search\n",{"type":21,"tag":455,"props":19446,"children":19448},{"class":457,"line":19447},83,[19449],{"type":21,"tag":455,"props":19450,"children":19451},{"style":462},[19452],{"type":26,"value":19453},"    // parameter very slow when matching the request against the\n",{"type":21,"tag":455,"props":19455,"children":19457},{"class":457,"line":19456},84,[19458],{"type":21,"tag":455,"props":19459,"children":19460},{"style":462},[19461],{"type":26,"value":19462},"    // cached values: https://bugs.chromium.org/p/chromium/issues/detail?id=682677.\n",{"type":21,"tag":455,"props":19464,"children":19466},{"class":457,"line":19465},85,[19467],{"type":21,"tag":455,"props":19468,"children":19469},{"style":462},[19470],{"type":26,"value":19471},"    // Your application may not need this - or hey, it may even be fixed by the time\n",{"type":21,"tag":455,"props":19473,"children":19475},{"class":457,"line":19474},86,[19476],{"type":21,"tag":455,"props":19477,"children":19478},{"style":462},[19479],{"type":26,"value":19480},"    // you're reading this!\n",{"type":21,"tag":455,"props":19482,"children":19484},{"class":457,"line":19483},87,[19485,19489,19494,19498,19503,19508,19512,19517,19521,19525,19529,19533],{"type":21,"tag":455,"props":19486,"children":19487},{"style":1349},[19488],{"type":26,"value":17508},{"type":21,"tag":455,"props":19490,"children":19491},{"style":486},[19492],{"type":26,"value":19493}," hasSearch ",{"type":21,"tag":455,"props":19495,"children":19496},{"style":1349},[19497],{"type":26,"value":3193},{"type":21,"tag":455,"props":19499,"children":19500},{"style":486},[19501],{"type":26,"value":19502}," (event.request.url.",{"type":21,"tag":455,"props":19504,"children":19505},{"style":1365},[19506],{"type":26,"value":19507},"indexOf",{"type":21,"tag":455,"props":19509,"children":19510},{"style":486},[19511],{"type":26,"value":489},{"type":21,"tag":455,"props":19513,"children":19514},{"style":492},[19515],{"type":26,"value":19516},"'?'",{"type":21,"tag":455,"props":19518,"children":19519},{"style":486},[19520],{"type":26,"value":4584},{"type":21,"tag":455,"props":19522,"children":19523},{"style":1349},[19524],{"type":26,"value":7353},{"type":21,"tag":455,"props":19526,"children":19527},{"style":1349},[19528],{"type":26,"value":2238},{"type":21,"tag":455,"props":19530,"children":19531},{"style":480},[19532],{"type":26,"value":7837},{"type":21,"tag":455,"props":19534,"children":19535},{"style":486},[19536],{"type":26,"value":2171},{"type":21,"tag":455,"props":19538,"children":19540},{"class":457,"line":19539},88,[19541],{"type":21,"tag":455,"props":19542,"children":19543},{"emptyLinePlaceholder":471},[19544],{"type":26,"value":474},{"type":21,"tag":455,"props":19546,"children":19548},{"class":457,"line":19547},89,[19549,19553,19558],{"type":21,"tag":455,"props":19550,"children":19551},{"style":486},[19552],{"type":26,"value":18694},{"type":21,"tag":455,"props":19554,"children":19555},{"style":1365},[19556],{"type":26,"value":19557},"respondWith",{"type":21,"tag":455,"props":19559,"children":19560},{"style":486},[19561],{"type":26,"value":11391},{"type":21,"tag":455,"props":19563,"children":19565},{"class":457,"line":19564},90,[19566,19570,19575],{"type":21,"tag":455,"props":19567,"children":19568},{"style":486},[19569],{"type":26,"value":18711},{"type":21,"tag":455,"props":19571,"children":19572},{"style":1365},[19573],{"type":26,"value":19574},"match",{"type":21,"tag":455,"props":19576,"children":19577},{"style":486},[19578],{"type":26,"value":19579},"(event.request, {\n",{"type":21,"tag":455,"props":19581,"children":19583},{"class":457,"line":19582},91,[19584],{"type":21,"tag":455,"props":19585,"children":19586},{"style":486},[19587],{"type":26,"value":19588},"            ignoreSearch: hasSearch\n",{"type":21,"tag":455,"props":19590,"children":19592},{"class":457,"line":19591},92,[19593,19597,19601,19605,19609,19613,19618],{"type":21,"tag":455,"props":19594,"children":19595},{"style":486},[19596],{"type":26,"value":18776},{"type":21,"tag":455,"props":19598,"children":19599},{"style":1365},[19600],{"type":26,"value":4520},{"type":21,"tag":455,"props":19602,"children":19603},{"style":486},[19604],{"type":26,"value":489},{"type":21,"tag":455,"props":19606,"children":19607},{"style":1349},[19608],{"type":26,"value":3897},{"type":21,"tag":455,"props":19610,"children":19611},{"style":486},[19612],{"type":26,"value":489},{"type":21,"tag":455,"props":19614,"children":19615},{"style":4527},[19616],{"type":26,"value":19617},"response",{"type":21,"tag":455,"props":19619,"children":19620},{"style":486},[19621],{"type":26,"value":7363},{"type":21,"tag":455,"props":19623,"children":19625},{"class":457,"line":19624},93,[19626,19630,19635,19639,19644],{"type":21,"tag":455,"props":19627,"children":19628},{"style":1349},[19629],{"type":26,"value":17196},{"type":21,"tag":455,"props":19631,"children":19632},{"style":486},[19633],{"type":26,"value":19634}," response ",{"type":21,"tag":455,"props":19636,"children":19637},{"style":1349},[19638],{"type":26,"value":12183},{"type":21,"tag":455,"props":19640,"children":19641},{"style":1365},[19642],{"type":26,"value":19643}," fetch",{"type":21,"tag":455,"props":19645,"children":19646},{"style":486},[19647],{"type":26,"value":19648},"(event.request);\n",{"type":21,"tag":455,"props":19650,"children":19652},{"class":457,"line":19651},94,[19653],{"type":21,"tag":455,"props":19654,"children":19655},{"style":486},[19656],{"type":26,"value":18837},{"type":21,"tag":455,"props":19658,"children":19660},{"class":457,"line":19659},95,[19661],{"type":21,"tag":455,"props":19662,"children":19663},{"style":486},[19664],{"type":26,"value":18845},{"type":21,"tag":455,"props":19666,"children":19668},{"class":457,"line":19667},96,[19669],{"type":21,"tag":455,"props":19670,"children":19671},{"style":486},[19672],{"type":26,"value":3952},{"type":21,"tag":22,"props":19674,"children":19675},{},[19676],{"type":26,"value":19677},"So, that's a lot to take in, but the comments above should help. Let's break down a few of the more complex bits:",{"type":21,"tag":80,"props":19679,"children":19681},{"id":19680},"install",[19682],{"type":26,"value":19683},"Install",{"type":21,"tag":22,"props":19685,"children":19686},{},[19687],{"type":26,"value":19688},"So, you'll notice that we have add an event listener for 'install'. This is the event that gets fired when a new version of the Service Worker is installed - whether for the first time, or because the Service Worker has changed in some way. The browser does a byte-wise comparison of the downloaded script, so any change will trigger a re-install of the script.",{"type":21,"tag":22,"props":19690,"children":19691},{},[19692,19694,19700],{"type":26,"value":19693},"Often, you won't need to change anything in the script itself, but some of the cached resources have changed, and we need to tell the browser that we want to refresh the cache. In order to do this, we'll change the ",{"type":21,"tag":246,"props":19695,"children":19697},{"className":19696},[],[19698],{"type":26,"value":19699},"cacheVersion",{"type":26,"value":19701}," number, which will trigger the browser to re-install the Service Worker, triggering our install event, and allowing us to re-downloand and cache all of the updated resources.",{"type":21,"tag":80,"props":19703,"children":19705},{"id":19704},"activate",[19706],{"type":26,"value":19707},"Activate",{"type":21,"tag":22,"props":19709,"children":19710},{},[19711,19713,19719],{"type":26,"value":19712},"After that, we have a listener on the 'activate' event. This gets triggered when our service worker takes control of the context - once it becomes 'active'. You'll notice in our 'install' event that we're calling ",{"type":21,"tag":246,"props":19714,"children":19716},{"className":19715},[],[19717],{"type":26,"value":19718},"self.skipWaiting();",{"type":26,"value":19720},". The normal behaviour is that when a new Service Worker is installed, it doesn't take over from the old Service Worker until the page is refreshed. This could be fine for your case, but in many cases, we'll want to start serving the new resources right away, so we tell the browser to skip waiting for this Service Worker, and activate it immediately, allowing it to take over from the old Service Worker (if any).",{"type":21,"tag":22,"props":19722,"children":19723},{},[19724,19726,19733],{"type":26,"value":19725},"In the activate event listener, we then clear out all of our old cache versions, leaving just the specified cache available. You'll want to take a look at the ",{"type":21,"tag":322,"props":19727,"children":19730},{"href":19728,"rel":19729},"https://developer.mozilla.org/en-US/docs/Web/API/Cache",[326],[19731],{"type":26,"value":19732},"Cache Interface",{"type":26,"value":19734}," for more details here.",{"type":21,"tag":22,"props":19736,"children":19737},{},[19738,19740,19746,19748,19754],{"type":26,"value":19739},"Finally, once we've cleared out the old caches, we post a message back to main thread of any clients we have control over that they should reload, since the cache has been updated (remember the ",{"type":21,"tag":246,"props":19741,"children":19743},{"className":19742},[],[19744],{"type":26,"value":19745},"handleMessage",{"type":26,"value":19747}," function in the install workers script?). What you want to do with this message will depend on your application, but it could be as simple as calling ",{"type":21,"tag":246,"props":19749,"children":19751},{"className":19750},[],[19752],{"type":26,"value":19753},"location.reload()",{"type":26,"value":19755}," to get the new resources from the Service Worker.",{"type":21,"tag":80,"props":19757,"children":19759},{"id":19758},"fetch",[19760],{"type":26,"value":19761},"Fetch",{"type":21,"tag":22,"props":19763,"children":19764},{},[19765,19767,19774],{"type":26,"value":19766},"And here's where the actual offline-enabling functionality happens. You'll want to take a look at the ",{"type":21,"tag":322,"props":19768,"children":19771},{"href":19769,"rel":19770},"https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch",[326],[19772],{"type":26,"value":19773},"Fetch API",{"type":26,"value":19775}," for more details on fetch, but the point here is that we're intercepting all requests from the client (that's the browser main thread for our origin), and checking the requested resource name against our cache. If we have the resource, we return it from our cache, and otherwise we pass the request through to the network.",{"type":21,"tag":22,"props":19777,"children":19778},{},[19779],{"type":26,"value":19780},"Two things are worth noting here:",{"type":21,"tag":183,"props":19782,"children":19783},{},[19784,19795],{"type":21,"tag":187,"props":19785,"children":19786},{},[19787,19789,19793],{"type":26,"value":19788},"Our fetch intercept catches ",{"type":21,"tag":33,"props":19790,"children":19791},{},[19792],{"type":26,"value":7549},{"type":26,"value":19794}," requests - not just XMLHTTPRequests, for example, but every single request the main thread makes of the network. There's a lot that can be done with this - we're just scratching the surface here.",{"type":21,"tag":187,"props":19796,"children":19797},{},[19798,19800,19807],{"type":26,"value":19799},"The 'serve from cache if available, and otherwise pass to the network' is just one potential model for how we can handle caching and serving resources with the Service Worker - see ",{"type":21,"tag":322,"props":19801,"children":19804},{"href":19802,"rel":19803},"https://developers.google.com/web/ilt/pwa/lab-caching-files-with-service-worker",[326],[19805],{"type":26,"value":19806},"this article on Caching File with Service Worker",{"type":26,"value":19808}," for some alternative approaches.",{"type":21,"tag":56,"props":19810,"children":19811},{"id":16569},[19812],{"type":26,"value":19813},"Are We There Yet?",{"type":21,"tag":22,"props":19815,"children":19816},{},[19817,19819,19826],{"type":26,"value":19818},"Pretty much! Assuming you can mark the other items off the checklists above, your application should now be ready to serve itself as a PWA. To confirm this, you can use the ",{"type":21,"tag":322,"props":19820,"children":19823},{"href":19821,"rel":19822},"https://developers.google.com/web/tools/lighthouse/",[326],[19824],{"type":26,"value":19825},"Lighthouse Extension",{"type":26,"value":19827}," from Google to test your site, and confirm that all is as it should be.",{"type":21,"tag":22,"props":19829,"children":19830},{},[19831],{"type":26,"value":19832},"While this gives you a PWA suitable for, say, desktop use, there's still a few pieces to the puzzle before we're ready to be assigned real estate on the home screens of Android or iOS devices - we'll be going into that next time.",{"type":21,"tag":1279,"props":19834,"children":19835},{},[19836],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":19838},[19839,19840,19841,19842,19847],{"id":16758,"depth":336,"text":16761},{"id":16909,"depth":336,"text":16912},{"id":17417,"depth":336,"text":17420},{"id":18295,"depth":336,"text":18298,"children":19843},[19844,19845,19846],{"id":19680,"depth":333,"text":19683},{"id":19704,"depth":333,"text":19707},{"id":19758,"depth":333,"text":19761},{"id":16569,"depth":336,"text":19813},"content:ckeefer:2017-2:MorePWAToYa-Part1.md","ckeefer/2017-2/MorePWAToYa-Part1.md","ckeefer/2017-2/MorePWAToYa-Part1",{"user":16604,"name":16605},{"_path":19853,"_dir":19854,"_draft":7,"_partial":7,"_locale":8,"title":19855,"description":19856,"publishDate":19857,"tags":19858,"excerpt":19856,"body":19860,"_type":343,"_id":23615,"_source":345,"_file":23616,"_stem":23617,"_extension":348,"author":23618},"/ckeefer/2016-7/gofetch2","2016-7","Go Fetch 2! (JavaScript Fetch API)","Last time we discussed the Fetch API in general, taking a look at how it differed from the XMLHttpRequest API, and some of its advantages. Today, we're going to take a look at a little library that you can include in your projects today that offers you localStorage caching for the Fetch API.","2016-10-10",[2804,19859,15],"jquery",{"type":18,"children":19861,"toc":23608},[19862,19872,19878,19891,19897,19909,19936,19949,19954,19960,19974,19980,19985,20014,20625,20644,20968,20981,20987,21000,23604],{"type":21,"tag":22,"props":19863,"children":19864},{},[19865,19870],{"type":21,"tag":322,"props":19866,"children":19868},{"href":19867},"/search/user:ckeefer/go/fetch",[19869],{"type":26,"value":15155},{"type":26,"value":19871}," we discussed the Fetch API in general, taking a look at how it differed from the XMLHttpRequest API, and some of its advantages. Today, we're going to take a look at a little library that you can include in your projects today that offers you localStorage caching for the Fetch API.",{"type":21,"tag":80,"props":19873,"children":19875},{"id":19874},"tlwr",[19876],{"type":26,"value":19877},"TL;WR",{"type":21,"tag":22,"props":19879,"children":19880},{},[19881,19883,19890],{"type":26,"value":19882},"If you're anxious to get to the good stuff, you can head straight to the ",{"type":21,"tag":322,"props":19884,"children":19887},{"href":19885,"rel":19886},"https://github.com/SaneMethod/fetchCache",[326],[19888],{"type":26,"value":19889},"fetchCache Github Repo",{"type":26,"value":551},{"type":21,"tag":80,"props":19892,"children":19894},{"id":19893},"cache-control",[19895],{"type":26,"value":19896},"Cache Control",{"type":21,"tag":22,"props":19898,"children":19899},{},[19900,19902,19907],{"type":26,"value":19901},"Before we get started, those of you more familiar with the Fetch API might be wondering why we need to use localStorage for caching fetch request responses at all. Isn't that what the ",{"type":21,"tag":246,"props":19903,"children":19905},{"className":19904},[],[19906],{"type":26,"value":18742},{"type":26,"value":19908}," property in the fetch settings is for?",{"type":21,"tag":22,"props":19910,"children":19911},{},[19912,19914,19920,19921,19927,19928,19934],{"type":26,"value":19913},"Yes and no. The cache setting allows you greater control over the standard HTTP cache, so that you don't need to use the old trick of appending a datetime string to your request for 'cache-busting' purposes, as you might have done in the past with XHR. Instead, you can simply specify ",{"type":21,"tag":246,"props":19915,"children":19917},{"className":19916},[],[19918],{"type":26,"value":19919},"cache:'no-store'",{"type":26,"value":411},{"type":21,"tag":246,"props":19922,"children":19924},{"className":19923},[],[19925],{"type":26,"value":19926},"cache:'no-cache'",{"type":26,"value":411},{"type":21,"tag":246,"props":19929,"children":19931},{"className":19930},[],[19932],{"type":26,"value":19933},"cache:'reload'",{"type":26,"value":19935},", depending on your use case.",{"type":21,"tag":22,"props":19937,"children":19938},{},[19939,19941,19947],{"type":26,"value":19940},"You can also bypass a network request (potentially useful for offline usage) and pull from the HTTP cache via ",{"type":21,"tag":246,"props":19942,"children":19944},{"className":19943},[],[19945],{"type":26,"value":19946},"cache:'force-cache'",{"type":26,"value":19948},", which appears to accomplish one of the goals of caching in localStorage.",{"type":21,"tag":22,"props":19950,"children":19951},{},[19952],{"type":26,"value":19953},"However, that's as far as our control over the cache goes. We can't specify how long we want items to remain in the cache, or what requests line up with which contents of the cache, or validate the contents of the cache before returning them. For that, we need to wrap fetch with a bit of custom logic to allow us to handle the caching ourselves.",{"type":21,"tag":80,"props":19955,"children":19957},{"id":19956},"looks-familiar",[19958],{"type":26,"value":19959},"Looks Familiar",{"type":21,"tag":22,"props":19961,"children":19962},{},[19963,19965,19972],{"type":26,"value":19964},"Just before we dive in, you may find that the API I'm about to present looks familiar - maybe because you've used ",{"type":21,"tag":322,"props":19966,"children":19969},{"href":19967,"rel":19968},"https://github.com/SaneMethod/jquery-ajax-localstorage-cache",[326],[19970],{"type":26,"value":19971},"JALC",{"type":26,"value":19973},", the plugin for similar caching with jQuery.ajax. The API similarity is most definitely on purpose, and you should feel comfortable moving from one to the other.",{"type":21,"tag":80,"props":19975,"children":19977},{"id":19976},"the-breakdown",[19978],{"type":26,"value":19979},"The Breakdown",{"type":21,"tag":22,"props":19981,"children":19982},{},[19983],{"type":26,"value":19984},"So, let's take a look at the code, and discuss what's happening.",{"type":21,"tag":22,"props":19986,"children":19987},{},[19988,19990,19997,19999,20005,20006,20012],{"type":26,"value":19989},"For those of you who've used JALC, much of this will look familiar, and you can reference ",{"type":21,"tag":322,"props":19991,"children":19994},{"href":19992,"rel":19993},"https://artandlogic.com/2013/06/ajax-caching-transports-compatible-with-jquery-deferred/",[326],[19995],{"type":26,"value":19996},"this previous breakdown of the core parts of JALC for more details",{"type":26,"value":19998},", so we'll skip over those. Let's take a look at our ",{"type":21,"tag":246,"props":20000,"children":20002},{"className":20001},[],[20003],{"type":26,"value":20004},"cacheResponse",{"type":26,"value":386},{"type":21,"tag":246,"props":20007,"children":20009},{"className":20008},[],[20010],{"type":26,"value":20011},"provideResponse",{"type":26,"value":20013}," functions, as they're the juicy bits.",{"type":21,"tag":239,"props":20015,"children":20019},{"className":20016,"code":20017,"language":20018,"meta":8,"style":8},"language-javascript shiki shiki-themes github-light github-dark","   /**\n    * Cache the response into our storage object.\n    * We clone the response so that we can drain the stream without making it\n    * unavailable to future handlers.\n    *\n    * @param {string} cacheKey Key under which to cache the data string. Bound in\n    * fetch override.\n    * @param {Storage} storage Object implementing Storage interface to store cached data\n    * (text or json exclusively) in. Bound in fetch override.\n    * @param {Number} hourstl Number of hours this value shoud remain in the cache.\n    * Bound in fetch override.\n    * @param {Response} response\n    */\nfunction cacheResponse(cacheKey, storage, hourstl, response) {\n    var cres = response.clone(),\n        dataType = (response.headers.get('Content-Type') || 'text/plain').toLowerCase();\n\n    cres.text().then((text) => {\n        try {\n            storage.setItem(cacheKey, text);\n            storage.setItem(cacheKey + 'cachettl', +new Date() + 1000 * 60 * 60 * hourstl);\n            storage.setItem(cacheKey + 'dataType', dataType);\n        } catch (e) {\n            // Remove any incomplete data that may have been saved before the exception was caught\n            removeFromStorage(storage, cacheKey);\n            console.log('Cache Error: ' + e, cacheKey, text);\n        }\n    });\n\n    return response;\n}\n\n","javascript",[20020],{"type":21,"tag":246,"props":20021,"children":20022},{"__ignoreMap":8},[20023,20031,20039,20047,20055,20063,20090,20098,20124,20132,20158,20166,20187,20195,20246,20276,20332,20339,20379,20391,20409,20488,20517,20534,20542,20555,20584,20591,20599,20606,20618],{"type":21,"tag":455,"props":20024,"children":20025},{"class":457,"line":458},[20026],{"type":21,"tag":455,"props":20027,"children":20028},{"style":462},[20029],{"type":26,"value":20030},"   /**\n",{"type":21,"tag":455,"props":20032,"children":20033},{"class":457,"line":336},[20034],{"type":21,"tag":455,"props":20035,"children":20036},{"style":462},[20037],{"type":26,"value":20038},"    * Cache the response into our storage object.\n",{"type":21,"tag":455,"props":20040,"children":20041},{"class":457,"line":333},[20042],{"type":21,"tag":455,"props":20043,"children":20044},{"style":462},[20045],{"type":26,"value":20046},"    * We clone the response so that we can drain the stream without making it\n",{"type":21,"tag":455,"props":20048,"children":20049},{"class":457,"line":1424},[20050],{"type":21,"tag":455,"props":20051,"children":20052},{"style":462},[20053],{"type":26,"value":20054},"    * unavailable to future handlers.\n",{"type":21,"tag":455,"props":20056,"children":20057},{"class":457,"line":1443},[20058],{"type":21,"tag":455,"props":20059,"children":20060},{"style":462},[20061],{"type":26,"value":20062},"    *\n",{"type":21,"tag":455,"props":20064,"children":20065},{"class":457,"line":1489},[20066,20071,20075,20080,20085],{"type":21,"tag":455,"props":20067,"children":20068},{"style":462},[20069],{"type":26,"value":20070},"    * ",{"type":21,"tag":455,"props":20072,"children":20073},{"style":1349},[20074],{"type":26,"value":17614},{"type":21,"tag":455,"props":20076,"children":20077},{"style":1365},[20078],{"type":26,"value":20079}," {string}",{"type":21,"tag":455,"props":20081,"children":20082},{"style":486},[20083],{"type":26,"value":20084}," cacheKey",{"type":21,"tag":455,"props":20086,"children":20087},{"style":462},[20088],{"type":26,"value":20089}," Key under which to cache the data string. Bound in\n",{"type":21,"tag":455,"props":20091,"children":20092},{"class":457,"line":1506},[20093],{"type":21,"tag":455,"props":20094,"children":20095},{"style":462},[20096],{"type":26,"value":20097},"    * fetch override.\n",{"type":21,"tag":455,"props":20099,"children":20100},{"class":457,"line":1547},[20101,20105,20109,20114,20119],{"type":21,"tag":455,"props":20102,"children":20103},{"style":462},[20104],{"type":26,"value":20070},{"type":21,"tag":455,"props":20106,"children":20107},{"style":1349},[20108],{"type":26,"value":17614},{"type":21,"tag":455,"props":20110,"children":20111},{"style":1365},[20112],{"type":26,"value":20113}," {Storage}",{"type":21,"tag":455,"props":20115,"children":20116},{"style":486},[20117],{"type":26,"value":20118}," storage",{"type":21,"tag":455,"props":20120,"children":20121},{"style":462},[20122],{"type":26,"value":20123}," Object implementing Storage interface to store cached data\n",{"type":21,"tag":455,"props":20125,"children":20126},{"class":457,"line":1564},[20127],{"type":21,"tag":455,"props":20128,"children":20129},{"style":462},[20130],{"type":26,"value":20131},"    * (text or json exclusively) in. Bound in fetch override.\n",{"type":21,"tag":455,"props":20133,"children":20134},{"class":457,"line":1605},[20135,20139,20143,20148,20153],{"type":21,"tag":455,"props":20136,"children":20137},{"style":462},[20138],{"type":26,"value":20070},{"type":21,"tag":455,"props":20140,"children":20141},{"style":1349},[20142],{"type":26,"value":17614},{"type":21,"tag":455,"props":20144,"children":20145},{"style":1365},[20146],{"type":26,"value":20147}," {Number}",{"type":21,"tag":455,"props":20149,"children":20150},{"style":486},[20151],{"type":26,"value":20152}," hourstl",{"type":21,"tag":455,"props":20154,"children":20155},{"style":462},[20156],{"type":26,"value":20157}," Number of hours this value shoud remain in the cache.\n",{"type":21,"tag":455,"props":20159,"children":20160},{"class":457,"line":1622},[20161],{"type":21,"tag":455,"props":20162,"children":20163},{"style":462},[20164],{"type":26,"value":20165},"    * Bound in fetch override.\n",{"type":21,"tag":455,"props":20167,"children":20168},{"class":457,"line":1663},[20169,20173,20177,20182],{"type":21,"tag":455,"props":20170,"children":20171},{"style":462},[20172],{"type":26,"value":20070},{"type":21,"tag":455,"props":20174,"children":20175},{"style":1349},[20176],{"type":26,"value":17614},{"type":21,"tag":455,"props":20178,"children":20179},{"style":1365},[20180],{"type":26,"value":20181}," {Response}",{"type":21,"tag":455,"props":20183,"children":20184},{"style":486},[20185],{"type":26,"value":20186}," response\n",{"type":21,"tag":455,"props":20188,"children":20189},{"class":457,"line":1680},[20190],{"type":21,"tag":455,"props":20191,"children":20192},{"style":462},[20193],{"type":26,"value":20194},"    */\n",{"type":21,"tag":455,"props":20196,"children":20197},{"class":457,"line":1721},[20198,20202,20207,20211,20216,20220,20225,20229,20234,20238,20242],{"type":21,"tag":455,"props":20199,"children":20200},{"style":1349},[20201],{"type":26,"value":3897},{"type":21,"tag":455,"props":20203,"children":20204},{"style":1365},[20205],{"type":26,"value":20206}," cacheResponse",{"type":21,"tag":455,"props":20208,"children":20209},{"style":486},[20210],{"type":26,"value":489},{"type":21,"tag":455,"props":20212,"children":20213},{"style":4527},[20214],{"type":26,"value":20215},"cacheKey",{"type":21,"tag":455,"props":20217,"children":20218},{"style":486},[20219],{"type":26,"value":2228},{"type":21,"tag":455,"props":20221,"children":20222},{"style":4527},[20223],{"type":26,"value":20224},"storage",{"type":21,"tag":455,"props":20226,"children":20227},{"style":486},[20228],{"type":26,"value":2228},{"type":21,"tag":455,"props":20230,"children":20231},{"style":4527},[20232],{"type":26,"value":20233},"hourstl",{"type":21,"tag":455,"props":20235,"children":20236},{"style":486},[20237],{"type":26,"value":2228},{"type":21,"tag":455,"props":20239,"children":20240},{"style":4527},[20241],{"type":26,"value":19617},{"type":21,"tag":455,"props":20243,"children":20244},{"style":486},[20245],{"type":26,"value":4879},{"type":21,"tag":455,"props":20247,"children":20248},{"class":457,"line":1738},[20249,20253,20258,20262,20266,20271],{"type":21,"tag":455,"props":20250,"children":20251},{"style":1349},[20252],{"type":26,"value":17508},{"type":21,"tag":455,"props":20254,"children":20255},{"style":486},[20256],{"type":26,"value":20257}," cres ",{"type":21,"tag":455,"props":20259,"children":20260},{"style":1349},[20261],{"type":26,"value":3193},{"type":21,"tag":455,"props":20263,"children":20264},{"style":486},[20265],{"type":26,"value":10279},{"type":21,"tag":455,"props":20267,"children":20268},{"style":1365},[20269],{"type":26,"value":20270},"clone",{"type":21,"tag":455,"props":20272,"children":20273},{"style":486},[20274],{"type":26,"value":20275},"(),\n",{"type":21,"tag":455,"props":20277,"children":20278},{"class":457,"line":1779},[20279,20284,20288,20293,20297,20301,20306,20310,20314,20319,20323,20328],{"type":21,"tag":455,"props":20280,"children":20281},{"style":486},[20282],{"type":26,"value":20283},"        dataType ",{"type":21,"tag":455,"props":20285,"children":20286},{"style":1349},[20287],{"type":26,"value":3193},{"type":21,"tag":455,"props":20289,"children":20290},{"style":486},[20291],{"type":26,"value":20292}," (response.headers.",{"type":21,"tag":455,"props":20294,"children":20295},{"style":1365},[20296],{"type":26,"value":6943},{"type":21,"tag":455,"props":20298,"children":20299},{"style":486},[20300],{"type":26,"value":489},{"type":21,"tag":455,"props":20302,"children":20303},{"style":492},[20304],{"type":26,"value":20305},"'Content-Type'",{"type":21,"tag":455,"props":20307,"children":20308},{"style":486},[20309],{"type":26,"value":4584},{"type":21,"tag":455,"props":20311,"children":20312},{"style":1349},[20313],{"type":26,"value":12183},{"type":21,"tag":455,"props":20315,"children":20316},{"style":492},[20317],{"type":26,"value":20318}," 'text/plain'",{"type":21,"tag":455,"props":20320,"children":20321},{"style":486},[20322],{"type":26,"value":5081},{"type":21,"tag":455,"props":20324,"children":20325},{"style":1365},[20326],{"type":26,"value":20327},"toLowerCase",{"type":21,"tag":455,"props":20329,"children":20330},{"style":486},[20331],{"type":26,"value":3398},{"type":21,"tag":455,"props":20333,"children":20334},{"class":457,"line":1796},[20335],{"type":21,"tag":455,"props":20336,"children":20337},{"emptyLinePlaceholder":471},[20338],{"type":26,"value":474},{"type":21,"tag":455,"props":20340,"children":20341},{"class":457,"line":1837},[20342,20347,20351,20355,20359,20363,20367,20371,20375],{"type":21,"tag":455,"props":20343,"children":20344},{"style":486},[20345],{"type":26,"value":20346},"    cres.",{"type":21,"tag":455,"props":20348,"children":20349},{"style":1365},[20350],{"type":26,"value":26},{"type":21,"tag":455,"props":20352,"children":20353},{"style":486},[20354],{"type":26,"value":19016},{"type":21,"tag":455,"props":20356,"children":20357},{"style":1365},[20358],{"type":26,"value":4520},{"type":21,"tag":455,"props":20360,"children":20361},{"style":486},[20362],{"type":26,"value":4575},{"type":21,"tag":455,"props":20364,"children":20365},{"style":4527},[20366],{"type":26,"value":26},{"type":21,"tag":455,"props":20368,"children":20369},{"style":486},[20370],{"type":26,"value":4584},{"type":21,"tag":455,"props":20372,"children":20373},{"style":1349},[20374],{"type":26,"value":4589},{"type":21,"tag":455,"props":20376,"children":20377},{"style":486},[20378],{"type":26,"value":2470},{"type":21,"tag":455,"props":20380,"children":20381},{"class":457,"line":1854},[20382,20387],{"type":21,"tag":455,"props":20383,"children":20384},{"style":1349},[20385],{"type":26,"value":20386},"        try",{"type":21,"tag":455,"props":20388,"children":20389},{"style":486},[20390],{"type":26,"value":2470},{"type":21,"tag":455,"props":20392,"children":20393},{"class":457,"line":1895},[20394,20399,20404],{"type":21,"tag":455,"props":20395,"children":20396},{"style":486},[20397],{"type":26,"value":20398},"            storage.",{"type":21,"tag":455,"props":20400,"children":20401},{"style":1365},[20402],{"type":26,"value":20403},"setItem",{"type":21,"tag":455,"props":20405,"children":20406},{"style":486},[20407],{"type":26,"value":20408},"(cacheKey, text);\n",{"type":21,"tag":455,"props":20410,"children":20411},{"class":457,"line":1912},[20412,20416,20420,20425,20429,20434,20438,20443,20448,20453,20457,20462,20466,20471,20475,20479,20483],{"type":21,"tag":455,"props":20413,"children":20414},{"style":486},[20415],{"type":26,"value":20398},{"type":21,"tag":455,"props":20417,"children":20418},{"style":1365},[20419],{"type":26,"value":20403},{"type":21,"tag":455,"props":20421,"children":20422},{"style":486},[20423],{"type":26,"value":20424},"(cacheKey ",{"type":21,"tag":455,"props":20426,"children":20427},{"style":1349},[20428],{"type":26,"value":2206},{"type":21,"tag":455,"props":20430,"children":20431},{"style":492},[20432],{"type":26,"value":20433}," 'cachettl'",{"type":21,"tag":455,"props":20435,"children":20436},{"style":486},[20437],{"type":26,"value":2228},{"type":21,"tag":455,"props":20439,"children":20440},{"style":1349},[20441],{"type":26,"value":20442},"+new",{"type":21,"tag":455,"props":20444,"children":20445},{"style":1365},[20446],{"type":26,"value":20447}," Date",{"type":21,"tag":455,"props":20449,"children":20450},{"style":486},[20451],{"type":26,"value":20452},"() ",{"type":21,"tag":455,"props":20454,"children":20455},{"style":1349},[20456],{"type":26,"value":2206},{"type":21,"tag":455,"props":20458,"children":20459},{"style":480},[20460],{"type":26,"value":20461}," 1000",{"type":21,"tag":455,"props":20463,"children":20464},{"style":1349},[20465],{"type":26,"value":11055},{"type":21,"tag":455,"props":20467,"children":20468},{"style":480},[20469],{"type":26,"value":20470}," 60",{"type":21,"tag":455,"props":20472,"children":20473},{"style":1349},[20474],{"type":26,"value":11055},{"type":21,"tag":455,"props":20476,"children":20477},{"style":480},[20478],{"type":26,"value":20470},{"type":21,"tag":455,"props":20480,"children":20481},{"style":1349},[20482],{"type":26,"value":11055},{"type":21,"tag":455,"props":20484,"children":20485},{"style":486},[20486],{"type":26,"value":20487}," hourstl);\n",{"type":21,"tag":455,"props":20489,"children":20490},{"class":457,"line":1953},[20491,20495,20499,20503,20507,20512],{"type":21,"tag":455,"props":20492,"children":20493},{"style":486},[20494],{"type":26,"value":20398},{"type":21,"tag":455,"props":20496,"children":20497},{"style":1365},[20498],{"type":26,"value":20403},{"type":21,"tag":455,"props":20500,"children":20501},{"style":486},[20502],{"type":26,"value":20424},{"type":21,"tag":455,"props":20504,"children":20505},{"style":1349},[20506],{"type":26,"value":2206},{"type":21,"tag":455,"props":20508,"children":20509},{"style":492},[20510],{"type":26,"value":20511}," 'dataType'",{"type":21,"tag":455,"props":20513,"children":20514},{"style":486},[20515],{"type":26,"value":20516},", dataType);\n",{"type":21,"tag":455,"props":20518,"children":20519},{"class":457,"line":1970},[20520,20525,20529],{"type":21,"tag":455,"props":20521,"children":20522},{"style":486},[20523],{"type":26,"value":20524},"        } ",{"type":21,"tag":455,"props":20526,"children":20527},{"style":1349},[20528],{"type":26,"value":4638},{"type":21,"tag":455,"props":20530,"children":20531},{"style":486},[20532],{"type":26,"value":20533}," (e) {\n",{"type":21,"tag":455,"props":20535,"children":20536},{"class":457,"line":1978},[20537],{"type":21,"tag":455,"props":20538,"children":20539},{"style":462},[20540],{"type":26,"value":20541},"            // Remove any incomplete data that may have been saved before the exception was caught\n",{"type":21,"tag":455,"props":20543,"children":20544},{"class":457,"line":1996},[20545,20550],{"type":21,"tag":455,"props":20546,"children":20547},{"style":1365},[20548],{"type":26,"value":20549},"            removeFromStorage",{"type":21,"tag":455,"props":20551,"children":20552},{"style":486},[20553],{"type":26,"value":20554},"(storage, cacheKey);\n",{"type":21,"tag":455,"props":20556,"children":20557},{"class":457,"line":4487},[20558,20562,20566,20570,20575,20579],{"type":21,"tag":455,"props":20559,"children":20560},{"style":486},[20561],{"type":26,"value":17732},{"type":21,"tag":455,"props":20563,"children":20564},{"style":1365},[20565],{"type":26,"value":3915},{"type":21,"tag":455,"props":20567,"children":20568},{"style":486},[20569],{"type":26,"value":489},{"type":21,"tag":455,"props":20571,"children":20572},{"style":492},[20573],{"type":26,"value":20574},"'Cache Error: '",{"type":21,"tag":455,"props":20576,"children":20577},{"style":1349},[20578],{"type":26,"value":3929},{"type":21,"tag":455,"props":20580,"children":20581},{"style":486},[20582],{"type":26,"value":20583}," e, cacheKey, text);\n",{"type":21,"tag":455,"props":20585,"children":20586},{"class":457,"line":4495},[20587],{"type":21,"tag":455,"props":20588,"children":20589},{"style":486},[20590],{"type":26,"value":5680},{"type":21,"tag":455,"props":20592,"children":20593},{"class":457,"line":4509},[20594],{"type":21,"tag":455,"props":20595,"children":20596},{"style":486},[20597],{"type":26,"value":20598},"    });\n",{"type":21,"tag":455,"props":20600,"children":20601},{"class":457,"line":4561},[20602],{"type":21,"tag":455,"props":20603,"children":20604},{"emptyLinePlaceholder":471},[20605],{"type":26,"value":474},{"type":21,"tag":455,"props":20607,"children":20608},{"class":457,"line":4596},[20609,20613],{"type":21,"tag":455,"props":20610,"children":20611},{"style":1349},[20612],{"type":26,"value":1984},{"type":21,"tag":455,"props":20614,"children":20615},{"style":486},[20616],{"type":26,"value":20617}," response;\n",{"type":21,"tag":455,"props":20619,"children":20620},{"class":457,"line":4627},[20621],{"type":21,"tag":455,"props":20622,"children":20623},{"style":486},[20624],{"type":26,"value":2002},{"type":21,"tag":22,"props":20626,"children":20627},{},[20628,20630,20635,20637,20642],{"type":26,"value":20629},"The interesting bit here is you'll see we clone the response. This is so we can 'drain' it with the ",{"type":21,"tag":246,"props":20631,"children":20633},{"className":20632},[],[20634],{"type":26,"value":26},{"type":26,"value":20636}," reader and store the results, without making the response unavailable to future promise handler blocks. By cloning the response, we get a buffered clone of the response value that we can do with as we wish, and we can pass the original response on to be handled however the future ",{"type":21,"tag":246,"props":20638,"children":20640},{"className":20639},[],[20641],{"type":26,"value":4520},{"type":26,"value":20643}," blocks wish to.",{"type":21,"tag":239,"props":20645,"children":20647},{"className":20016,"code":20646,"language":20018,"meta":8,"style":8},"   /**\n    * Create a new response containing the cached value, and return a promise\n    * that resolves with this response.\n    *\n    * @param value\n    * @param dataType\n    * @returns {Promise}\n    */\nfunction provideResponse(value, dataType) {\n    var response = new Response(\n        value,\n        {\n            status: 200,\n            statusText: 'success',\n            headers: {\n                'Content-Type': dataType\n            }\n        }\n    );\n\n    return new Promise(function (resolve, reject) {\n        resolve(response);\n    });\n}\n",[20648],{"type":21,"tag":246,"props":20649,"children":20650},{"__ignoreMap":8},[20651,20658,20666,20674,20681,20697,20713,20730,20737,20771,20799,20807,20814,20830,20847,20855,20868,20875,20882,20889,20896,20941,20954,20961],{"type":21,"tag":455,"props":20652,"children":20653},{"class":457,"line":458},[20654],{"type":21,"tag":455,"props":20655,"children":20656},{"style":462},[20657],{"type":26,"value":20030},{"type":21,"tag":455,"props":20659,"children":20660},{"class":457,"line":336},[20661],{"type":21,"tag":455,"props":20662,"children":20663},{"style":462},[20664],{"type":26,"value":20665},"    * Create a new response containing the cached value, and return a promise\n",{"type":21,"tag":455,"props":20667,"children":20668},{"class":457,"line":333},[20669],{"type":21,"tag":455,"props":20670,"children":20671},{"style":462},[20672],{"type":26,"value":20673},"    * that resolves with this response.\n",{"type":21,"tag":455,"props":20675,"children":20676},{"class":457,"line":1424},[20677],{"type":21,"tag":455,"props":20678,"children":20679},{"style":462},[20680],{"type":26,"value":20062},{"type":21,"tag":455,"props":20682,"children":20683},{"class":457,"line":1443},[20684,20688,20692],{"type":21,"tag":455,"props":20685,"children":20686},{"style":462},[20687],{"type":26,"value":20070},{"type":21,"tag":455,"props":20689,"children":20690},{"style":1349},[20691],{"type":26,"value":17614},{"type":21,"tag":455,"props":20693,"children":20694},{"style":486},[20695],{"type":26,"value":20696}," value\n",{"type":21,"tag":455,"props":20698,"children":20699},{"class":457,"line":1489},[20700,20704,20708],{"type":21,"tag":455,"props":20701,"children":20702},{"style":462},[20703],{"type":26,"value":20070},{"type":21,"tag":455,"props":20705,"children":20706},{"style":1349},[20707],{"type":26,"value":17614},{"type":21,"tag":455,"props":20709,"children":20710},{"style":486},[20711],{"type":26,"value":20712}," dataType\n",{"type":21,"tag":455,"props":20714,"children":20715},{"class":457,"line":1506},[20716,20720,20725],{"type":21,"tag":455,"props":20717,"children":20718},{"style":462},[20719],{"type":26,"value":20070},{"type":21,"tag":455,"props":20721,"children":20722},{"style":1349},[20723],{"type":26,"value":20724},"@returns",{"type":21,"tag":455,"props":20726,"children":20727},{"style":1365},[20728],{"type":26,"value":20729}," {Promise}\n",{"type":21,"tag":455,"props":20731,"children":20732},{"class":457,"line":1547},[20733],{"type":21,"tag":455,"props":20734,"children":20735},{"style":462},[20736],{"type":26,"value":20194},{"type":21,"tag":455,"props":20738,"children":20739},{"class":457,"line":1564},[20740,20744,20749,20753,20758,20762,20767],{"type":21,"tag":455,"props":20741,"children":20742},{"style":1349},[20743],{"type":26,"value":3897},{"type":21,"tag":455,"props":20745,"children":20746},{"style":1365},[20747],{"type":26,"value":20748}," provideResponse",{"type":21,"tag":455,"props":20750,"children":20751},{"style":486},[20752],{"type":26,"value":489},{"type":21,"tag":455,"props":20754,"children":20755},{"style":4527},[20756],{"type":26,"value":20757},"value",{"type":21,"tag":455,"props":20759,"children":20760},{"style":486},[20761],{"type":26,"value":2228},{"type":21,"tag":455,"props":20763,"children":20764},{"style":4527},[20765],{"type":26,"value":20766},"dataType",{"type":21,"tag":455,"props":20768,"children":20769},{"style":486},[20770],{"type":26,"value":4879},{"type":21,"tag":455,"props":20772,"children":20773},{"class":457,"line":1605},[20774,20778,20782,20786,20790,20795],{"type":21,"tag":455,"props":20775,"children":20776},{"style":1349},[20777],{"type":26,"value":17508},{"type":21,"tag":455,"props":20779,"children":20780},{"style":486},[20781],{"type":26,"value":19634},{"type":21,"tag":455,"props":20783,"children":20784},{"style":1349},[20785],{"type":26,"value":3193},{"type":21,"tag":455,"props":20787,"children":20788},{"style":1349},[20789],{"type":26,"value":2183},{"type":21,"tag":455,"props":20791,"children":20792},{"style":1365},[20793],{"type":26,"value":20794}," Response",{"type":21,"tag":455,"props":20796,"children":20797},{"style":486},[20798],{"type":26,"value":11391},{"type":21,"tag":455,"props":20800,"children":20801},{"class":457,"line":1622},[20802],{"type":21,"tag":455,"props":20803,"children":20804},{"style":486},[20805],{"type":26,"value":20806},"        value,\n",{"type":21,"tag":455,"props":20808,"children":20809},{"class":457,"line":1663},[20810],{"type":21,"tag":455,"props":20811,"children":20812},{"style":486},[20813],{"type":26,"value":15366},{"type":21,"tag":455,"props":20815,"children":20816},{"class":457,"line":1680},[20817,20822,20826],{"type":21,"tag":455,"props":20818,"children":20819},{"style":486},[20820],{"type":26,"value":20821},"            status: ",{"type":21,"tag":455,"props":20823,"children":20824},{"style":480},[20825],{"type":26,"value":10244},{"type":21,"tag":455,"props":20827,"children":20828},{"style":486},[20829],{"type":26,"value":2483},{"type":21,"tag":455,"props":20831,"children":20832},{"class":457,"line":1721},[20833,20838,20843],{"type":21,"tag":455,"props":20834,"children":20835},{"style":486},[20836],{"type":26,"value":20837},"            statusText: ",{"type":21,"tag":455,"props":20839,"children":20840},{"style":492},[20841],{"type":26,"value":20842},"'success'",{"type":21,"tag":455,"props":20844,"children":20845},{"style":486},[20846],{"type":26,"value":2483},{"type":21,"tag":455,"props":20848,"children":20849},{"class":457,"line":1738},[20850],{"type":21,"tag":455,"props":20851,"children":20852},{"style":486},[20853],{"type":26,"value":20854},"            headers: {\n",{"type":21,"tag":455,"props":20856,"children":20857},{"class":457,"line":1779},[20858,20863],{"type":21,"tag":455,"props":20859,"children":20860},{"style":492},[20861],{"type":26,"value":20862},"                'Content-Type'",{"type":21,"tag":455,"props":20864,"children":20865},{"style":486},[20866],{"type":26,"value":20867},": dataType\n",{"type":21,"tag":455,"props":20869,"children":20870},{"class":457,"line":1796},[20871],{"type":21,"tag":455,"props":20872,"children":20873},{"style":486},[20874],{"type":26,"value":12130},{"type":21,"tag":455,"props":20876,"children":20877},{"class":457,"line":1837},[20878],{"type":21,"tag":455,"props":20879,"children":20880},{"style":486},[20881],{"type":26,"value":5680},{"type":21,"tag":455,"props":20883,"children":20884},{"class":457,"line":1854},[20885],{"type":21,"tag":455,"props":20886,"children":20887},{"style":486},[20888],{"type":26,"value":18845},{"type":21,"tag":455,"props":20890,"children":20891},{"class":457,"line":1895},[20892],{"type":21,"tag":455,"props":20893,"children":20894},{"emptyLinePlaceholder":471},[20895],{"type":26,"value":474},{"type":21,"tag":455,"props":20897,"children":20898},{"class":457,"line":1912},[20899,20903,20907,20911,20915,20919,20923,20928,20932,20937],{"type":21,"tag":455,"props":20900,"children":20901},{"style":1349},[20902],{"type":26,"value":1984},{"type":21,"tag":455,"props":20904,"children":20905},{"style":1349},[20906],{"type":26,"value":2183},{"type":21,"tag":455,"props":20908,"children":20909},{"style":480},[20910],{"type":26,"value":19053},{"type":21,"tag":455,"props":20912,"children":20913},{"style":486},[20914],{"type":26,"value":489},{"type":21,"tag":455,"props":20916,"children":20917},{"style":1349},[20918],{"type":26,"value":3897},{"type":21,"tag":455,"props":20920,"children":20921},{"style":486},[20922],{"type":26,"value":2136},{"type":21,"tag":455,"props":20924,"children":20925},{"style":4527},[20926],{"type":26,"value":20927},"resolve",{"type":21,"tag":455,"props":20929,"children":20930},{"style":486},[20931],{"type":26,"value":2228},{"type":21,"tag":455,"props":20933,"children":20934},{"style":4527},[20935],{"type":26,"value":20936},"reject",{"type":21,"tag":455,"props":20938,"children":20939},{"style":486},[20940],{"type":26,"value":4879},{"type":21,"tag":455,"props":20942,"children":20943},{"class":457,"line":1953},[20944,20949],{"type":21,"tag":455,"props":20945,"children":20946},{"style":1365},[20947],{"type":26,"value":20948},"        resolve",{"type":21,"tag":455,"props":20950,"children":20951},{"style":486},[20952],{"type":26,"value":20953},"(response);\n",{"type":21,"tag":455,"props":20955,"children":20956},{"class":457,"line":1970},[20957],{"type":21,"tag":455,"props":20958,"children":20959},{"style":486},[20960],{"type":26,"value":20598},{"type":21,"tag":455,"props":20962,"children":20963},{"class":457,"line":1978},[20964],{"type":21,"tag":455,"props":20965,"children":20966},{"style":486},[20967],{"type":26,"value":2002},{"type":21,"tag":22,"props":20969,"children":20970},{},[20971,20973,20979],{"type":26,"value":20972},"Here's the last really interesting bit, and it demonstrates the advantage of having access to the ",{"type":21,"tag":246,"props":20974,"children":20976},{"className":20975},[],[20977],{"type":26,"value":20978},"Response",{"type":26,"value":20980}," primitive - we can create our own streamable response by simply feeding in the value to the Response constructor, and return a new Promise as the fetch API expects immediately resolving with our response.",{"type":21,"tag":80,"props":20982,"children":20984},{"id":20983},"all-together-now",[20985],{"type":26,"value":20986},"All Together Now",{"type":21,"tag":22,"props":20988,"children":20989},{},[20990,20992,20998],{"type":26,"value":20991},"Here's the full code as of the time of this writing, and I recommend taking a look at the ",{"type":21,"tag":322,"props":20993,"children":20995},{"href":19885,"rel":20994},[326],[20996],{"type":26,"value":20997},"github repo",{"type":26,"value":20999}," for the up-to-date version. As always, let me know if you have any questions in the comments.",{"type":21,"tag":239,"props":21001,"children":21003},{"className":20016,"code":21002,"language":20018,"meta":8,"style":8},"/**\n * Copyright (c) Christopher Keefer, 2016.\n * https://github.com/SaneMethod/fetchCache\n *\n * Override fetch in the global context to allow us to cache the response to fetch in a Storage interface\n * implementing object (such as localStorage).\n */\n(function (fetch) {\n    /* If the context doesn't support fetch, we won't attempt to patch in our\n     caching using fetch, for obvious reasons. */\n    if (!fetch) return;\n\n    /**\n     * Generate the cache key under which to store the local data - either the cache key supplied,\n     * or one generated from the url, the Content-type header (if specified) and the body (if specified).\n     *\n     * @returns {string}\n     */\n    function genCacheKey(url, settings) {\n        var {headers:{'Content-type': type}} = ('headers' in settings) ? settings : {headers: {}},\n            {body} = settings;\n\n        return settings.cacheKey || url + (type || '') + (body || '');\n    }\n\n    /**\n     * Determine whether we're using localStorage or, if the user has specified something other than a boolean\n     * value for options.localCache, whether the value appears to satisfy the plugin's requirements.\n     * Otherwise, throw a new TypeError indicating what type of value we expect.\n     *\n     * @param {boolean|object} storage\n     * @returns {boolean|object}\n     */\n    function getStorage(storage) {\n        if (!storage) return false;\n        if (storage === true) return self.localStorage;\n        if (typeof storage === \"object\" && 'getItem' in storage &&\n            'removeItem' in storage && 'setItem' in storage) {\n            return storage;\n        }\n        throw new TypeError(\"localCache must either be a boolean value, \" +\n            \"or an object which implements the Storage interface.\");\n    }\n\n    /**\n     * Remove the item specified by cacheKey and its attendant meta items from storage.\n     *\n     * @param {Storage|object} storage\n     * @param {string} cacheKey\n     */\n    function removeFromStorage(storage, cacheKey) {\n        storage.removeItem(cacheKey);\n        storage.removeItem(cacheKey + 'cachettl');\n        storage.removeItem(cacheKey + 'dataType');\n    }\n\n    /**\n     * Cache the response into our storage object.\n     * We clone the response so that we can drain the stream without making it\n     * unavailable to future handlers.\n     *\n     * @param {string} cacheKey Key under which to cache the data string. Bound in\n     * fetch override.\n     * @param {Storage} storage Object implementing Storage interface to store cached data\n     * (text or json exclusively) in. Bound in fetch override.\n     * @param {Number} hourstl Number of hours this value shoud remain in the cache.\n     * Bound in fetch override.\n     * @param {Response} response\n     */\n    function cacheResponse(cacheKey, storage, hourstl, response) {\n        var cres = response.clone(),\n            dataType = (response.headers.get('Content-Type') || 'text/plain').toLowerCase();\n\n        cres.text().then((text) => {\n            try {\n                storage.setItem(cacheKey, text);\n                storage.setItem(cacheKey + 'cachettl', +new Date() + 1000 * 60 * 60 * hourstl);\n                storage.setItem(cacheKey + 'dataType', dataType);\n            } catch (e) {\n                // Remove any incomplete data that may have been saved before the exception was caught\n                removeFromStorage(storage, cacheKey);\n                console.log('Cache Error: ' + e, cacheKey, text);\n            }\n        });\n\n        return response;\n    }\n\n    /**\n     * Create a new response containing the cached value, and return a promise\n     * that resolves with this response.\n     *\n     * @param value\n     * @param dataType\n     * @returns {Promise}\n     */\n    function provideResponse(value, dataType) {\n        var response = new Response(\n            value,\n            {\n                status: 200,\n                statusText: 'success',\n                headers: {\n                    'Content-Type': dataType\n                }\n            }\n        );\n\n        return new Promise(function (resolve, reject) {\n            resolve(response);\n        });\n    }\n\n    /**\n     * Override fetch on the global context, so that we can intercept\n     * fetch calls and respond with locally cached content, if available.\n     * New parameters available on the call to fetch:\n     * localCache   : true // required - either a boolean (if true, localStorage is used,\n     * if false request is not cached or returned from cache), or an object implementing the\n     * Storage interface, in which case that object is used instead.\n     * cacheTTL     : 5, // optional, cache time in hours, default is 5. Use float numbers for\n     * values less than a full hour (e.g. 0.5 for 1/2 hour).\n     * cacheKey     : 'post', // optional - key under which cached string will be stored.\n     * isCacheValid : function  // optional - return true for valid, false for invalid.\n     */\n    self.fetch = function (url, settings) {\n        var storage = getStorage(settings.localCache),\n            hourstl = settings.cacheTTL || 5,\n            cacheKey = genCacheKey(url, settings),\n            cacheValid = settings.isCacheValid,\n            ttl,\n            value,\n            dataType;\n\n        if (!storage) return fetch(url, settings);\n\n        ttl = storage.getItem(cacheKey + 'cachettl');\n\n        if (cacheValid && typeof cacheValid === 'function' && !cacheValid()) {\n            removeFromStorage(storage, cacheKey);\n            ttl = 0;\n        }\n\n        if (ttl && ttl \u003C +new Date()) {\n            removeFromStorage(storage, cacheKey);\n        }\n\n        value = storage.getItem(cacheKey);\n\n        if (!value) {\n            /* If not cached, we'll make the request and add a then block to the resulting promise,\n             in which we'll cache the result. */\n            return fetch(url, settings).then(cacheResponse.bind(null, cacheKey, storage, hourstl));\n        }\n\n        /* Value is cached, so we'll simply create and respond with a promise of our own,\n         and provide a response object. */\n        dataType = storage.getItem(cacheKey + 'dataType') || 'text/plain';\n        return provideResponse(value, dataType);\n    };\n})(self.fetch);\n",[21004],{"type":21,"tag":246,"props":21005,"children":21006},{"__ignoreMap":8},[21007,21014,21022,21030,21038,21046,21054,21061,21084,21092,21100,21128,21135,21142,21150,21158,21166,21182,21189,21223,21295,21312,21319,21383,21390,21397,21404,21412,21420,21428,21435,21456,21472,21479,21503,21536,21571,21622,21656,21668,21675,21706,21718,21725,21732,21739,21747,21754,21774,21794,21801,21833,21851,21878,21905,21912,21919,21926,21934,21942,21950,21957,21980,21988,22011,22019,22042,22050,22069,22076,22123,22150,22202,22209,22249,22261,22277,22348,22375,22391,22399,22411,22439,22446,22453,22460,22471,22478,22485,22492,22500,22508,22515,22530,22545,22560,22567,22599,22627,22636,22645,22662,22679,22688,22701,22709,22717,22726,22734,22778,22791,22799,22807,22815,22823,22832,22841,22850,22864,22873,22882,22896,22905,22919,22933,22941,22982,23007,23038,23060,23078,23087,23095,23104,23112,23145,23153,23192,23200,23255,23267,23288,23296,23304,23343,23355,23363,23371,23396,23404,23425,23434,23443,23488,23496,23504,23513,23522,23570,23587,23595],{"type":21,"tag":455,"props":21008,"children":21009},{"class":457,"line":458},[21010],{"type":21,"tag":455,"props":21011,"children":21012},{"style":462},[21013],{"type":26,"value":17461},{"type":21,"tag":455,"props":21015,"children":21016},{"class":457,"line":336},[21017],{"type":21,"tag":455,"props":21018,"children":21019},{"style":462},[21020],{"type":26,"value":21021}," * Copyright (c) Christopher Keefer, 2016.\n",{"type":21,"tag":455,"props":21023,"children":21024},{"class":457,"line":333},[21025],{"type":21,"tag":455,"props":21026,"children":21027},{"style":462},[21028],{"type":26,"value":21029}," * https://github.com/SaneMethod/fetchCache\n",{"type":21,"tag":455,"props":21031,"children":21032},{"class":457,"line":1424},[21033],{"type":21,"tag":455,"props":21034,"children":21035},{"style":462},[21036],{"type":26,"value":21037}," *\n",{"type":21,"tag":455,"props":21039,"children":21040},{"class":457,"line":1443},[21041],{"type":21,"tag":455,"props":21042,"children":21043},{"style":462},[21044],{"type":26,"value":21045}," * Override fetch in the global context to allow us to cache the response to fetch in a Storage interface\n",{"type":21,"tag":455,"props":21047,"children":21048},{"class":457,"line":1489},[21049],{"type":21,"tag":455,"props":21050,"children":21051},{"style":462},[21052],{"type":26,"value":21053}," * implementing object (such as localStorage).\n",{"type":21,"tag":455,"props":21055,"children":21056},{"class":457,"line":1506},[21057],{"type":21,"tag":455,"props":21058,"children":21059},{"style":462},[21060],{"type":26,"value":14425},{"type":21,"tag":455,"props":21062,"children":21063},{"class":457,"line":1547},[21064,21068,21072,21076,21080],{"type":21,"tag":455,"props":21065,"children":21066},{"style":486},[21067],{"type":26,"value":489},{"type":21,"tag":455,"props":21069,"children":21070},{"style":1349},[21071],{"type":26,"value":3897},{"type":21,"tag":455,"props":21073,"children":21074},{"style":486},[21075],{"type":26,"value":2136},{"type":21,"tag":455,"props":21077,"children":21078},{"style":4527},[21079],{"type":26,"value":19758},{"type":21,"tag":455,"props":21081,"children":21082},{"style":486},[21083],{"type":26,"value":4879},{"type":21,"tag":455,"props":21085,"children":21086},{"class":457,"line":1564},[21087],{"type":21,"tag":455,"props":21088,"children":21089},{"style":462},[21090],{"type":26,"value":21091},"    /* If the context doesn't support fetch, we won't attempt to patch in our\n",{"type":21,"tag":455,"props":21093,"children":21094},{"class":457,"line":1605},[21095],{"type":21,"tag":455,"props":21096,"children":21097},{"style":462},[21098],{"type":26,"value":21099},"     caching using fetch, for obvious reasons. */\n",{"type":21,"tag":455,"props":21101,"children":21102},{"class":457,"line":1622},[21103,21107,21111,21115,21120,21124],{"type":21,"tag":455,"props":21104,"children":21105},{"style":1349},[21106],{"type":26,"value":1402},{"type":21,"tag":455,"props":21108,"children":21109},{"style":486},[21110],{"type":26,"value":2136},{"type":21,"tag":455,"props":21112,"children":21113},{"style":1349},[21114],{"type":26,"value":1272},{"type":21,"tag":455,"props":21116,"children":21117},{"style":486},[21118],{"type":26,"value":21119},"fetch) ",{"type":21,"tag":455,"props":21121,"children":21122},{"style":1349},[21123],{"type":26,"value":260},{"type":21,"tag":455,"props":21125,"children":21126},{"style":486},[21127],{"type":26,"value":1440},{"type":21,"tag":455,"props":21129,"children":21130},{"class":457,"line":1663},[21131],{"type":21,"tag":455,"props":21132,"children":21133},{"emptyLinePlaceholder":471},[21134],{"type":26,"value":474},{"type":21,"tag":455,"props":21136,"children":21137},{"class":457,"line":1680},[21138],{"type":21,"tag":455,"props":21139,"children":21140},{"style":462},[21141],{"type":26,"value":17593},{"type":21,"tag":455,"props":21143,"children":21144},{"class":457,"line":1721},[21145],{"type":21,"tag":455,"props":21146,"children":21147},{"style":462},[21148],{"type":26,"value":21149},"     * Generate the cache key under which to store the local data - either the cache key supplied,\n",{"type":21,"tag":455,"props":21151,"children":21152},{"class":457,"line":1738},[21153],{"type":21,"tag":455,"props":21154,"children":21155},{"style":462},[21156],{"type":26,"value":21157},"     * or one generated from the url, the Content-type header (if specified) and the body (if specified).\n",{"type":21,"tag":455,"props":21159,"children":21160},{"class":457,"line":1779},[21161],{"type":21,"tag":455,"props":21162,"children":21163},{"style":462},[21164],{"type":26,"value":21165},"     *\n",{"type":21,"tag":455,"props":21167,"children":21168},{"class":457,"line":1796},[21169,21173,21177],{"type":21,"tag":455,"props":21170,"children":21171},{"style":462},[21172],{"type":26,"value":17609},{"type":21,"tag":455,"props":21174,"children":21175},{"style":1349},[21176],{"type":26,"value":20724},{"type":21,"tag":455,"props":21178,"children":21179},{"style":1365},[21180],{"type":26,"value":21181}," {string}\n",{"type":21,"tag":455,"props":21183,"children":21184},{"class":457,"line":1837},[21185],{"type":21,"tag":455,"props":21186,"children":21187},{"style":462},[21188],{"type":26,"value":11610},{"type":21,"tag":455,"props":21190,"children":21191},{"class":457,"line":1854},[21192,21196,21201,21205,21210,21214,21219],{"type":21,"tag":455,"props":21193,"children":21194},{"style":1349},[21195],{"type":26,"value":17639},{"type":21,"tag":455,"props":21197,"children":21198},{"style":1365},[21199],{"type":26,"value":21200}," genCacheKey",{"type":21,"tag":455,"props":21202,"children":21203},{"style":486},[21204],{"type":26,"value":489},{"type":21,"tag":455,"props":21206,"children":21207},{"style":4527},[21208],{"type":26,"value":21209},"url",{"type":21,"tag":455,"props":21211,"children":21212},{"style":486},[21213],{"type":26,"value":2228},{"type":21,"tag":455,"props":21215,"children":21216},{"style":4527},[21217],{"type":26,"value":21218},"settings",{"type":21,"tag":455,"props":21220,"children":21221},{"style":486},[21222],{"type":26,"value":4879},{"type":21,"tag":455,"props":21224,"children":21225},{"class":457,"line":1895},[21226,21231,21235,21240,21245,21250,21255,21259,21263,21268,21272,21277,21281,21286,21290],{"type":21,"tag":455,"props":21227,"children":21228},{"style":1349},[21229],{"type":26,"value":21230},"        var",{"type":21,"tag":455,"props":21232,"children":21233},{"style":486},[21234],{"type":26,"value":7508},{"type":21,"tag":455,"props":21236,"children":21237},{"style":4527},[21238],{"type":26,"value":21239},"headers",{"type":21,"tag":455,"props":21241,"children":21242},{"style":486},[21243],{"type":26,"value":21244},":{",{"type":21,"tag":455,"props":21246,"children":21247},{"style":492},[21248],{"type":26,"value":21249},"'Content-type'",{"type":21,"tag":455,"props":21251,"children":21252},{"style":486},[21253],{"type":26,"value":21254},": type}} ",{"type":21,"tag":455,"props":21256,"children":21257},{"style":1349},[21258],{"type":26,"value":3193},{"type":21,"tag":455,"props":21260,"children":21261},{"style":486},[21262],{"type":26,"value":2136},{"type":21,"tag":455,"props":21264,"children":21265},{"style":492},[21266],{"type":26,"value":21267},"'headers'",{"type":21,"tag":455,"props":21269,"children":21270},{"style":1349},[21271],{"type":26,"value":8476},{"type":21,"tag":455,"props":21273,"children":21274},{"style":486},[21275],{"type":26,"value":21276}," settings) ",{"type":21,"tag":455,"props":21278,"children":21279},{"style":1349},[21280],{"type":26,"value":1116},{"type":21,"tag":455,"props":21282,"children":21283},{"style":486},[21284],{"type":26,"value":21285}," settings ",{"type":21,"tag":455,"props":21287,"children":21288},{"style":1349},[21289],{"type":26,"value":831},{"type":21,"tag":455,"props":21291,"children":21292},{"style":486},[21293],{"type":26,"value":21294}," {headers: {}},\n",{"type":21,"tag":455,"props":21296,"children":21297},{"class":457,"line":1912},[21298,21303,21307],{"type":21,"tag":455,"props":21299,"children":21300},{"style":486},[21301],{"type":26,"value":21302},"            {body} ",{"type":21,"tag":455,"props":21304,"children":21305},{"style":1349},[21306],{"type":26,"value":3193},{"type":21,"tag":455,"props":21308,"children":21309},{"style":486},[21310],{"type":26,"value":21311}," settings;\n",{"type":21,"tag":455,"props":21313,"children":21314},{"class":457,"line":1953},[21315],{"type":21,"tag":455,"props":21316,"children":21317},{"emptyLinePlaceholder":471},[21318],{"type":26,"value":474},{"type":21,"tag":455,"props":21320,"children":21321},{"class":457,"line":1970},[21322,21326,21331,21335,21340,21344,21349,21353,21358,21362,21366,21371,21375,21379],{"type":21,"tag":455,"props":21323,"children":21324},{"style":1349},[21325],{"type":26,"value":1430},{"type":21,"tag":455,"props":21327,"children":21328},{"style":486},[21329],{"type":26,"value":21330}," settings.cacheKey ",{"type":21,"tag":455,"props":21332,"children":21333},{"style":1349},[21334],{"type":26,"value":12183},{"type":21,"tag":455,"props":21336,"children":21337},{"style":486},[21338],{"type":26,"value":21339}," url ",{"type":21,"tag":455,"props":21341,"children":21342},{"style":1349},[21343],{"type":26,"value":2206},{"type":21,"tag":455,"props":21345,"children":21346},{"style":486},[21347],{"type":26,"value":21348}," (type ",{"type":21,"tag":455,"props":21350,"children":21351},{"style":1349},[21352],{"type":26,"value":12183},{"type":21,"tag":455,"props":21354,"children":21355},{"style":492},[21356],{"type":26,"value":21357}," ''",{"type":21,"tag":455,"props":21359,"children":21360},{"style":486},[21361],{"type":26,"value":4584},{"type":21,"tag":455,"props":21363,"children":21364},{"style":1349},[21365],{"type":26,"value":2206},{"type":21,"tag":455,"props":21367,"children":21368},{"style":486},[21369],{"type":26,"value":21370}," (body ",{"type":21,"tag":455,"props":21372,"children":21373},{"style":1349},[21374],{"type":26,"value":12183},{"type":21,"tag":455,"props":21376,"children":21377},{"style":492},[21378],{"type":26,"value":21357},{"type":21,"tag":455,"props":21380,"children":21381},{"style":486},[21382],{"type":26,"value":2171},{"type":21,"tag":455,"props":21384,"children":21385},{"class":457,"line":1978},[21386],{"type":21,"tag":455,"props":21387,"children":21388},{"style":486},[21389],{"type":26,"value":6008},{"type":21,"tag":455,"props":21391,"children":21392},{"class":457,"line":1996},[21393],{"type":21,"tag":455,"props":21394,"children":21395},{"emptyLinePlaceholder":471},[21396],{"type":26,"value":474},{"type":21,"tag":455,"props":21398,"children":21399},{"class":457,"line":4487},[21400],{"type":21,"tag":455,"props":21401,"children":21402},{"style":462},[21403],{"type":26,"value":17593},{"type":21,"tag":455,"props":21405,"children":21406},{"class":457,"line":4495},[21407],{"type":21,"tag":455,"props":21408,"children":21409},{"style":462},[21410],{"type":26,"value":21411},"     * Determine whether we're using localStorage or, if the user has specified something other than a boolean\n",{"type":21,"tag":455,"props":21413,"children":21414},{"class":457,"line":4509},[21415],{"type":21,"tag":455,"props":21416,"children":21417},{"style":462},[21418],{"type":26,"value":21419},"     * value for options.localCache, whether the value appears to satisfy the plugin's requirements.\n",{"type":21,"tag":455,"props":21421,"children":21422},{"class":457,"line":4561},[21423],{"type":21,"tag":455,"props":21424,"children":21425},{"style":462},[21426],{"type":26,"value":21427},"     * Otherwise, throw a new TypeError indicating what type of value we expect.\n",{"type":21,"tag":455,"props":21429,"children":21430},{"class":457,"line":4596},[21431],{"type":21,"tag":455,"props":21432,"children":21433},{"style":462},[21434],{"type":26,"value":21165},{"type":21,"tag":455,"props":21436,"children":21437},{"class":457,"line":4627},[21438,21442,21446,21451],{"type":21,"tag":455,"props":21439,"children":21440},{"style":462},[21441],{"type":26,"value":17609},{"type":21,"tag":455,"props":21443,"children":21444},{"style":1349},[21445],{"type":26,"value":17614},{"type":21,"tag":455,"props":21447,"children":21448},{"style":1365},[21449],{"type":26,"value":21450}," {boolean|object}",{"type":21,"tag":455,"props":21452,"children":21453},{"style":486},[21454],{"type":26,"value":21455}," storage\n",{"type":21,"tag":455,"props":21457,"children":21458},{"class":457,"line":5942},[21459,21463,21467],{"type":21,"tag":455,"props":21460,"children":21461},{"style":462},[21462],{"type":26,"value":17609},{"type":21,"tag":455,"props":21464,"children":21465},{"style":1349},[21466],{"type":26,"value":20724},{"type":21,"tag":455,"props":21468,"children":21469},{"style":1365},[21470],{"type":26,"value":21471}," {boolean|object}\n",{"type":21,"tag":455,"props":21473,"children":21474},{"class":457,"line":5965},[21475],{"type":21,"tag":455,"props":21476,"children":21477},{"style":462},[21478],{"type":26,"value":11610},{"type":21,"tag":455,"props":21480,"children":21481},{"class":457,"line":5983},[21482,21486,21491,21495,21499],{"type":21,"tag":455,"props":21483,"children":21484},{"style":1349},[21485],{"type":26,"value":17639},{"type":21,"tag":455,"props":21487,"children":21488},{"style":1365},[21489],{"type":26,"value":21490}," getStorage",{"type":21,"tag":455,"props":21492,"children":21493},{"style":486},[21494],{"type":26,"value":489},{"type":21,"tag":455,"props":21496,"children":21497},{"style":4527},[21498],{"type":26,"value":20224},{"type":21,"tag":455,"props":21500,"children":21501},{"style":486},[21502],{"type":26,"value":4879},{"type":21,"tag":455,"props":21504,"children":21505},{"class":457,"line":6002},[21506,21510,21514,21518,21523,21527,21532],{"type":21,"tag":455,"props":21507,"children":21508},{"style":1349},[21509],{"type":26,"value":5627},{"type":21,"tag":455,"props":21511,"children":21512},{"style":486},[21513],{"type":26,"value":2136},{"type":21,"tag":455,"props":21515,"children":21516},{"style":1349},[21517],{"type":26,"value":1272},{"type":21,"tag":455,"props":21519,"children":21520},{"style":486},[21521],{"type":26,"value":21522},"storage) ",{"type":21,"tag":455,"props":21524,"children":21525},{"style":1349},[21526],{"type":26,"value":260},{"type":21,"tag":455,"props":21528,"children":21529},{"style":480},[21530],{"type":26,"value":21531}," false",{"type":21,"tag":455,"props":21533,"children":21534},{"style":486},[21535],{"type":26,"value":1440},{"type":21,"tag":455,"props":21537,"children":21538},{"class":457,"line":6011},[21539,21543,21548,21553,21558,21562,21566],{"type":21,"tag":455,"props":21540,"children":21541},{"style":1349},[21542],{"type":26,"value":5627},{"type":21,"tag":455,"props":21544,"children":21545},{"style":486},[21546],{"type":26,"value":21547}," (storage ",{"type":21,"tag":455,"props":21549,"children":21550},{"style":1349},[21551],{"type":26,"value":21552},"===",{"type":21,"tag":455,"props":21554,"children":21555},{"style":480},[21556],{"type":26,"value":21557}," true",{"type":21,"tag":455,"props":21559,"children":21560},{"style":486},[21561],{"type":26,"value":4584},{"type":21,"tag":455,"props":21563,"children":21564},{"style":1349},[21565],{"type":26,"value":260},{"type":21,"tag":455,"props":21567,"children":21568},{"style":486},[21569],{"type":26,"value":21570}," self.localStorage;\n",{"type":21,"tag":455,"props":21572,"children":21573},{"class":457,"line":6019},[21574,21578,21582,21586,21591,21595,21600,21604,21609,21613,21617],{"type":21,"tag":455,"props":21575,"children":21576},{"style":1349},[21577],{"type":26,"value":5627},{"type":21,"tag":455,"props":21579,"children":21580},{"style":486},[21581],{"type":26,"value":2136},{"type":21,"tag":455,"props":21583,"children":21584},{"style":1349},[21585],{"type":26,"value":7343},{"type":21,"tag":455,"props":21587,"children":21588},{"style":486},[21589],{"type":26,"value":21590}," storage ",{"type":21,"tag":455,"props":21592,"children":21593},{"style":1349},[21594],{"type":26,"value":21552},{"type":21,"tag":455,"props":21596,"children":21597},{"style":492},[21598],{"type":26,"value":21599}," \"object\"",{"type":21,"tag":455,"props":21601,"children":21602},{"style":1349},[21603],{"type":26,"value":1467},{"type":21,"tag":455,"props":21605,"children":21606},{"style":492},[21607],{"type":26,"value":21608}," 'getItem'",{"type":21,"tag":455,"props":21610,"children":21611},{"style":1349},[21612],{"type":26,"value":8476},{"type":21,"tag":455,"props":21614,"children":21615},{"style":486},[21616],{"type":26,"value":21590},{"type":21,"tag":455,"props":21618,"children":21619},{"style":1349},[21620],{"type":26,"value":21621},"&&\n",{"type":21,"tag":455,"props":21623,"children":21624},{"class":457,"line":6028},[21625,21630,21634,21638,21642,21647,21651],{"type":21,"tag":455,"props":21626,"children":21627},{"style":492},[21628],{"type":26,"value":21629},"            'removeItem'",{"type":21,"tag":455,"props":21631,"children":21632},{"style":1349},[21633],{"type":26,"value":8476},{"type":21,"tag":455,"props":21635,"children":21636},{"style":486},[21637],{"type":26,"value":21590},{"type":21,"tag":455,"props":21639,"children":21640},{"style":1349},[21641],{"type":26,"value":11637},{"type":21,"tag":455,"props":21643,"children":21644},{"style":492},[21645],{"type":26,"value":21646}," 'setItem'",{"type":21,"tag":455,"props":21648,"children":21649},{"style":1349},[21650],{"type":26,"value":8476},{"type":21,"tag":455,"props":21652,"children":21653},{"style":486},[21654],{"type":26,"value":21655}," storage) {\n",{"type":21,"tag":455,"props":21657,"children":21658},{"class":457,"line":6085},[21659,21663],{"type":21,"tag":455,"props":21660,"children":21661},{"style":1349},[21662],{"type":26,"value":17196},{"type":21,"tag":455,"props":21664,"children":21665},{"style":486},[21666],{"type":26,"value":21667}," storage;\n",{"type":21,"tag":455,"props":21669,"children":21670},{"class":457,"line":6097},[21671],{"type":21,"tag":455,"props":21672,"children":21673},{"style":486},[21674],{"type":26,"value":5680},{"type":21,"tag":455,"props":21676,"children":21677},{"class":457,"line":6117},[21678,21683,21687,21692,21696,21701],{"type":21,"tag":455,"props":21679,"children":21680},{"style":1349},[21681],{"type":26,"value":21682},"        throw",{"type":21,"tag":455,"props":21684,"children":21685},{"style":1349},[21686],{"type":26,"value":2183},{"type":21,"tag":455,"props":21688,"children":21689},{"style":1365},[21690],{"type":26,"value":21691}," TypeError",{"type":21,"tag":455,"props":21693,"children":21694},{"style":486},[21695],{"type":26,"value":489},{"type":21,"tag":455,"props":21697,"children":21698},{"style":492},[21699],{"type":26,"value":21700},"\"localCache must either be a boolean value, \"",{"type":21,"tag":455,"props":21702,"children":21703},{"style":1349},[21704],{"type":26,"value":21705}," +\n",{"type":21,"tag":455,"props":21707,"children":21708},{"class":457,"line":6134},[21709,21714],{"type":21,"tag":455,"props":21710,"children":21711},{"style":492},[21712],{"type":26,"value":21713},"            \"or an object which implements the Storage interface.\"",{"type":21,"tag":455,"props":21715,"children":21716},{"style":486},[21717],{"type":26,"value":2171},{"type":21,"tag":455,"props":21719,"children":21720},{"class":457,"line":6142},[21721],{"type":21,"tag":455,"props":21722,"children":21723},{"style":486},[21724],{"type":26,"value":6008},{"type":21,"tag":455,"props":21726,"children":21727},{"class":457,"line":10092},[21728],{"type":21,"tag":455,"props":21729,"children":21730},{"emptyLinePlaceholder":471},[21731],{"type":26,"value":474},{"type":21,"tag":455,"props":21733,"children":21734},{"class":457,"line":10100},[21735],{"type":21,"tag":455,"props":21736,"children":21737},{"style":462},[21738],{"type":26,"value":17593},{"type":21,"tag":455,"props":21740,"children":21741},{"class":457,"line":10109},[21742],{"type":21,"tag":455,"props":21743,"children":21744},{"style":462},[21745],{"type":26,"value":21746},"     * Remove the item specified by cacheKey and its attendant meta items from storage.\n",{"type":21,"tag":455,"props":21748,"children":21749},{"class":457,"line":10118},[21750],{"type":21,"tag":455,"props":21751,"children":21752},{"style":462},[21753],{"type":26,"value":21165},{"type":21,"tag":455,"props":21755,"children":21756},{"class":457,"line":10136},[21757,21761,21765,21770],{"type":21,"tag":455,"props":21758,"children":21759},{"style":462},[21760],{"type":26,"value":17609},{"type":21,"tag":455,"props":21762,"children":21763},{"style":1349},[21764],{"type":26,"value":17614},{"type":21,"tag":455,"props":21766,"children":21767},{"style":1365},[21768],{"type":26,"value":21769}," {Storage|object}",{"type":21,"tag":455,"props":21771,"children":21772},{"style":486},[21773],{"type":26,"value":21455},{"type":21,"tag":455,"props":21775,"children":21776},{"class":457,"line":10154},[21777,21781,21785,21789],{"type":21,"tag":455,"props":21778,"children":21779},{"style":462},[21780],{"type":26,"value":17609},{"type":21,"tag":455,"props":21782,"children":21783},{"style":1349},[21784],{"type":26,"value":17614},{"type":21,"tag":455,"props":21786,"children":21787},{"style":1365},[21788],{"type":26,"value":20079},{"type":21,"tag":455,"props":21790,"children":21791},{"style":486},[21792],{"type":26,"value":21793}," cacheKey\n",{"type":21,"tag":455,"props":21795,"children":21796},{"class":457,"line":10172},[21797],{"type":21,"tag":455,"props":21798,"children":21799},{"style":462},[21800],{"type":26,"value":11610},{"type":21,"tag":455,"props":21802,"children":21803},{"class":457,"line":10180},[21804,21808,21813,21817,21821,21825,21829],{"type":21,"tag":455,"props":21805,"children":21806},{"style":1349},[21807],{"type":26,"value":17639},{"type":21,"tag":455,"props":21809,"children":21810},{"style":1365},[21811],{"type":26,"value":21812}," removeFromStorage",{"type":21,"tag":455,"props":21814,"children":21815},{"style":486},[21816],{"type":26,"value":489},{"type":21,"tag":455,"props":21818,"children":21819},{"style":4527},[21820],{"type":26,"value":20224},{"type":21,"tag":455,"props":21822,"children":21823},{"style":486},[21824],{"type":26,"value":2228},{"type":21,"tag":455,"props":21826,"children":21827},{"style":4527},[21828],{"type":26,"value":20215},{"type":21,"tag":455,"props":21830,"children":21831},{"style":486},[21832],{"type":26,"value":4879},{"type":21,"tag":455,"props":21834,"children":21835},{"class":457,"line":10188},[21836,21841,21846],{"type":21,"tag":455,"props":21837,"children":21838},{"style":486},[21839],{"type":26,"value":21840},"        storage.",{"type":21,"tag":455,"props":21842,"children":21843},{"style":1365},[21844],{"type":26,"value":21845},"removeItem",{"type":21,"tag":455,"props":21847,"children":21848},{"style":486},[21849],{"type":26,"value":21850},"(cacheKey);\n",{"type":21,"tag":455,"props":21852,"children":21853},{"class":457,"line":10197},[21854,21858,21862,21866,21870,21874],{"type":21,"tag":455,"props":21855,"children":21856},{"style":486},[21857],{"type":26,"value":21840},{"type":21,"tag":455,"props":21859,"children":21860},{"style":1365},[21861],{"type":26,"value":21845},{"type":21,"tag":455,"props":21863,"children":21864},{"style":486},[21865],{"type":26,"value":20424},{"type":21,"tag":455,"props":21867,"children":21868},{"style":1349},[21869],{"type":26,"value":2206},{"type":21,"tag":455,"props":21871,"children":21872},{"style":492},[21873],{"type":26,"value":20433},{"type":21,"tag":455,"props":21875,"children":21876},{"style":486},[21877],{"type":26,"value":2171},{"type":21,"tag":455,"props":21879,"children":21880},{"class":457,"line":10205},[21881,21885,21889,21893,21897,21901],{"type":21,"tag":455,"props":21882,"children":21883},{"style":486},[21884],{"type":26,"value":21840},{"type":21,"tag":455,"props":21886,"children":21887},{"style":1365},[21888],{"type":26,"value":21845},{"type":21,"tag":455,"props":21890,"children":21891},{"style":486},[21892],{"type":26,"value":20424},{"type":21,"tag":455,"props":21894,"children":21895},{"style":1349},[21896],{"type":26,"value":2206},{"type":21,"tag":455,"props":21898,"children":21899},{"style":492},[21900],{"type":26,"value":20511},{"type":21,"tag":455,"props":21902,"children":21903},{"style":486},[21904],{"type":26,"value":2171},{"type":21,"tag":455,"props":21906,"children":21907},{"class":457,"line":10214},[21908],{"type":21,"tag":455,"props":21909,"children":21910},{"style":486},[21911],{"type":26,"value":6008},{"type":21,"tag":455,"props":21913,"children":21914},{"class":457,"line":10223},[21915],{"type":21,"tag":455,"props":21916,"children":21917},{"emptyLinePlaceholder":471},[21918],{"type":26,"value":474},{"type":21,"tag":455,"props":21920,"children":21921},{"class":457,"line":10260},[21922],{"type":21,"tag":455,"props":21923,"children":21924},{"style":462},[21925],{"type":26,"value":17593},{"type":21,"tag":455,"props":21927,"children":21928},{"class":457,"line":10290},[21929],{"type":21,"tag":455,"props":21930,"children":21931},{"style":462},[21932],{"type":26,"value":21933},"     * Cache the response into our storage object.\n",{"type":21,"tag":455,"props":21935,"children":21936},{"class":457,"line":10326},[21937],{"type":21,"tag":455,"props":21938,"children":21939},{"style":462},[21940],{"type":26,"value":21941},"     * We clone the response so that we can drain the stream without making it\n",{"type":21,"tag":455,"props":21943,"children":21944},{"class":457,"line":10361},[21945],{"type":21,"tag":455,"props":21946,"children":21947},{"style":462},[21948],{"type":26,"value":21949},"     * unavailable to future handlers.\n",{"type":21,"tag":455,"props":21951,"children":21952},{"class":457,"line":10395},[21953],{"type":21,"tag":455,"props":21954,"children":21955},{"style":462},[21956],{"type":26,"value":21165},{"type":21,"tag":455,"props":21958,"children":21959},{"class":457,"line":10429},[21960,21964,21968,21972,21976],{"type":21,"tag":455,"props":21961,"children":21962},{"style":462},[21963],{"type":26,"value":17609},{"type":21,"tag":455,"props":21965,"children":21966},{"style":1349},[21967],{"type":26,"value":17614},{"type":21,"tag":455,"props":21969,"children":21970},{"style":1365},[21971],{"type":26,"value":20079},{"type":21,"tag":455,"props":21973,"children":21974},{"style":486},[21975],{"type":26,"value":20084},{"type":21,"tag":455,"props":21977,"children":21978},{"style":462},[21979],{"type":26,"value":20089},{"type":21,"tag":455,"props":21981,"children":21982},{"class":457,"line":10437},[21983],{"type":21,"tag":455,"props":21984,"children":21985},{"style":462},[21986],{"type":26,"value":21987},"     * fetch override.\n",{"type":21,"tag":455,"props":21989,"children":21990},{"class":457,"line":12797},[21991,21995,21999,22003,22007],{"type":21,"tag":455,"props":21992,"children":21993},{"style":462},[21994],{"type":26,"value":17609},{"type":21,"tag":455,"props":21996,"children":21997},{"style":1349},[21998],{"type":26,"value":17614},{"type":21,"tag":455,"props":22000,"children":22001},{"style":1365},[22002],{"type":26,"value":20113},{"type":21,"tag":455,"props":22004,"children":22005},{"style":486},[22006],{"type":26,"value":20118},{"type":21,"tag":455,"props":22008,"children":22009},{"style":462},[22010],{"type":26,"value":20123},{"type":21,"tag":455,"props":22012,"children":22013},{"class":457,"line":12839},[22014],{"type":21,"tag":455,"props":22015,"children":22016},{"style":462},[22017],{"type":26,"value":22018},"     * (text or json exclusively) in. Bound in fetch override.\n",{"type":21,"tag":455,"props":22020,"children":22021},{"class":457,"line":12847},[22022,22026,22030,22034,22038],{"type":21,"tag":455,"props":22023,"children":22024},{"style":462},[22025],{"type":26,"value":17609},{"type":21,"tag":455,"props":22027,"children":22028},{"style":1349},[22029],{"type":26,"value":17614},{"type":21,"tag":455,"props":22031,"children":22032},{"style":1365},[22033],{"type":26,"value":20147},{"type":21,"tag":455,"props":22035,"children":22036},{"style":486},[22037],{"type":26,"value":20152},{"type":21,"tag":455,"props":22039,"children":22040},{"style":462},[22041],{"type":26,"value":20157},{"type":21,"tag":455,"props":22043,"children":22044},{"class":457,"line":12863},[22045],{"type":21,"tag":455,"props":22046,"children":22047},{"style":462},[22048],{"type":26,"value":22049},"     * Bound in fetch override.\n",{"type":21,"tag":455,"props":22051,"children":22052},{"class":457,"line":12883},[22053,22057,22061,22065],{"type":21,"tag":455,"props":22054,"children":22055},{"style":462},[22056],{"type":26,"value":17609},{"type":21,"tag":455,"props":22058,"children":22059},{"style":1349},[22060],{"type":26,"value":17614},{"type":21,"tag":455,"props":22062,"children":22063},{"style":1365},[22064],{"type":26,"value":20181},{"type":21,"tag":455,"props":22066,"children":22067},{"style":486},[22068],{"type":26,"value":20186},{"type":21,"tag":455,"props":22070,"children":22071},{"class":457,"line":12895},[22072],{"type":21,"tag":455,"props":22073,"children":22074},{"style":462},[22075],{"type":26,"value":11610},{"type":21,"tag":455,"props":22077,"children":22078},{"class":457,"line":12903},[22079,22083,22087,22091,22095,22099,22103,22107,22111,22115,22119],{"type":21,"tag":455,"props":22080,"children":22081},{"style":1349},[22082],{"type":26,"value":17639},{"type":21,"tag":455,"props":22084,"children":22085},{"style":1365},[22086],{"type":26,"value":20206},{"type":21,"tag":455,"props":22088,"children":22089},{"style":486},[22090],{"type":26,"value":489},{"type":21,"tag":455,"props":22092,"children":22093},{"style":4527},[22094],{"type":26,"value":20215},{"type":21,"tag":455,"props":22096,"children":22097},{"style":486},[22098],{"type":26,"value":2228},{"type":21,"tag":455,"props":22100,"children":22101},{"style":4527},[22102],{"type":26,"value":20224},{"type":21,"tag":455,"props":22104,"children":22105},{"style":486},[22106],{"type":26,"value":2228},{"type":21,"tag":455,"props":22108,"children":22109},{"style":4527},[22110],{"type":26,"value":20233},{"type":21,"tag":455,"props":22112,"children":22113},{"style":486},[22114],{"type":26,"value":2228},{"type":21,"tag":455,"props":22116,"children":22117},{"style":4527},[22118],{"type":26,"value":19617},{"type":21,"tag":455,"props":22120,"children":22121},{"style":486},[22122],{"type":26,"value":4879},{"type":21,"tag":455,"props":22124,"children":22125},{"class":457,"line":12911},[22126,22130,22134,22138,22142,22146],{"type":21,"tag":455,"props":22127,"children":22128},{"style":1349},[22129],{"type":26,"value":21230},{"type":21,"tag":455,"props":22131,"children":22132},{"style":486},[22133],{"type":26,"value":20257},{"type":21,"tag":455,"props":22135,"children":22136},{"style":1349},[22137],{"type":26,"value":3193},{"type":21,"tag":455,"props":22139,"children":22140},{"style":486},[22141],{"type":26,"value":10279},{"type":21,"tag":455,"props":22143,"children":22144},{"style":1365},[22145],{"type":26,"value":20270},{"type":21,"tag":455,"props":22147,"children":22148},{"style":486},[22149],{"type":26,"value":20275},{"type":21,"tag":455,"props":22151,"children":22152},{"class":457,"line":12928},[22153,22158,22162,22166,22170,22174,22178,22182,22186,22190,22194,22198],{"type":21,"tag":455,"props":22154,"children":22155},{"style":486},[22156],{"type":26,"value":22157},"            dataType ",{"type":21,"tag":455,"props":22159,"children":22160},{"style":1349},[22161],{"type":26,"value":3193},{"type":21,"tag":455,"props":22163,"children":22164},{"style":486},[22165],{"type":26,"value":20292},{"type":21,"tag":455,"props":22167,"children":22168},{"style":1365},[22169],{"type":26,"value":6943},{"type":21,"tag":455,"props":22171,"children":22172},{"style":486},[22173],{"type":26,"value":489},{"type":21,"tag":455,"props":22175,"children":22176},{"style":492},[22177],{"type":26,"value":20305},{"type":21,"tag":455,"props":22179,"children":22180},{"style":486},[22181],{"type":26,"value":4584},{"type":21,"tag":455,"props":22183,"children":22184},{"style":1349},[22185],{"type":26,"value":12183},{"type":21,"tag":455,"props":22187,"children":22188},{"style":492},[22189],{"type":26,"value":20318},{"type":21,"tag":455,"props":22191,"children":22192},{"style":486},[22193],{"type":26,"value":5081},{"type":21,"tag":455,"props":22195,"children":22196},{"style":1365},[22197],{"type":26,"value":20327},{"type":21,"tag":455,"props":22199,"children":22200},{"style":486},[22201],{"type":26,"value":3398},{"type":21,"tag":455,"props":22203,"children":22204},{"class":457,"line":12936},[22205],{"type":21,"tag":455,"props":22206,"children":22207},{"emptyLinePlaceholder":471},[22208],{"type":26,"value":474},{"type":21,"tag":455,"props":22210,"children":22211},{"class":457,"line":12953},[22212,22217,22221,22225,22229,22233,22237,22241,22245],{"type":21,"tag":455,"props":22213,"children":22214},{"style":486},[22215],{"type":26,"value":22216},"        cres.",{"type":21,"tag":455,"props":22218,"children":22219},{"style":1365},[22220],{"type":26,"value":26},{"type":21,"tag":455,"props":22222,"children":22223},{"style":486},[22224],{"type":26,"value":19016},{"type":21,"tag":455,"props":22226,"children":22227},{"style":1365},[22228],{"type":26,"value":4520},{"type":21,"tag":455,"props":22230,"children":22231},{"style":486},[22232],{"type":26,"value":4575},{"type":21,"tag":455,"props":22234,"children":22235},{"style":4527},[22236],{"type":26,"value":26},{"type":21,"tag":455,"props":22238,"children":22239},{"style":486},[22240],{"type":26,"value":4584},{"type":21,"tag":455,"props":22242,"children":22243},{"style":1349},[22244],{"type":26,"value":4589},{"type":21,"tag":455,"props":22246,"children":22247},{"style":486},[22248],{"type":26,"value":2470},{"type":21,"tag":455,"props":22250,"children":22251},{"class":457,"line":12961},[22252,22257],{"type":21,"tag":455,"props":22253,"children":22254},{"style":1349},[22255],{"type":26,"value":22256},"            try",{"type":21,"tag":455,"props":22258,"children":22259},{"style":486},[22260],{"type":26,"value":2470},{"type":21,"tag":455,"props":22262,"children":22263},{"class":457,"line":12973},[22264,22269,22273],{"type":21,"tag":455,"props":22265,"children":22266},{"style":486},[22267],{"type":26,"value":22268},"                storage.",{"type":21,"tag":455,"props":22270,"children":22271},{"style":1365},[22272],{"type":26,"value":20403},{"type":21,"tag":455,"props":22274,"children":22275},{"style":486},[22276],{"type":26,"value":20408},{"type":21,"tag":455,"props":22278,"children":22279},{"class":457,"line":19363},[22280,22284,22288,22292,22296,22300,22304,22308,22312,22316,22320,22324,22328,22332,22336,22340,22344],{"type":21,"tag":455,"props":22281,"children":22282},{"style":486},[22283],{"type":26,"value":22268},{"type":21,"tag":455,"props":22285,"children":22286},{"style":1365},[22287],{"type":26,"value":20403},{"type":21,"tag":455,"props":22289,"children":22290},{"style":486},[22291],{"type":26,"value":20424},{"type":21,"tag":455,"props":22293,"children":22294},{"style":1349},[22295],{"type":26,"value":2206},{"type":21,"tag":455,"props":22297,"children":22298},{"style":492},[22299],{"type":26,"value":20433},{"type":21,"tag":455,"props":22301,"children":22302},{"style":486},[22303],{"type":26,"value":2228},{"type":21,"tag":455,"props":22305,"children":22306},{"style":1349},[22307],{"type":26,"value":20442},{"type":21,"tag":455,"props":22309,"children":22310},{"style":1365},[22311],{"type":26,"value":20447},{"type":21,"tag":455,"props":22313,"children":22314},{"style":486},[22315],{"type":26,"value":20452},{"type":21,"tag":455,"props":22317,"children":22318},{"style":1349},[22319],{"type":26,"value":2206},{"type":21,"tag":455,"props":22321,"children":22322},{"style":480},[22323],{"type":26,"value":20461},{"type":21,"tag":455,"props":22325,"children":22326},{"style":1349},[22327],{"type":26,"value":11055},{"type":21,"tag":455,"props":22329,"children":22330},{"style":480},[22331],{"type":26,"value":20470},{"type":21,"tag":455,"props":22333,"children":22334},{"style":1349},[22335],{"type":26,"value":11055},{"type":21,"tag":455,"props":22337,"children":22338},{"style":480},[22339],{"type":26,"value":20470},{"type":21,"tag":455,"props":22341,"children":22342},{"style":1349},[22343],{"type":26,"value":11055},{"type":21,"tag":455,"props":22345,"children":22346},{"style":486},[22347],{"type":26,"value":20487},{"type":21,"tag":455,"props":22349,"children":22350},{"class":457,"line":19371},[22351,22355,22359,22363,22367,22371],{"type":21,"tag":455,"props":22352,"children":22353},{"style":486},[22354],{"type":26,"value":22268},{"type":21,"tag":455,"props":22356,"children":22357},{"style":1365},[22358],{"type":26,"value":20403},{"type":21,"tag":455,"props":22360,"children":22361},{"style":486},[22362],{"type":26,"value":20424},{"type":21,"tag":455,"props":22364,"children":22365},{"style":1349},[22366],{"type":26,"value":2206},{"type":21,"tag":455,"props":22368,"children":22369},{"style":492},[22370],{"type":26,"value":20511},{"type":21,"tag":455,"props":22372,"children":22373},{"style":486},[22374],{"type":26,"value":20516},{"type":21,"tag":455,"props":22376,"children":22377},{"class":457,"line":19380},[22378,22383,22387],{"type":21,"tag":455,"props":22379,"children":22380},{"style":486},[22381],{"type":26,"value":22382},"            } ",{"type":21,"tag":455,"props":22384,"children":22385},{"style":1349},[22386],{"type":26,"value":4638},{"type":21,"tag":455,"props":22388,"children":22389},{"style":486},[22390],{"type":26,"value":20533},{"type":21,"tag":455,"props":22392,"children":22393},{"class":457,"line":19389},[22394],{"type":21,"tag":455,"props":22395,"children":22396},{"style":462},[22397],{"type":26,"value":22398},"                // Remove any incomplete data that may have been saved before the exception was caught\n",{"type":21,"tag":455,"props":22400,"children":22401},{"class":457,"line":19397},[22402,22407],{"type":21,"tag":455,"props":22403,"children":22404},{"style":1365},[22405],{"type":26,"value":22406},"                removeFromStorage",{"type":21,"tag":455,"props":22408,"children":22409},{"style":486},[22410],{"type":26,"value":20554},{"type":21,"tag":455,"props":22412,"children":22413},{"class":457,"line":19438},[22414,22419,22423,22427,22431,22435],{"type":21,"tag":455,"props":22415,"children":22416},{"style":486},[22417],{"type":26,"value":22418},"                console.",{"type":21,"tag":455,"props":22420,"children":22421},{"style":1365},[22422],{"type":26,"value":3915},{"type":21,"tag":455,"props":22424,"children":22425},{"style":486},[22426],{"type":26,"value":489},{"type":21,"tag":455,"props":22428,"children":22429},{"style":492},[22430],{"type":26,"value":20574},{"type":21,"tag":455,"props":22432,"children":22433},{"style":1349},[22434],{"type":26,"value":3929},{"type":21,"tag":455,"props":22436,"children":22437},{"style":486},[22438],{"type":26,"value":20583},{"type":21,"tag":455,"props":22440,"children":22441},{"class":457,"line":19447},[22442],{"type":21,"tag":455,"props":22443,"children":22444},{"style":486},[22445],{"type":26,"value":12130},{"type":21,"tag":455,"props":22447,"children":22448},{"class":457,"line":19456},[22449],{"type":21,"tag":455,"props":22450,"children":22451},{"style":486},[22452],{"type":26,"value":17829},{"type":21,"tag":455,"props":22454,"children":22455},{"class":457,"line":19465},[22456],{"type":21,"tag":455,"props":22457,"children":22458},{"emptyLinePlaceholder":471},[22459],{"type":26,"value":474},{"type":21,"tag":455,"props":22461,"children":22462},{"class":457,"line":19474},[22463,22467],{"type":21,"tag":455,"props":22464,"children":22465},{"style":1349},[22466],{"type":26,"value":1430},{"type":21,"tag":455,"props":22468,"children":22469},{"style":486},[22470],{"type":26,"value":20617},{"type":21,"tag":455,"props":22472,"children":22473},{"class":457,"line":19483},[22474],{"type":21,"tag":455,"props":22475,"children":22476},{"style":486},[22477],{"type":26,"value":6008},{"type":21,"tag":455,"props":22479,"children":22480},{"class":457,"line":19539},[22481],{"type":21,"tag":455,"props":22482,"children":22483},{"emptyLinePlaceholder":471},[22484],{"type":26,"value":474},{"type":21,"tag":455,"props":22486,"children":22487},{"class":457,"line":19547},[22488],{"type":21,"tag":455,"props":22489,"children":22490},{"style":462},[22491],{"type":26,"value":17593},{"type":21,"tag":455,"props":22493,"children":22494},{"class":457,"line":19564},[22495],{"type":21,"tag":455,"props":22496,"children":22497},{"style":462},[22498],{"type":26,"value":22499},"     * Create a new response containing the cached value, and return a promise\n",{"type":21,"tag":455,"props":22501,"children":22502},{"class":457,"line":19582},[22503],{"type":21,"tag":455,"props":22504,"children":22505},{"style":462},[22506],{"type":26,"value":22507},"     * that resolves with this response.\n",{"type":21,"tag":455,"props":22509,"children":22510},{"class":457,"line":19591},[22511],{"type":21,"tag":455,"props":22512,"children":22513},{"style":462},[22514],{"type":26,"value":21165},{"type":21,"tag":455,"props":22516,"children":22517},{"class":457,"line":19624},[22518,22522,22526],{"type":21,"tag":455,"props":22519,"children":22520},{"style":462},[22521],{"type":26,"value":17609},{"type":21,"tag":455,"props":22523,"children":22524},{"style":1349},[22525],{"type":26,"value":17614},{"type":21,"tag":455,"props":22527,"children":22528},{"style":486},[22529],{"type":26,"value":20696},{"type":21,"tag":455,"props":22531,"children":22532},{"class":457,"line":19651},[22533,22537,22541],{"type":21,"tag":455,"props":22534,"children":22535},{"style":462},[22536],{"type":26,"value":17609},{"type":21,"tag":455,"props":22538,"children":22539},{"style":1349},[22540],{"type":26,"value":17614},{"type":21,"tag":455,"props":22542,"children":22543},{"style":486},[22544],{"type":26,"value":20712},{"type":21,"tag":455,"props":22546,"children":22547},{"class":457,"line":19659},[22548,22552,22556],{"type":21,"tag":455,"props":22549,"children":22550},{"style":462},[22551],{"type":26,"value":17609},{"type":21,"tag":455,"props":22553,"children":22554},{"style":1349},[22555],{"type":26,"value":20724},{"type":21,"tag":455,"props":22557,"children":22558},{"style":1365},[22559],{"type":26,"value":20729},{"type":21,"tag":455,"props":22561,"children":22562},{"class":457,"line":19667},[22563],{"type":21,"tag":455,"props":22564,"children":22565},{"style":462},[22566],{"type":26,"value":11610},{"type":21,"tag":455,"props":22568,"children":22570},{"class":457,"line":22569},97,[22571,22575,22579,22583,22587,22591,22595],{"type":21,"tag":455,"props":22572,"children":22573},{"style":1349},[22574],{"type":26,"value":17639},{"type":21,"tag":455,"props":22576,"children":22577},{"style":1365},[22578],{"type":26,"value":20748},{"type":21,"tag":455,"props":22580,"children":22581},{"style":486},[22582],{"type":26,"value":489},{"type":21,"tag":455,"props":22584,"children":22585},{"style":4527},[22586],{"type":26,"value":20757},{"type":21,"tag":455,"props":22588,"children":22589},{"style":486},[22590],{"type":26,"value":2228},{"type":21,"tag":455,"props":22592,"children":22593},{"style":4527},[22594],{"type":26,"value":20766},{"type":21,"tag":455,"props":22596,"children":22597},{"style":486},[22598],{"type":26,"value":4879},{"type":21,"tag":455,"props":22600,"children":22602},{"class":457,"line":22601},98,[22603,22607,22611,22615,22619,22623],{"type":21,"tag":455,"props":22604,"children":22605},{"style":1349},[22606],{"type":26,"value":21230},{"type":21,"tag":455,"props":22608,"children":22609},{"style":486},[22610],{"type":26,"value":19634},{"type":21,"tag":455,"props":22612,"children":22613},{"style":1349},[22614],{"type":26,"value":3193},{"type":21,"tag":455,"props":22616,"children":22617},{"style":1349},[22618],{"type":26,"value":2183},{"type":21,"tag":455,"props":22620,"children":22621},{"style":1365},[22622],{"type":26,"value":20794},{"type":21,"tag":455,"props":22624,"children":22625},{"style":486},[22626],{"type":26,"value":11391},{"type":21,"tag":455,"props":22628,"children":22630},{"class":457,"line":22629},99,[22631],{"type":21,"tag":455,"props":22632,"children":22633},{"style":486},[22634],{"type":26,"value":22635},"            value,\n",{"type":21,"tag":455,"props":22637,"children":22639},{"class":457,"line":22638},100,[22640],{"type":21,"tag":455,"props":22641,"children":22642},{"style":486},[22643],{"type":26,"value":22644},"            {\n",{"type":21,"tag":455,"props":22646,"children":22648},{"class":457,"line":22647},101,[22649,22654,22658],{"type":21,"tag":455,"props":22650,"children":22651},{"style":486},[22652],{"type":26,"value":22653},"                status: ",{"type":21,"tag":455,"props":22655,"children":22656},{"style":480},[22657],{"type":26,"value":10244},{"type":21,"tag":455,"props":22659,"children":22660},{"style":486},[22661],{"type":26,"value":2483},{"type":21,"tag":455,"props":22663,"children":22665},{"class":457,"line":22664},102,[22666,22671,22675],{"type":21,"tag":455,"props":22667,"children":22668},{"style":486},[22669],{"type":26,"value":22670},"                statusText: ",{"type":21,"tag":455,"props":22672,"children":22673},{"style":492},[22674],{"type":26,"value":20842},{"type":21,"tag":455,"props":22676,"children":22677},{"style":486},[22678],{"type":26,"value":2483},{"type":21,"tag":455,"props":22680,"children":22682},{"class":457,"line":22681},103,[22683],{"type":21,"tag":455,"props":22684,"children":22685},{"style":486},[22686],{"type":26,"value":22687},"                headers: {\n",{"type":21,"tag":455,"props":22689,"children":22691},{"class":457,"line":22690},104,[22692,22697],{"type":21,"tag":455,"props":22693,"children":22694},{"style":492},[22695],{"type":26,"value":22696},"                    'Content-Type'",{"type":21,"tag":455,"props":22698,"children":22699},{"style":486},[22700],{"type":26,"value":20867},{"type":21,"tag":455,"props":22702,"children":22704},{"class":457,"line":22703},105,[22705],{"type":21,"tag":455,"props":22706,"children":22707},{"style":486},[22708],{"type":26,"value":12461},{"type":21,"tag":455,"props":22710,"children":22712},{"class":457,"line":22711},106,[22713],{"type":21,"tag":455,"props":22714,"children":22715},{"style":486},[22716],{"type":26,"value":12130},{"type":21,"tag":455,"props":22718,"children":22720},{"class":457,"line":22719},107,[22721],{"type":21,"tag":455,"props":22722,"children":22723},{"style":486},[22724],{"type":26,"value":22725},"        );\n",{"type":21,"tag":455,"props":22727,"children":22729},{"class":457,"line":22728},108,[22730],{"type":21,"tag":455,"props":22731,"children":22732},{"emptyLinePlaceholder":471},[22733],{"type":26,"value":474},{"type":21,"tag":455,"props":22735,"children":22737},{"class":457,"line":22736},109,[22738,22742,22746,22750,22754,22758,22762,22766,22770,22774],{"type":21,"tag":455,"props":22739,"children":22740},{"style":1349},[22741],{"type":26,"value":1430},{"type":21,"tag":455,"props":22743,"children":22744},{"style":1349},[22745],{"type":26,"value":2183},{"type":21,"tag":455,"props":22747,"children":22748},{"style":480},[22749],{"type":26,"value":19053},{"type":21,"tag":455,"props":22751,"children":22752},{"style":486},[22753],{"type":26,"value":489},{"type":21,"tag":455,"props":22755,"children":22756},{"style":1349},[22757],{"type":26,"value":3897},{"type":21,"tag":455,"props":22759,"children":22760},{"style":486},[22761],{"type":26,"value":2136},{"type":21,"tag":455,"props":22763,"children":22764},{"style":4527},[22765],{"type":26,"value":20927},{"type":21,"tag":455,"props":22767,"children":22768},{"style":486},[22769],{"type":26,"value":2228},{"type":21,"tag":455,"props":22771,"children":22772},{"style":4527},[22773],{"type":26,"value":20936},{"type":21,"tag":455,"props":22775,"children":22776},{"style":486},[22777],{"type":26,"value":4879},{"type":21,"tag":455,"props":22779,"children":22781},{"class":457,"line":22780},110,[22782,22787],{"type":21,"tag":455,"props":22783,"children":22784},{"style":1365},[22785],{"type":26,"value":22786},"            resolve",{"type":21,"tag":455,"props":22788,"children":22789},{"style":486},[22790],{"type":26,"value":20953},{"type":21,"tag":455,"props":22792,"children":22794},{"class":457,"line":22793},111,[22795],{"type":21,"tag":455,"props":22796,"children":22797},{"style":486},[22798],{"type":26,"value":17829},{"type":21,"tag":455,"props":22800,"children":22802},{"class":457,"line":22801},112,[22803],{"type":21,"tag":455,"props":22804,"children":22805},{"style":486},[22806],{"type":26,"value":6008},{"type":21,"tag":455,"props":22808,"children":22810},{"class":457,"line":22809},113,[22811],{"type":21,"tag":455,"props":22812,"children":22813},{"emptyLinePlaceholder":471},[22814],{"type":26,"value":474},{"type":21,"tag":455,"props":22816,"children":22818},{"class":457,"line":22817},114,[22819],{"type":21,"tag":455,"props":22820,"children":22821},{"style":462},[22822],{"type":26,"value":17593},{"type":21,"tag":455,"props":22824,"children":22826},{"class":457,"line":22825},115,[22827],{"type":21,"tag":455,"props":22828,"children":22829},{"style":462},[22830],{"type":26,"value":22831},"     * Override fetch on the global context, so that we can intercept\n",{"type":21,"tag":455,"props":22833,"children":22835},{"class":457,"line":22834},116,[22836],{"type":21,"tag":455,"props":22837,"children":22838},{"style":462},[22839],{"type":26,"value":22840},"     * fetch calls and respond with locally cached content, if available.\n",{"type":21,"tag":455,"props":22842,"children":22844},{"class":457,"line":22843},117,[22845],{"type":21,"tag":455,"props":22846,"children":22847},{"style":462},[22848],{"type":26,"value":22849},"     * New parameters available on the call to fetch:\n",{"type":21,"tag":455,"props":22851,"children":22853},{"class":457,"line":22852},118,[22854,22859],{"type":21,"tag":455,"props":22855,"children":22856},{"style":462},[22857],{"type":26,"value":22858},"     * localCache   : true",{"type":21,"tag":455,"props":22860,"children":22861},{"style":462},[22862],{"type":26,"value":22863}," // required - either a boolean (if true, localStorage is used,\n",{"type":21,"tag":455,"props":22865,"children":22867},{"class":457,"line":22866},119,[22868],{"type":21,"tag":455,"props":22869,"children":22870},{"style":462},[22871],{"type":26,"value":22872},"     * if false request is not cached or returned from cache), or an object implementing the\n",{"type":21,"tag":455,"props":22874,"children":22876},{"class":457,"line":22875},120,[22877],{"type":21,"tag":455,"props":22878,"children":22879},{"style":462},[22880],{"type":26,"value":22881},"     * Storage interface, in which case that object is used instead.\n",{"type":21,"tag":455,"props":22883,"children":22885},{"class":457,"line":22884},121,[22886,22891],{"type":21,"tag":455,"props":22887,"children":22888},{"style":462},[22889],{"type":26,"value":22890},"     * cacheTTL     : 5,",{"type":21,"tag":455,"props":22892,"children":22893},{"style":462},[22894],{"type":26,"value":22895}," // optional, cache time in hours, default is 5. Use float numbers for\n",{"type":21,"tag":455,"props":22897,"children":22899},{"class":457,"line":22898},122,[22900],{"type":21,"tag":455,"props":22901,"children":22902},{"style":462},[22903],{"type":26,"value":22904},"     * values less than a full hour (e.g. 0.5 for 1/2 hour).\n",{"type":21,"tag":455,"props":22906,"children":22908},{"class":457,"line":22907},123,[22909,22914],{"type":21,"tag":455,"props":22910,"children":22911},{"style":462},[22912],{"type":26,"value":22913},"     * cacheKey     : 'post',",{"type":21,"tag":455,"props":22915,"children":22916},{"style":462},[22917],{"type":26,"value":22918}," // optional - key under which cached string will be stored.\n",{"type":21,"tag":455,"props":22920,"children":22922},{"class":457,"line":22921},124,[22923,22928],{"type":21,"tag":455,"props":22924,"children":22925},{"style":462},[22926],{"type":26,"value":22927},"     * isCacheValid : function",{"type":21,"tag":455,"props":22929,"children":22930},{"style":462},[22931],{"type":26,"value":22932},"  // optional - return true for valid, false for invalid.\n",{"type":21,"tag":455,"props":22934,"children":22936},{"class":457,"line":22935},125,[22937],{"type":21,"tag":455,"props":22938,"children":22939},{"style":462},[22940],{"type":26,"value":11610},{"type":21,"tag":455,"props":22942,"children":22944},{"class":457,"line":22943},126,[22945,22950,22954,22958,22962,22966,22970,22974,22978],{"type":21,"tag":455,"props":22946,"children":22947},{"style":486},[22948],{"type":26,"value":22949},"    self.",{"type":21,"tag":455,"props":22951,"children":22952},{"style":1365},[22953],{"type":26,"value":19758},{"type":21,"tag":455,"props":22955,"children":22956},{"style":1349},[22957],{"type":26,"value":2131},{"type":21,"tag":455,"props":22959,"children":22960},{"style":1349},[22961],{"type":26,"value":4068},{"type":21,"tag":455,"props":22963,"children":22964},{"style":486},[22965],{"type":26,"value":2136},{"type":21,"tag":455,"props":22967,"children":22968},{"style":4527},[22969],{"type":26,"value":21209},{"type":21,"tag":455,"props":22971,"children":22972},{"style":486},[22973],{"type":26,"value":2228},{"type":21,"tag":455,"props":22975,"children":22976},{"style":4527},[22977],{"type":26,"value":21218},{"type":21,"tag":455,"props":22979,"children":22980},{"style":486},[22981],{"type":26,"value":4879},{"type":21,"tag":455,"props":22983,"children":22985},{"class":457,"line":22984},127,[22986,22990,22994,22998,23002],{"type":21,"tag":455,"props":22987,"children":22988},{"style":1349},[22989],{"type":26,"value":21230},{"type":21,"tag":455,"props":22991,"children":22992},{"style":486},[22993],{"type":26,"value":21590},{"type":21,"tag":455,"props":22995,"children":22996},{"style":1349},[22997],{"type":26,"value":3193},{"type":21,"tag":455,"props":22999,"children":23000},{"style":1365},[23001],{"type":26,"value":21490},{"type":21,"tag":455,"props":23003,"children":23004},{"style":486},[23005],{"type":26,"value":23006},"(settings.localCache),\n",{"type":21,"tag":455,"props":23008,"children":23010},{"class":457,"line":23009},128,[23011,23016,23020,23025,23029,23034],{"type":21,"tag":455,"props":23012,"children":23013},{"style":486},[23014],{"type":26,"value":23015},"            hourstl ",{"type":21,"tag":455,"props":23017,"children":23018},{"style":1349},[23019],{"type":26,"value":3193},{"type":21,"tag":455,"props":23021,"children":23022},{"style":486},[23023],{"type":26,"value":23024}," settings.cacheTTL ",{"type":21,"tag":455,"props":23026,"children":23027},{"style":1349},[23028],{"type":26,"value":12183},{"type":21,"tag":455,"props":23030,"children":23031},{"style":480},[23032],{"type":26,"value":23033}," 5",{"type":21,"tag":455,"props":23035,"children":23036},{"style":486},[23037],{"type":26,"value":2483},{"type":21,"tag":455,"props":23039,"children":23041},{"class":457,"line":23040},129,[23042,23047,23051,23055],{"type":21,"tag":455,"props":23043,"children":23044},{"style":486},[23045],{"type":26,"value":23046},"            cacheKey ",{"type":21,"tag":455,"props":23048,"children":23049},{"style":1349},[23050],{"type":26,"value":3193},{"type":21,"tag":455,"props":23052,"children":23053},{"style":1365},[23054],{"type":26,"value":21200},{"type":21,"tag":455,"props":23056,"children":23057},{"style":486},[23058],{"type":26,"value":23059},"(url, settings),\n",{"type":21,"tag":455,"props":23061,"children":23063},{"class":457,"line":23062},130,[23064,23069,23073],{"type":21,"tag":455,"props":23065,"children":23066},{"style":486},[23067],{"type":26,"value":23068},"            cacheValid ",{"type":21,"tag":455,"props":23070,"children":23071},{"style":1349},[23072],{"type":26,"value":3193},{"type":21,"tag":455,"props":23074,"children":23075},{"style":486},[23076],{"type":26,"value":23077}," settings.isCacheValid,\n",{"type":21,"tag":455,"props":23079,"children":23081},{"class":457,"line":23080},131,[23082],{"type":21,"tag":455,"props":23083,"children":23084},{"style":486},[23085],{"type":26,"value":23086},"            ttl,\n",{"type":21,"tag":455,"props":23088,"children":23090},{"class":457,"line":23089},132,[23091],{"type":21,"tag":455,"props":23092,"children":23093},{"style":486},[23094],{"type":26,"value":22635},{"type":21,"tag":455,"props":23096,"children":23098},{"class":457,"line":23097},133,[23099],{"type":21,"tag":455,"props":23100,"children":23101},{"style":486},[23102],{"type":26,"value":23103},"            dataType;\n",{"type":21,"tag":455,"props":23105,"children":23107},{"class":457,"line":23106},134,[23108],{"type":21,"tag":455,"props":23109,"children":23110},{"emptyLinePlaceholder":471},[23111],{"type":26,"value":474},{"type":21,"tag":455,"props":23113,"children":23115},{"class":457,"line":23114},135,[23116,23120,23124,23128,23132,23136,23140],{"type":21,"tag":455,"props":23117,"children":23118},{"style":1349},[23119],{"type":26,"value":5627},{"type":21,"tag":455,"props":23121,"children":23122},{"style":486},[23123],{"type":26,"value":2136},{"type":21,"tag":455,"props":23125,"children":23126},{"style":1349},[23127],{"type":26,"value":1272},{"type":21,"tag":455,"props":23129,"children":23130},{"style":486},[23131],{"type":26,"value":21522},{"type":21,"tag":455,"props":23133,"children":23134},{"style":1349},[23135],{"type":26,"value":260},{"type":21,"tag":455,"props":23137,"children":23138},{"style":1365},[23139],{"type":26,"value":19643},{"type":21,"tag":455,"props":23141,"children":23142},{"style":486},[23143],{"type":26,"value":23144},"(url, settings);\n",{"type":21,"tag":455,"props":23146,"children":23148},{"class":457,"line":23147},136,[23149],{"type":21,"tag":455,"props":23150,"children":23151},{"emptyLinePlaceholder":471},[23152],{"type":26,"value":474},{"type":21,"tag":455,"props":23154,"children":23156},{"class":457,"line":23155},137,[23157,23162,23166,23171,23176,23180,23184,23188],{"type":21,"tag":455,"props":23158,"children":23159},{"style":486},[23160],{"type":26,"value":23161},"        ttl ",{"type":21,"tag":455,"props":23163,"children":23164},{"style":1349},[23165],{"type":26,"value":3193},{"type":21,"tag":455,"props":23167,"children":23168},{"style":486},[23169],{"type":26,"value":23170}," storage.",{"type":21,"tag":455,"props":23172,"children":23173},{"style":1365},[23174],{"type":26,"value":23175},"getItem",{"type":21,"tag":455,"props":23177,"children":23178},{"style":486},[23179],{"type":26,"value":20424},{"type":21,"tag":455,"props":23181,"children":23182},{"style":1349},[23183],{"type":26,"value":2206},{"type":21,"tag":455,"props":23185,"children":23186},{"style":492},[23187],{"type":26,"value":20433},{"type":21,"tag":455,"props":23189,"children":23190},{"style":486},[23191],{"type":26,"value":2171},{"type":21,"tag":455,"props":23193,"children":23195},{"class":457,"line":23194},138,[23196],{"type":21,"tag":455,"props":23197,"children":23198},{"emptyLinePlaceholder":471},[23199],{"type":26,"value":474},{"type":21,"tag":455,"props":23201,"children":23203},{"class":457,"line":23202},139,[23204,23208,23213,23217,23222,23227,23231,23236,23240,23245,23250],{"type":21,"tag":455,"props":23205,"children":23206},{"style":1349},[23207],{"type":26,"value":5627},{"type":21,"tag":455,"props":23209,"children":23210},{"style":486},[23211],{"type":26,"value":23212}," (cacheValid ",{"type":21,"tag":455,"props":23214,"children":23215},{"style":1349},[23216],{"type":26,"value":11637},{"type":21,"tag":455,"props":23218,"children":23219},{"style":1349},[23220],{"type":26,"value":23221}," typeof",{"type":21,"tag":455,"props":23223,"children":23224},{"style":486},[23225],{"type":26,"value":23226}," cacheValid ",{"type":21,"tag":455,"props":23228,"children":23229},{"style":1349},[23230],{"type":26,"value":21552},{"type":21,"tag":455,"props":23232,"children":23233},{"style":492},[23234],{"type":26,"value":23235}," 'function'",{"type":21,"tag":455,"props":23237,"children":23238},{"style":1349},[23239],{"type":26,"value":1467},{"type":21,"tag":455,"props":23241,"children":23242},{"style":1349},[23243],{"type":26,"value":23244}," !",{"type":21,"tag":455,"props":23246,"children":23247},{"style":1365},[23248],{"type":26,"value":23249},"cacheValid",{"type":21,"tag":455,"props":23251,"children":23252},{"style":486},[23253],{"type":26,"value":23254},"()) {\n",{"type":21,"tag":455,"props":23256,"children":23258},{"class":457,"line":23257},140,[23259,23263],{"type":21,"tag":455,"props":23260,"children":23261},{"style":1365},[23262],{"type":26,"value":20549},{"type":21,"tag":455,"props":23264,"children":23265},{"style":486},[23266],{"type":26,"value":20554},{"type":21,"tag":455,"props":23268,"children":23270},{"class":457,"line":23269},141,[23271,23276,23280,23284],{"type":21,"tag":455,"props":23272,"children":23273},{"style":486},[23274],{"type":26,"value":23275},"            ttl ",{"type":21,"tag":455,"props":23277,"children":23278},{"style":1349},[23279],{"type":26,"value":3193},{"type":21,"tag":455,"props":23281,"children":23282},{"style":480},[23283],{"type":26,"value":1417},{"type":21,"tag":455,"props":23285,"children":23286},{"style":486},[23287],{"type":26,"value":1440},{"type":21,"tag":455,"props":23289,"children":23291},{"class":457,"line":23290},142,[23292],{"type":21,"tag":455,"props":23293,"children":23294},{"style":486},[23295],{"type":26,"value":5680},{"type":21,"tag":455,"props":23297,"children":23299},{"class":457,"line":23298},143,[23300],{"type":21,"tag":455,"props":23301,"children":23302},{"emptyLinePlaceholder":471},[23303],{"type":26,"value":474},{"type":21,"tag":455,"props":23305,"children":23307},{"class":457,"line":23306},144,[23308,23312,23317,23321,23326,23330,23335,23339],{"type":21,"tag":455,"props":23309,"children":23310},{"style":1349},[23311],{"type":26,"value":5627},{"type":21,"tag":455,"props":23313,"children":23314},{"style":486},[23315],{"type":26,"value":23316}," (ttl ",{"type":21,"tag":455,"props":23318,"children":23319},{"style":1349},[23320],{"type":26,"value":11637},{"type":21,"tag":455,"props":23322,"children":23323},{"style":486},[23324],{"type":26,"value":23325}," ttl ",{"type":21,"tag":455,"props":23327,"children":23328},{"style":1349},[23329],{"type":26,"value":11627},{"type":21,"tag":455,"props":23331,"children":23332},{"style":1349},[23333],{"type":26,"value":23334}," +new",{"type":21,"tag":455,"props":23336,"children":23337},{"style":1365},[23338],{"type":26,"value":20447},{"type":21,"tag":455,"props":23340,"children":23341},{"style":486},[23342],{"type":26,"value":23254},{"type":21,"tag":455,"props":23344,"children":23346},{"class":457,"line":23345},145,[23347,23351],{"type":21,"tag":455,"props":23348,"children":23349},{"style":1365},[23350],{"type":26,"value":20549},{"type":21,"tag":455,"props":23352,"children":23353},{"style":486},[23354],{"type":26,"value":20554},{"type":21,"tag":455,"props":23356,"children":23358},{"class":457,"line":23357},146,[23359],{"type":21,"tag":455,"props":23360,"children":23361},{"style":486},[23362],{"type":26,"value":5680},{"type":21,"tag":455,"props":23364,"children":23366},{"class":457,"line":23365},147,[23367],{"type":21,"tag":455,"props":23368,"children":23369},{"emptyLinePlaceholder":471},[23370],{"type":26,"value":474},{"type":21,"tag":455,"props":23372,"children":23374},{"class":457,"line":23373},148,[23375,23380,23384,23388,23392],{"type":21,"tag":455,"props":23376,"children":23377},{"style":486},[23378],{"type":26,"value":23379},"        value ",{"type":21,"tag":455,"props":23381,"children":23382},{"style":1349},[23383],{"type":26,"value":3193},{"type":21,"tag":455,"props":23385,"children":23386},{"style":486},[23387],{"type":26,"value":23170},{"type":21,"tag":455,"props":23389,"children":23390},{"style":1365},[23391],{"type":26,"value":23175},{"type":21,"tag":455,"props":23393,"children":23394},{"style":486},[23395],{"type":26,"value":21850},{"type":21,"tag":455,"props":23397,"children":23399},{"class":457,"line":23398},149,[23400],{"type":21,"tag":455,"props":23401,"children":23402},{"emptyLinePlaceholder":471},[23403],{"type":26,"value":474},{"type":21,"tag":455,"props":23405,"children":23407},{"class":457,"line":23406},150,[23408,23412,23416,23420],{"type":21,"tag":455,"props":23409,"children":23410},{"style":1349},[23411],{"type":26,"value":5627},{"type":21,"tag":455,"props":23413,"children":23414},{"style":486},[23415],{"type":26,"value":2136},{"type":21,"tag":455,"props":23417,"children":23418},{"style":1349},[23419],{"type":26,"value":1272},{"type":21,"tag":455,"props":23421,"children":23422},{"style":486},[23423],{"type":26,"value":23424},"value) {\n",{"type":21,"tag":455,"props":23426,"children":23428},{"class":457,"line":23427},151,[23429],{"type":21,"tag":455,"props":23430,"children":23431},{"style":462},[23432],{"type":26,"value":23433},"            /* If not cached, we'll make the request and add a then block to the resulting promise,\n",{"type":21,"tag":455,"props":23435,"children":23437},{"class":457,"line":23436},152,[23438],{"type":21,"tag":455,"props":23439,"children":23440},{"style":462},[23441],{"type":26,"value":23442},"             in which we'll cache the result. */\n",{"type":21,"tag":455,"props":23444,"children":23446},{"class":457,"line":23445},153,[23447,23451,23455,23460,23464,23469,23474,23478,23483],{"type":21,"tag":455,"props":23448,"children":23449},{"style":1349},[23450],{"type":26,"value":17196},{"type":21,"tag":455,"props":23452,"children":23453},{"style":1365},[23454],{"type":26,"value":19643},{"type":21,"tag":455,"props":23456,"children":23457},{"style":486},[23458],{"type":26,"value":23459},"(url, settings).",{"type":21,"tag":455,"props":23461,"children":23462},{"style":1365},[23463],{"type":26,"value":4520},{"type":21,"tag":455,"props":23465,"children":23466},{"style":486},[23467],{"type":26,"value":23468},"(cacheResponse.",{"type":21,"tag":455,"props":23470,"children":23471},{"style":1365},[23472],{"type":26,"value":23473},"bind",{"type":21,"tag":455,"props":23475,"children":23476},{"style":486},[23477],{"type":26,"value":489},{"type":21,"tag":455,"props":23479,"children":23480},{"style":480},[23481],{"type":26,"value":23482},"null",{"type":21,"tag":455,"props":23484,"children":23485},{"style":486},[23486],{"type":26,"value":23487},", cacheKey, storage, hourstl));\n",{"type":21,"tag":455,"props":23489,"children":23491},{"class":457,"line":23490},154,[23492],{"type":21,"tag":455,"props":23493,"children":23494},{"style":486},[23495],{"type":26,"value":5680},{"type":21,"tag":455,"props":23497,"children":23499},{"class":457,"line":23498},155,[23500],{"type":21,"tag":455,"props":23501,"children":23502},{"emptyLinePlaceholder":471},[23503],{"type":26,"value":474},{"type":21,"tag":455,"props":23505,"children":23507},{"class":457,"line":23506},156,[23508],{"type":21,"tag":455,"props":23509,"children":23510},{"style":462},[23511],{"type":26,"value":23512},"        /* Value is cached, so we'll simply create and respond with a promise of our own,\n",{"type":21,"tag":455,"props":23514,"children":23516},{"class":457,"line":23515},157,[23517],{"type":21,"tag":455,"props":23518,"children":23519},{"style":462},[23520],{"type":26,"value":23521},"         and provide a response object. */\n",{"type":21,"tag":455,"props":23523,"children":23525},{"class":457,"line":23524},158,[23526,23530,23534,23538,23542,23546,23550,23554,23558,23562,23566],{"type":21,"tag":455,"props":23527,"children":23528},{"style":486},[23529],{"type":26,"value":20283},{"type":21,"tag":455,"props":23531,"children":23532},{"style":1349},[23533],{"type":26,"value":3193},{"type":21,"tag":455,"props":23535,"children":23536},{"style":486},[23537],{"type":26,"value":23170},{"type":21,"tag":455,"props":23539,"children":23540},{"style":1365},[23541],{"type":26,"value":23175},{"type":21,"tag":455,"props":23543,"children":23544},{"style":486},[23545],{"type":26,"value":20424},{"type":21,"tag":455,"props":23547,"children":23548},{"style":1349},[23549],{"type":26,"value":2206},{"type":21,"tag":455,"props":23551,"children":23552},{"style":492},[23553],{"type":26,"value":20511},{"type":21,"tag":455,"props":23555,"children":23556},{"style":486},[23557],{"type":26,"value":4584},{"type":21,"tag":455,"props":23559,"children":23560},{"style":1349},[23561],{"type":26,"value":12183},{"type":21,"tag":455,"props":23563,"children":23564},{"style":492},[23565],{"type":26,"value":20318},{"type":21,"tag":455,"props":23567,"children":23568},{"style":486},[23569],{"type":26,"value":1440},{"type":21,"tag":455,"props":23571,"children":23573},{"class":457,"line":23572},159,[23574,23578,23582],{"type":21,"tag":455,"props":23575,"children":23576},{"style":1349},[23577],{"type":26,"value":1430},{"type":21,"tag":455,"props":23579,"children":23580},{"style":1365},[23581],{"type":26,"value":20748},{"type":21,"tag":455,"props":23583,"children":23584},{"style":486},[23585],{"type":26,"value":23586},"(value, dataType);\n",{"type":21,"tag":455,"props":23588,"children":23590},{"class":457,"line":23589},160,[23591],{"type":21,"tag":455,"props":23592,"children":23593},{"style":486},[23594],{"type":26,"value":17578},{"type":21,"tag":455,"props":23596,"children":23598},{"class":457,"line":23597},161,[23599],{"type":21,"tag":455,"props":23600,"children":23601},{"style":486},[23602],{"type":26,"value":23603},"})(self.fetch);\n",{"type":21,"tag":1279,"props":23605,"children":23606},{},[23607],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":23609},[23610,23611,23612,23613,23614],{"id":19874,"depth":333,"text":19877},{"id":19893,"depth":333,"text":19896},{"id":19956,"depth":333,"text":19959},{"id":19976,"depth":333,"text":19979},{"id":20983,"depth":333,"text":20986},"content:ckeefer:2016-7:goFetch2.md","ckeefer/2016-7/goFetch2.md","ckeefer/2016-7/goFetch2",{"user":16604,"name":16605},{"_path":23620,"_dir":23621,"_draft":7,"_partial":7,"_locale":8,"title":23622,"description":23623,"publishDate":23624,"tags":23625,"excerpt":23623,"body":23626,"_type":343,"_id":24993,"_source":345,"_file":24994,"_stem":24995,"_extension":348,"author":24996},"/ckeefer/2016-6/gofetch1","2016-6","Go Fetch! (JavaScript Fetch API)","Long ago, we briefly brushed upon the topic of what has made jQuery such a valuable part of the web developer's toolset for such a long time - namely, a cleaner interface for interacting with the DOM, and the $.ajax abstraction over XMLHttpRequest.","2016-10-03",[2804,19859,15],{"type":18,"children":23627,"toc":24985},[23628,23639,23666,23671,23677,23682,23689,24009,24042,24047,24231,24243,24247,24445,24467,24472,24478,24532,24537,24543,24577,24583,24588,24897,24910,24923,24928,24933,24939,24944,24950,24981],{"type":21,"tag":22,"props":23629,"children":23630},{},[23631,23637],{"type":21,"tag":322,"props":23632,"children":23634},{"href":23633},"/search/jquery/ajax/blobs/user:ckeefer",[23635],{"type":26,"value":23636},"Long ago",{"type":26,"value":23638},", we briefly brushed upon the topic of what has made jQuery such a valuable part of the web developer's toolset for such a long time - namely, a cleaner interface for interacting with the DOM, and the $.ajax abstraction over XMLHttpRequest.",{"type":21,"tag":22,"props":23640,"children":23641},{},[23642,23644,23650,23651,23657,23659,23665],{"type":26,"value":23643},"These days, I would go a step farther and discuss how it has positively influenced browser APIs. jQuery offered a way to find elements using their css selectors, and this eventually gave us ",{"type":21,"tag":246,"props":23645,"children":23647},{"className":23646},[],[23648],{"type":26,"value":23649},"document.querySelector",{"type":26,"value":386},{"type":21,"tag":246,"props":23652,"children":23654},{"className":23653},[],[23655],{"type":26,"value":23656},"document.querySelectorAll",{"type":26,"value":23658},". More recently, browser developers have taken another page from jQuery's playbook and introduced a new, Promise-based API for making asynchronous requests, and so much more - ",{"type":21,"tag":322,"props":23660,"children":23663},{"href":23661,"rel":23662},"https://developer.mozilla.org/en/docs/Web/API/Fetch_API",[326],[23664],{"type":26,"value":19761},{"type":26,"value":551},{"type":21,"tag":22,"props":23667,"children":23668},{},[23669],{"type":26,"value":23670},"Why go Fetch? Let's take a look.",{"type":21,"tag":80,"props":23672,"children":23674},{"id":23673},"a-comparison",[23675],{"type":26,"value":23676},"A Comparison",{"type":21,"tag":22,"props":23678,"children":23679},{},[23680],{"type":26,"value":23681},"First, let's take a look at what a simple request using three different APIs might look like - first, our old friend XMLHttpRequest, then jQuery's ajax, and then Fetch.",{"type":21,"tag":23683,"props":23684,"children":23686},"h5",{"id":23685},"xmlhttprequest",[23687],{"type":26,"value":23688},"XMLHttpRequest",{"type":21,"tag":239,"props":23690,"children":23692},{"className":20016,"code":23691,"language":20018,"meta":8,"style":8},"var xhr = new XMLHttpRequest();\n\nxhr.addEventListener('load', function(){\n    var json = this.response || JSON.parse(this.responseText);\n    console.log(json);\n});\n\nxhr.addEventListener('error', function(err){\n    console.log(\"Oh dear, something went wrong: \", err);\n});\n\nxhr.responseType = 'json';\nxhr.open('get', '//jsonplaceholder.typicode.com/users', true);\nxhr.send();\n",[23693],{"type":21,"tag":246,"props":23694,"children":23695},{"__ignoreMap":8},[23696,23725,23732,23764,23822,23838,23845,23852,23893,23918,23925,23932,23953,23994],{"type":21,"tag":455,"props":23697,"children":23698},{"class":457,"line":458},[23699,23703,23708,23712,23716,23721],{"type":21,"tag":455,"props":23700,"children":23701},{"style":1349},[23702],{"type":26,"value":3183},{"type":21,"tag":455,"props":23704,"children":23705},{"style":486},[23706],{"type":26,"value":23707}," xhr ",{"type":21,"tag":455,"props":23709,"children":23710},{"style":1349},[23711],{"type":26,"value":3193},{"type":21,"tag":455,"props":23713,"children":23714},{"style":1349},[23715],{"type":26,"value":2183},{"type":21,"tag":455,"props":23717,"children":23718},{"style":1365},[23719],{"type":26,"value":23720}," XMLHttpRequest",{"type":21,"tag":455,"props":23722,"children":23723},{"style":486},[23724],{"type":26,"value":3398},{"type":21,"tag":455,"props":23726,"children":23727},{"class":457,"line":336},[23728],{"type":21,"tag":455,"props":23729,"children":23730},{"emptyLinePlaceholder":471},[23731],{"type":26,"value":474},{"type":21,"tag":455,"props":23733,"children":23734},{"class":457,"line":333},[23735,23740,23744,23748,23752,23756,23760],{"type":21,"tag":455,"props":23736,"children":23737},{"style":486},[23738],{"type":26,"value":23739},"xhr.",{"type":21,"tag":455,"props":23741,"children":23742},{"style":1365},[23743],{"type":26,"value":17999},{"type":21,"tag":455,"props":23745,"children":23746},{"style":486},[23747],{"type":26,"value":489},{"type":21,"tag":455,"props":23749,"children":23750},{"style":492},[23751],{"type":26,"value":18163},{"type":21,"tag":455,"props":23753,"children":23754},{"style":486},[23755],{"type":26,"value":2228},{"type":21,"tag":455,"props":23757,"children":23758},{"style":1349},[23759],{"type":26,"value":3897},{"type":21,"tag":455,"props":23761,"children":23762},{"style":486},[23763],{"type":26,"value":3902},{"type":21,"tag":455,"props":23765,"children":23766},{"class":457,"line":1424},[23767,23771,23776,23780,23785,23790,23794,23799,23803,23808,23812,23817],{"type":21,"tag":455,"props":23768,"children":23769},{"style":1349},[23770],{"type":26,"value":17508},{"type":21,"tag":455,"props":23772,"children":23773},{"style":486},[23774],{"type":26,"value":23775}," json ",{"type":21,"tag":455,"props":23777,"children":23778},{"style":1349},[23779],{"type":26,"value":3193},{"type":21,"tag":455,"props":23781,"children":23782},{"style":480},[23783],{"type":26,"value":23784}," this",{"type":21,"tag":455,"props":23786,"children":23787},{"style":486},[23788],{"type":26,"value":23789},".response ",{"type":21,"tag":455,"props":23791,"children":23792},{"style":1349},[23793],{"type":26,"value":12183},{"type":21,"tag":455,"props":23795,"children":23796},{"style":480},[23797],{"type":26,"value":23798}," JSON",{"type":21,"tag":455,"props":23800,"children":23801},{"style":486},[23802],{"type":26,"value":551},{"type":21,"tag":455,"props":23804,"children":23805},{"style":1365},[23806],{"type":26,"value":23807},"parse",{"type":21,"tag":455,"props":23809,"children":23810},{"style":486},[23811],{"type":26,"value":489},{"type":21,"tag":455,"props":23813,"children":23814},{"style":480},[23815],{"type":26,"value":23816},"this",{"type":21,"tag":455,"props":23818,"children":23819},{"style":486},[23820],{"type":26,"value":23821},".responseText);\n",{"type":21,"tag":455,"props":23823,"children":23824},{"class":457,"line":1443},[23825,23829,23833],{"type":21,"tag":455,"props":23826,"children":23827},{"style":486},[23828],{"type":26,"value":4602},{"type":21,"tag":455,"props":23830,"children":23831},{"style":1365},[23832],{"type":26,"value":3915},{"type":21,"tag":455,"props":23834,"children":23835},{"style":486},[23836],{"type":26,"value":23837},"(json);\n",{"type":21,"tag":455,"props":23839,"children":23840},{"class":457,"line":1489},[23841],{"type":21,"tag":455,"props":23842,"children":23843},{"style":486},[23844],{"type":26,"value":3952},{"type":21,"tag":455,"props":23846,"children":23847},{"class":457,"line":1506},[23848],{"type":21,"tag":455,"props":23849,"children":23850},{"emptyLinePlaceholder":471},[23851],{"type":26,"value":474},{"type":21,"tag":455,"props":23853,"children":23854},{"class":457,"line":1547},[23855,23859,23863,23867,23872,23876,23880,23884,23889],{"type":21,"tag":455,"props":23856,"children":23857},{"style":486},[23858],{"type":26,"value":23739},{"type":21,"tag":455,"props":23860,"children":23861},{"style":1365},[23862],{"type":26,"value":17999},{"type":21,"tag":455,"props":23864,"children":23865},{"style":486},[23866],{"type":26,"value":489},{"type":21,"tag":455,"props":23868,"children":23869},{"style":492},[23870],{"type":26,"value":23871},"'error'",{"type":21,"tag":455,"props":23873,"children":23874},{"style":486},[23875],{"type":26,"value":2228},{"type":21,"tag":455,"props":23877,"children":23878},{"style":1349},[23879],{"type":26,"value":3897},{"type":21,"tag":455,"props":23881,"children":23882},{"style":486},[23883],{"type":26,"value":489},{"type":21,"tag":455,"props":23885,"children":23886},{"style":4527},[23887],{"type":26,"value":23888},"err",{"type":21,"tag":455,"props":23890,"children":23891},{"style":486},[23892],{"type":26,"value":7363},{"type":21,"tag":455,"props":23894,"children":23895},{"class":457,"line":1564},[23896,23900,23904,23908,23913],{"type":21,"tag":455,"props":23897,"children":23898},{"style":486},[23899],{"type":26,"value":4602},{"type":21,"tag":455,"props":23901,"children":23902},{"style":1365},[23903],{"type":26,"value":3915},{"type":21,"tag":455,"props":23905,"children":23906},{"style":486},[23907],{"type":26,"value":489},{"type":21,"tag":455,"props":23909,"children":23910},{"style":492},[23911],{"type":26,"value":23912},"\"Oh dear, something went wrong: \"",{"type":21,"tag":455,"props":23914,"children":23915},{"style":486},[23916],{"type":26,"value":23917},", err);\n",{"type":21,"tag":455,"props":23919,"children":23920},{"class":457,"line":1605},[23921],{"type":21,"tag":455,"props":23922,"children":23923},{"style":486},[23924],{"type":26,"value":3952},{"type":21,"tag":455,"props":23926,"children":23927},{"class":457,"line":1622},[23928],{"type":21,"tag":455,"props":23929,"children":23930},{"emptyLinePlaceholder":471},[23931],{"type":26,"value":474},{"type":21,"tag":455,"props":23933,"children":23934},{"class":457,"line":1663},[23935,23940,23944,23949],{"type":21,"tag":455,"props":23936,"children":23937},{"style":486},[23938],{"type":26,"value":23939},"xhr.responseType ",{"type":21,"tag":455,"props":23941,"children":23942},{"style":1349},[23943],{"type":26,"value":3193},{"type":21,"tag":455,"props":23945,"children":23946},{"style":492},[23947],{"type":26,"value":23948}," 'json'",{"type":21,"tag":455,"props":23950,"children":23951},{"style":486},[23952],{"type":26,"value":1440},{"type":21,"tag":455,"props":23954,"children":23955},{"class":457,"line":1680},[23956,23960,23964,23968,23973,23977,23982,23986,23990],{"type":21,"tag":455,"props":23957,"children":23958},{"style":486},[23959],{"type":26,"value":23739},{"type":21,"tag":455,"props":23961,"children":23962},{"style":1365},[23963],{"type":26,"value":18716},{"type":21,"tag":455,"props":23965,"children":23966},{"style":486},[23967],{"type":26,"value":489},{"type":21,"tag":455,"props":23969,"children":23970},{"style":492},[23971],{"type":26,"value":23972},"'get'",{"type":21,"tag":455,"props":23974,"children":23975},{"style":486},[23976],{"type":26,"value":2228},{"type":21,"tag":455,"props":23978,"children":23979},{"style":492},[23980],{"type":26,"value":23981},"'//jsonplaceholder.typicode.com/users'",{"type":21,"tag":455,"props":23983,"children":23984},{"style":486},[23985],{"type":26,"value":2228},{"type":21,"tag":455,"props":23987,"children":23988},{"style":480},[23989],{"type":26,"value":3679},{"type":21,"tag":455,"props":23991,"children":23992},{"style":486},[23993],{"type":26,"value":2171},{"type":21,"tag":455,"props":23995,"children":23996},{"class":457,"line":1721},[23997,24001,24005],{"type":21,"tag":455,"props":23998,"children":23999},{"style":486},[24000],{"type":26,"value":23739},{"type":21,"tag":455,"props":24002,"children":24003},{"style":1365},[24004],{"type":26,"value":5994},{"type":21,"tag":455,"props":24006,"children":24007},{"style":486},[24008],{"type":26,"value":3398},{"type":21,"tag":22,"props":24010,"children":24011},{},[24012,24014,24019,24021,24027,24028,24033,24035,24040],{"type":26,"value":24013},"Not terrible, of course, but a little more verbose than we might like, and we don't get the nice, logical progression of ",{"type":21,"tag":33,"props":24015,"children":24016},{},[24017],{"type":26,"value":24018},"this-then-that",{"type":26,"value":24020}," in our code, since we have to assign our ",{"type":21,"tag":246,"props":24022,"children":24024},{"className":24023},[],[24025],{"type":26,"value":24026},"load",{"type":26,"value":386},{"type":21,"tag":246,"props":24029,"children":24031},{"className":24030},[],[24032],{"type":26,"value":17792},{"type":26,"value":24034}," event handlers before we make the ",{"type":21,"tag":246,"props":24036,"children":24038},{"className":24037},[],[24039],{"type":26,"value":5994},{"type":26,"value":24041}," call.",{"type":21,"tag":23683,"props":24043,"children":24044},{"id":19859},[24045],{"type":26,"value":24046},"jQuery",{"type":21,"tag":239,"props":24048,"children":24050},{"className":20016,"code":24049,"language":20018,"meta":8,"style":8},"$.ajax({\n    type:'GET',\n    url:'//jsonplaceholder.typicode.com/users',\n    dataType:'json'\n}).done(function(data){ \n    console.log(data); \n}).fail(function(jqXHR){\n    console.log(\"Oh dear, something went wrong: \", jqXHR.status, jqXHR.responseText);\n});\n",[24051],{"type":21,"tag":246,"props":24052,"children":24053},{"__ignoreMap":8},[24054,24071,24087,24103,24116,24151,24167,24200,24224],{"type":21,"tag":455,"props":24055,"children":24056},{"class":457,"line":458},[24057,24062,24067],{"type":21,"tag":455,"props":24058,"children":24059},{"style":486},[24060],{"type":26,"value":24061},"$.",{"type":21,"tag":455,"props":24063,"children":24064},{"style":1365},[24065],{"type":26,"value":24066},"ajax",{"type":21,"tag":455,"props":24068,"children":24069},{"style":486},[24070],{"type":26,"value":7075},{"type":21,"tag":455,"props":24072,"children":24073},{"class":457,"line":336},[24074,24079,24083],{"type":21,"tag":455,"props":24075,"children":24076},{"style":486},[24077],{"type":26,"value":24078},"    type:",{"type":21,"tag":455,"props":24080,"children":24081},{"style":492},[24082],{"type":26,"value":7088},{"type":21,"tag":455,"props":24084,"children":24085},{"style":486},[24086],{"type":26,"value":2483},{"type":21,"tag":455,"props":24088,"children":24089},{"class":457,"line":333},[24090,24095,24099],{"type":21,"tag":455,"props":24091,"children":24092},{"style":486},[24093],{"type":26,"value":24094},"    url:",{"type":21,"tag":455,"props":24096,"children":24097},{"style":492},[24098],{"type":26,"value":23981},{"type":21,"tag":455,"props":24100,"children":24101},{"style":486},[24102],{"type":26,"value":2483},{"type":21,"tag":455,"props":24104,"children":24105},{"class":457,"line":1424},[24106,24111],{"type":21,"tag":455,"props":24107,"children":24108},{"style":486},[24109],{"type":26,"value":24110},"    dataType:",{"type":21,"tag":455,"props":24112,"children":24113},{"style":492},[24114],{"type":26,"value":24115},"'json'\n",{"type":21,"tag":455,"props":24117,"children":24118},{"class":457,"line":1443},[24119,24124,24129,24133,24137,24141,24146],{"type":21,"tag":455,"props":24120,"children":24121},{"style":486},[24122],{"type":26,"value":24123},"}).",{"type":21,"tag":455,"props":24125,"children":24126},{"style":1365},[24127],{"type":26,"value":24128},"done",{"type":21,"tag":455,"props":24130,"children":24131},{"style":486},[24132],{"type":26,"value":489},{"type":21,"tag":455,"props":24134,"children":24135},{"style":1349},[24136],{"type":26,"value":3897},{"type":21,"tag":455,"props":24138,"children":24139},{"style":486},[24140],{"type":26,"value":489},{"type":21,"tag":455,"props":24142,"children":24143},{"style":4527},[24144],{"type":26,"value":24145},"data",{"type":21,"tag":455,"props":24147,"children":24148},{"style":486},[24149],{"type":26,"value":24150},"){ \n",{"type":21,"tag":455,"props":24152,"children":24153},{"class":457,"line":1489},[24154,24158,24162],{"type":21,"tag":455,"props":24155,"children":24156},{"style":486},[24157],{"type":26,"value":4602},{"type":21,"tag":455,"props":24159,"children":24160},{"style":1365},[24161],{"type":26,"value":3915},{"type":21,"tag":455,"props":24163,"children":24164},{"style":486},[24165],{"type":26,"value":24166},"(data); \n",{"type":21,"tag":455,"props":24168,"children":24169},{"class":457,"line":1506},[24170,24174,24179,24183,24187,24191,24196],{"type":21,"tag":455,"props":24171,"children":24172},{"style":486},[24173],{"type":26,"value":24123},{"type":21,"tag":455,"props":24175,"children":24176},{"style":1365},[24177],{"type":26,"value":24178},"fail",{"type":21,"tag":455,"props":24180,"children":24181},{"style":486},[24182],{"type":26,"value":489},{"type":21,"tag":455,"props":24184,"children":24185},{"style":1349},[24186],{"type":26,"value":3897},{"type":21,"tag":455,"props":24188,"children":24189},{"style":486},[24190],{"type":26,"value":489},{"type":21,"tag":455,"props":24192,"children":24193},{"style":4527},[24194],{"type":26,"value":24195},"jqXHR",{"type":21,"tag":455,"props":24197,"children":24198},{"style":486},[24199],{"type":26,"value":7363},{"type":21,"tag":455,"props":24201,"children":24202},{"class":457,"line":1547},[24203,24207,24211,24215,24219],{"type":21,"tag":455,"props":24204,"children":24205},{"style":486},[24206],{"type":26,"value":4602},{"type":21,"tag":455,"props":24208,"children":24209},{"style":1365},[24210],{"type":26,"value":3915},{"type":21,"tag":455,"props":24212,"children":24213},{"style":486},[24214],{"type":26,"value":489},{"type":21,"tag":455,"props":24216,"children":24217},{"style":492},[24218],{"type":26,"value":23912},{"type":21,"tag":455,"props":24220,"children":24221},{"style":486},[24222],{"type":26,"value":24223},", jqXHR.status, jqXHR.responseText);\n",{"type":21,"tag":455,"props":24225,"children":24226},{"class":457,"line":1564},[24227],{"type":21,"tag":455,"props":24228,"children":24229},{"style":486},[24230],{"type":26,"value":3952},{"type":21,"tag":22,"props":24232,"children":24233},{},[24234,24236,24241],{"type":26,"value":24235},"Ah, much nicer - we can specify all the parameters to the ajax call within a single settings object, and with Deferred support baked in since v1.4, we can dangle a ",{"type":21,"tag":246,"props":24237,"children":24239},{"className":24238},[],[24240],{"type":26,"value":24128},{"type":26,"value":24242}," block after our call, keeping the logic more compact and readable.",{"type":21,"tag":23683,"props":24244,"children":24245},{"id":19758},[24246],{"type":26,"value":19761},{"type":21,"tag":239,"props":24248,"children":24250},{"className":20016,"code":24249,"language":20018,"meta":8,"style":8},"fetch('//jsonplaceholder.typicode.com/users', {\n    method:'GET'\n}).then((response) => {\n    return response.json();\n}).then((json) => {\n    console.log(json);\n}).catch((err) => {\n    console.log(\"Oh dear, something went wrong: \", err);\n});;\n",[24251],{"type":21,"tag":246,"props":24252,"children":24253},{"__ignoreMap":8},[24254,24274,24287,24318,24337,24368,24383,24414,24437],{"type":21,"tag":455,"props":24255,"children":24256},{"class":457,"line":458},[24257,24261,24265,24269],{"type":21,"tag":455,"props":24258,"children":24259},{"style":1365},[24260],{"type":26,"value":19758},{"type":21,"tag":455,"props":24262,"children":24263},{"style":486},[24264],{"type":26,"value":489},{"type":21,"tag":455,"props":24266,"children":24267},{"style":492},[24268],{"type":26,"value":23981},{"type":21,"tag":455,"props":24270,"children":24271},{"style":486},[24272],{"type":26,"value":24273},", {\n",{"type":21,"tag":455,"props":24275,"children":24276},{"class":457,"line":336},[24277,24282],{"type":21,"tag":455,"props":24278,"children":24279},{"style":486},[24280],{"type":26,"value":24281},"    method:",{"type":21,"tag":455,"props":24283,"children":24284},{"style":492},[24285],{"type":26,"value":24286},"'GET'\n",{"type":21,"tag":455,"props":24288,"children":24289},{"class":457,"line":333},[24290,24294,24298,24302,24306,24310,24314],{"type":21,"tag":455,"props":24291,"children":24292},{"style":486},[24293],{"type":26,"value":24123},{"type":21,"tag":455,"props":24295,"children":24296},{"style":1365},[24297],{"type":26,"value":4520},{"type":21,"tag":455,"props":24299,"children":24300},{"style":486},[24301],{"type":26,"value":4575},{"type":21,"tag":455,"props":24303,"children":24304},{"style":4527},[24305],{"type":26,"value":19617},{"type":21,"tag":455,"props":24307,"children":24308},{"style":486},[24309],{"type":26,"value":4584},{"type":21,"tag":455,"props":24311,"children":24312},{"style":1349},[24313],{"type":26,"value":4589},{"type":21,"tag":455,"props":24315,"children":24316},{"style":486},[24317],{"type":26,"value":2470},{"type":21,"tag":455,"props":24319,"children":24320},{"class":457,"line":1424},[24321,24325,24329,24333],{"type":21,"tag":455,"props":24322,"children":24323},{"style":1349},[24324],{"type":26,"value":1984},{"type":21,"tag":455,"props":24326,"children":24327},{"style":486},[24328],{"type":26,"value":10279},{"type":21,"tag":455,"props":24330,"children":24331},{"style":1365},[24332],{"type":26,"value":3544},{"type":21,"tag":455,"props":24334,"children":24335},{"style":486},[24336],{"type":26,"value":3398},{"type":21,"tag":455,"props":24338,"children":24339},{"class":457,"line":1443},[24340,24344,24348,24352,24356,24360,24364],{"type":21,"tag":455,"props":24341,"children":24342},{"style":486},[24343],{"type":26,"value":24123},{"type":21,"tag":455,"props":24345,"children":24346},{"style":1365},[24347],{"type":26,"value":4520},{"type":21,"tag":455,"props":24349,"children":24350},{"style":486},[24351],{"type":26,"value":4575},{"type":21,"tag":455,"props":24353,"children":24354},{"style":4527},[24355],{"type":26,"value":3544},{"type":21,"tag":455,"props":24357,"children":24358},{"style":486},[24359],{"type":26,"value":4584},{"type":21,"tag":455,"props":24361,"children":24362},{"style":1349},[24363],{"type":26,"value":4589},{"type":21,"tag":455,"props":24365,"children":24366},{"style":486},[24367],{"type":26,"value":2470},{"type":21,"tag":455,"props":24369,"children":24370},{"class":457,"line":1489},[24371,24375,24379],{"type":21,"tag":455,"props":24372,"children":24373},{"style":486},[24374],{"type":26,"value":4602},{"type":21,"tag":455,"props":24376,"children":24377},{"style":1365},[24378],{"type":26,"value":3915},{"type":21,"tag":455,"props":24380,"children":24381},{"style":486},[24382],{"type":26,"value":23837},{"type":21,"tag":455,"props":24384,"children":24385},{"class":457,"line":1506},[24386,24390,24394,24398,24402,24406,24410],{"type":21,"tag":455,"props":24387,"children":24388},{"style":486},[24389],{"type":26,"value":24123},{"type":21,"tag":455,"props":24391,"children":24392},{"style":1365},[24393],{"type":26,"value":4638},{"type":21,"tag":455,"props":24395,"children":24396},{"style":486},[24397],{"type":26,"value":4575},{"type":21,"tag":455,"props":24399,"children":24400},{"style":4527},[24401],{"type":26,"value":23888},{"type":21,"tag":455,"props":24403,"children":24404},{"style":486},[24405],{"type":26,"value":4584},{"type":21,"tag":455,"props":24407,"children":24408},{"style":1349},[24409],{"type":26,"value":4589},{"type":21,"tag":455,"props":24411,"children":24412},{"style":486},[24413],{"type":26,"value":2470},{"type":21,"tag":455,"props":24415,"children":24416},{"class":457,"line":1547},[24417,24421,24425,24429,24433],{"type":21,"tag":455,"props":24418,"children":24419},{"style":486},[24420],{"type":26,"value":4602},{"type":21,"tag":455,"props":24422,"children":24423},{"style":1365},[24424],{"type":26,"value":3915},{"type":21,"tag":455,"props":24426,"children":24427},{"style":486},[24428],{"type":26,"value":489},{"type":21,"tag":455,"props":24430,"children":24431},{"style":492},[24432],{"type":26,"value":23912},{"type":21,"tag":455,"props":24434,"children":24435},{"style":486},[24436],{"type":26,"value":23917},{"type":21,"tag":455,"props":24438,"children":24439},{"class":457,"line":1564},[24440],{"type":21,"tag":455,"props":24441,"children":24442},{"style":486},[24443],{"type":26,"value":24444},"});;\n",{"type":21,"tag":22,"props":24446,"children":24447},{},[24448,24450,24457,24458,24465],{"type":26,"value":24449},"You can see the similarities to the $.ajax example, but the fetch API manages to be both a tad more compact - in part thanks to ",{"type":21,"tag":322,"props":24451,"children":24454},{"href":24452,"rel":24453},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise",[326],[24455],{"type":26,"value":24456},"Promises",{"type":26,"value":386},{"type":21,"tag":322,"props":24459,"children":24462},{"href":24460,"rel":24461},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions",[326],[24463],{"type":26,"value":24464},"Arrow Functions",{"type":26,"value":24466}," - and more powerful in a variety of ways.",{"type":21,"tag":22,"props":24468,"children":24469},{},[24470],{"type":26,"value":24471},"For instance, let's talk a little about the Response readers, and the Response and Request objects.",{"type":21,"tag":80,"props":24473,"children":24475},{"id":24474},"response-readers",[24476],{"type":26,"value":24477},"Response Readers",{"type":21,"tag":22,"props":24479,"children":24480},{},[24481,24483,24488,24490,24496,24498,24503,24504,24509,24510,24516,24517,24523,24524,24530],{"type":26,"value":24482},"You'll notice in the example above that our initial ",{"type":21,"tag":246,"props":24484,"children":24486},{"className":24485},[],[24487],{"type":26,"value":4520},{"type":26,"value":24489}," block returns a call to .json() on the response. What's happening here is that the response is presented to us as a stream. We could read this stream manually if we wanted to, via ",{"type":21,"tag":246,"props":24491,"children":24493},{"className":24492},[],[24494],{"type":26,"value":24495},"response.body.getReader()",{"type":26,"value":24497},", but for most purposes, we can rely on one of the built in readers: ",{"type":21,"tag":246,"props":24499,"children":24501},{"className":24500},[],[24502],{"type":26,"value":26},{"type":26,"value":2228},{"type":21,"tag":246,"props":24505,"children":24507},{"className":24506},[],[24508],{"type":26,"value":3544},{"type":26,"value":2228},{"type":21,"tag":246,"props":24511,"children":24513},{"className":24512},[],[24514],{"type":26,"value":24515},"formData",{"type":26,"value":2228},{"type":21,"tag":246,"props":24518,"children":24520},{"className":24519},[],[24521],{"type":26,"value":24522},"blob",{"type":26,"value":386},{"type":21,"tag":246,"props":24525,"children":24527},{"className":24526},[],[24528],{"type":26,"value":24529},"arrayBuffer",{"type":26,"value":24531},". These readers drain the stream and return the value decoded as one might expect in the form of the specified reader - i.e. using the blob reader on the stream will return its value decoded as a blob object.",{"type":21,"tag":22,"props":24533,"children":24534},{},[24535],{"type":26,"value":24536},"This is neat, because it offers us the opportunity to consider how we want to decode any given return - no hacks on top of responseText here. One gotcha to be mindful of, though, is that the readers do 'drain the stream', as I noted above, so if you were to return the response to a future then block, and attempt to read again, that read would fail. We'll talk more about this point next time.",{"type":21,"tag":80,"props":24538,"children":24540},{"id":24539},"response-request",[24541],{"type":26,"value":24542},"Response & Request",{"type":21,"tag":22,"props":24544,"children":24545},{},[24546,24548,24553,24554,24560,24562,24568,24569,24575],{"type":26,"value":24547},"Another nicety of the Fetch api is that we finally gain access to the ",{"type":21,"tag":246,"props":24549,"children":24551},{"className":24550},[],[24552],{"type":26,"value":20978},{"type":26,"value":386},{"type":21,"tag":246,"props":24555,"children":24557},{"className":24556},[],[24558],{"type":26,"value":24559},"Request",{"type":26,"value":24561}," primitives that underlie these requests, allowing us to form our own ",{"type":21,"tag":322,"props":24563,"children":24566},{"href":24564,"rel":24565},"https://developer.mozilla.org/en-US/docs/Web/API/Response",[326],[24567],{"type":26,"value":20978},{"type":26,"value":386},{"type":21,"tag":322,"props":24570,"children":24573},{"href":24571,"rel":24572},"https://developer.mozilla.org/en-US/docs/Web/API/Request",[326],[24574],{"type":26,"value":24559},{"type":26,"value":24576}," objects. This isn't likely to come up very often (especially outside of the context of a web worker), but we'll also be taking a look at how we can use the Response object to produce our own streamable response from cached values next time.",{"type":21,"tag":80,"props":24578,"children":24580},{"id":24579},"gotchas",[24581],{"type":26,"value":24582},"Gotchas",{"type":21,"tag":22,"props":24584,"children":24585},{},[24586],{"type":26,"value":24587},"One of the differences between XMLHttpRequest (and thus $.ajax), and fetch is that network returns that indicate failure, like a 400 or 500 response, won't be considered errors by your fetch promise - if you want them to be errors that are handled in the catch block, you'll want to check for them and throw them yourself:",{"type":21,"tag":239,"props":24589,"children":24591},{"className":20016,"code":24590,"language":20018,"meta":8,"style":8},"fetch('//jsonplaceholder.typicode.com/users', {\n    method:'GET',\n    credentials:'omit',\n    headers:{\n        'Content-Type': 'application/json'\n    },\n    mode:'cors',\n    cache:'no-cache',\n    referrer:'no-referrer'\n}).then((response) => {\n    let status = response.status;\n\n    if (status \u003C 200 || status > 299) throw new Error(status);\n\n}).catch((error) => {\n    // Catch our network error here\n});\n",[24592],{"type":21,"tag":246,"props":24593,"children":24594},{"__ignoreMap":8},[24595,24614,24629,24646,24654,24670,24677,24693,24710,24723,24754,24776,24783,24844,24851,24882,24890],{"type":21,"tag":455,"props":24596,"children":24597},{"class":457,"line":458},[24598,24602,24606,24610],{"type":21,"tag":455,"props":24599,"children":24600},{"style":1365},[24601],{"type":26,"value":19758},{"type":21,"tag":455,"props":24603,"children":24604},{"style":486},[24605],{"type":26,"value":489},{"type":21,"tag":455,"props":24607,"children":24608},{"style":492},[24609],{"type":26,"value":23981},{"type":21,"tag":455,"props":24611,"children":24612},{"style":486},[24613],{"type":26,"value":24273},{"type":21,"tag":455,"props":24615,"children":24616},{"class":457,"line":336},[24617,24621,24625],{"type":21,"tag":455,"props":24618,"children":24619},{"style":486},[24620],{"type":26,"value":24281},{"type":21,"tag":455,"props":24622,"children":24623},{"style":492},[24624],{"type":26,"value":7088},{"type":21,"tag":455,"props":24626,"children":24627},{"style":486},[24628],{"type":26,"value":2483},{"type":21,"tag":455,"props":24630,"children":24631},{"class":457,"line":333},[24632,24637,24642],{"type":21,"tag":455,"props":24633,"children":24634},{"style":486},[24635],{"type":26,"value":24636},"    credentials:",{"type":21,"tag":455,"props":24638,"children":24639},{"style":492},[24640],{"type":26,"value":24641},"'omit'",{"type":21,"tag":455,"props":24643,"children":24644},{"style":486},[24645],{"type":26,"value":2483},{"type":21,"tag":455,"props":24647,"children":24648},{"class":457,"line":1424},[24649],{"type":21,"tag":455,"props":24650,"children":24651},{"style":486},[24652],{"type":26,"value":24653},"    headers:{\n",{"type":21,"tag":455,"props":24655,"children":24656},{"class":457,"line":1443},[24657,24661,24665],{"type":21,"tag":455,"props":24658,"children":24659},{"style":492},[24660],{"type":26,"value":10055},{"type":21,"tag":455,"props":24662,"children":24663},{"style":486},[24664],{"type":26,"value":7129},{"type":21,"tag":455,"props":24666,"children":24667},{"style":492},[24668],{"type":26,"value":24669},"'application/json'\n",{"type":21,"tag":455,"props":24671,"children":24672},{"class":457,"line":1489},[24673],{"type":21,"tag":455,"props":24674,"children":24675},{"style":486},[24676],{"type":26,"value":5854},{"type":21,"tag":455,"props":24678,"children":24679},{"class":457,"line":1506},[24680,24685,24689],{"type":21,"tag":455,"props":24681,"children":24682},{"style":486},[24683],{"type":26,"value":24684},"    mode:",{"type":21,"tag":455,"props":24686,"children":24687},{"style":492},[24688],{"type":26,"value":3273},{"type":21,"tag":455,"props":24690,"children":24691},{"style":486},[24692],{"type":26,"value":2483},{"type":21,"tag":455,"props":24694,"children":24695},{"class":457,"line":1547},[24696,24701,24706],{"type":21,"tag":455,"props":24697,"children":24698},{"style":486},[24699],{"type":26,"value":24700},"    cache:",{"type":21,"tag":455,"props":24702,"children":24703},{"style":492},[24704],{"type":26,"value":24705},"'no-cache'",{"type":21,"tag":455,"props":24707,"children":24708},{"style":486},[24709],{"type":26,"value":2483},{"type":21,"tag":455,"props":24711,"children":24712},{"class":457,"line":1564},[24713,24718],{"type":21,"tag":455,"props":24714,"children":24715},{"style":486},[24716],{"type":26,"value":24717},"    referrer:",{"type":21,"tag":455,"props":24719,"children":24720},{"style":492},[24721],{"type":26,"value":24722},"'no-referrer'\n",{"type":21,"tag":455,"props":24724,"children":24725},{"class":457,"line":1605},[24726,24730,24734,24738,24742,24746,24750],{"type":21,"tag":455,"props":24727,"children":24728},{"style":486},[24729],{"type":26,"value":24123},{"type":21,"tag":455,"props":24731,"children":24732},{"style":1365},[24733],{"type":26,"value":4520},{"type":21,"tag":455,"props":24735,"children":24736},{"style":486},[24737],{"type":26,"value":4575},{"type":21,"tag":455,"props":24739,"children":24740},{"style":4527},[24741],{"type":26,"value":19617},{"type":21,"tag":455,"props":24743,"children":24744},{"style":486},[24745],{"type":26,"value":4584},{"type":21,"tag":455,"props":24747,"children":24748},{"style":1349},[24749],{"type":26,"value":4589},{"type":21,"tag":455,"props":24751,"children":24752},{"style":486},[24753],{"type":26,"value":2470},{"type":21,"tag":455,"props":24755,"children":24756},{"class":457,"line":1622},[24757,24762,24767,24771],{"type":21,"tag":455,"props":24758,"children":24759},{"style":1349},[24760],{"type":26,"value":24761},"    let",{"type":21,"tag":455,"props":24763,"children":24764},{"style":486},[24765],{"type":26,"value":24766}," status ",{"type":21,"tag":455,"props":24768,"children":24769},{"style":1349},[24770],{"type":26,"value":3193},{"type":21,"tag":455,"props":24772,"children":24773},{"style":486},[24774],{"type":26,"value":24775}," response.status;\n",{"type":21,"tag":455,"props":24777,"children":24778},{"class":457,"line":1663},[24779],{"type":21,"tag":455,"props":24780,"children":24781},{"emptyLinePlaceholder":471},[24782],{"type":26,"value":474},{"type":21,"tag":455,"props":24784,"children":24785},{"class":457,"line":1680},[24786,24790,24795,24799,24804,24808,24812,24816,24821,24825,24830,24834,24839],{"type":21,"tag":455,"props":24787,"children":24788},{"style":1349},[24789],{"type":26,"value":1402},{"type":21,"tag":455,"props":24791,"children":24792},{"style":486},[24793],{"type":26,"value":24794}," (status ",{"type":21,"tag":455,"props":24796,"children":24797},{"style":1349},[24798],{"type":26,"value":11627},{"type":21,"tag":455,"props":24800,"children":24801},{"style":480},[24802],{"type":26,"value":24803}," 200",{"type":21,"tag":455,"props":24805,"children":24806},{"style":1349},[24807],{"type":26,"value":3883},{"type":21,"tag":455,"props":24809,"children":24810},{"style":486},[24811],{"type":26,"value":24766},{"type":21,"tag":455,"props":24813,"children":24814},{"style":1349},[24815],{"type":26,"value":1457},{"type":21,"tag":455,"props":24817,"children":24818},{"style":480},[24819],{"type":26,"value":24820}," 299",{"type":21,"tag":455,"props":24822,"children":24823},{"style":486},[24824],{"type":26,"value":4584},{"type":21,"tag":455,"props":24826,"children":24827},{"style":1349},[24828],{"type":26,"value":24829},"throw",{"type":21,"tag":455,"props":24831,"children":24832},{"style":1349},[24833],{"type":26,"value":2183},{"type":21,"tag":455,"props":24835,"children":24836},{"style":1365},[24837],{"type":26,"value":24838}," Error",{"type":21,"tag":455,"props":24840,"children":24841},{"style":486},[24842],{"type":26,"value":24843},"(status);\n",{"type":21,"tag":455,"props":24845,"children":24846},{"class":457,"line":1721},[24847],{"type":21,"tag":455,"props":24848,"children":24849},{"emptyLinePlaceholder":471},[24850],{"type":26,"value":474},{"type":21,"tag":455,"props":24852,"children":24853},{"class":457,"line":1738},[24854,24858,24862,24866,24870,24874,24878],{"type":21,"tag":455,"props":24855,"children":24856},{"style":486},[24857],{"type":26,"value":24123},{"type":21,"tag":455,"props":24859,"children":24860},{"style":1365},[24861],{"type":26,"value":4638},{"type":21,"tag":455,"props":24863,"children":24864},{"style":486},[24865],{"type":26,"value":4575},{"type":21,"tag":455,"props":24867,"children":24868},{"style":4527},[24869],{"type":26,"value":17792},{"type":21,"tag":455,"props":24871,"children":24872},{"style":486},[24873],{"type":26,"value":4584},{"type":21,"tag":455,"props":24875,"children":24876},{"style":1349},[24877],{"type":26,"value":4589},{"type":21,"tag":455,"props":24879,"children":24880},{"style":486},[24881],{"type":26,"value":2470},{"type":21,"tag":455,"props":24883,"children":24884},{"class":457,"line":1779},[24885],{"type":21,"tag":455,"props":24886,"children":24887},{"style":462},[24888],{"type":26,"value":24889},"    // Catch our network error here\n",{"type":21,"tag":455,"props":24891,"children":24892},{"class":457,"line":1796},[24893],{"type":21,"tag":455,"props":24894,"children":24895},{"style":486},[24896],{"type":26,"value":3952},{"type":21,"tag":22,"props":24898,"children":24899},{},[24900,24902,24908],{"type":26,"value":24901},"You'll notice I also included a more comprehensive settings object this time. For the details on all the possible settings, I suggest checking out the links in the further reading section, below. For now, the one we really need to talk about is the ",{"type":21,"tag":246,"props":24903,"children":24905},{"className":24904},[],[24906],{"type":26,"value":24907},"credentials",{"type":26,"value":24909}," property.",{"type":21,"tag":22,"props":24911,"children":24912},{},[24913,24915,24921],{"type":26,"value":24914},"By default, XMLHttpRequest will send credentials to requests on the same-origin. Not so for fetch - 'omit' is the default value for credentials, so if you need to send cookies up to your server to authenticate a request, for instance, you'll need to be sure to specify ",{"type":21,"tag":246,"props":24916,"children":24918},{"className":24917},[],[24919],{"type":26,"value":24920},"credentials:'include'",{"type":26,"value":24922}," instead.",{"type":21,"tag":22,"props":24924,"children":24925},{},[24926],{"type":26,"value":24927},"Finally, at the moment there's no way to cancel a fetch request (this is in the works, and will probably coincide with being able to cancel a Promise), there's no progress events as there are in the XHR2 spec (although you can build your own, should you be so inclined), and there's no option for synchronous requests (which is good, and sync requests are deprecated and going to disappear in XHR at some future point as well).",{"type":21,"tag":22,"props":24929,"children":24930},{},[24931],{"type":26,"value":24932},"These potential gotchas are the usual cost of being on the sharp edge of emerging tech, and many will likely be smoothed over by browser developers or libraries that wrap the fetch functionality.",{"type":21,"tag":80,"props":24934,"children":24936},{"id":24935},"next-time",[24937],{"type":26,"value":24938},"Next Time",{"type":21,"tag":22,"props":24940,"children":24941},{},[24942],{"type":26,"value":24943},"Speaking of libraries that wrap the Fetch functionality, as I've been hinting, next time we'll take a deeper look into actually using the Fetch API, and present and dissect a library that will allow us to cache responses we've retrieved with fetch for bandwidth savings and offline usage. Stay tuned.",{"type":21,"tag":80,"props":24945,"children":24947},{"id":24946},"further-reading",[24948],{"type":26,"value":24949},"Further Reading",{"type":21,"tag":2953,"props":24951,"children":24952},{},[24953,24961,24971],{"type":21,"tag":187,"props":24954,"children":24955},{},[24956],{"type":21,"tag":322,"props":24957,"children":24959},{"href":23661,"rel":24958},[326],[24960],{"type":26,"value":19773},{"type":21,"tag":187,"props":24962,"children":24963},{},[24964],{"type":21,"tag":322,"props":24965,"children":24968},{"href":24966,"rel":24967},"https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en",[326],[24969],{"type":26,"value":24970},"Introduction to fetch",{"type":21,"tag":187,"props":24972,"children":24973},{},[24974],{"type":21,"tag":322,"props":24975,"children":24978},{"href":24976,"rel":24977},"https://jakearchibald.com/2015/thats-so-fetch/",[326],[24979],{"type":26,"value":24980},"That's so fetch",{"type":21,"tag":1279,"props":24982,"children":24983},{},[24984],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":24986},[24987,24988,24989,24990,24991,24992],{"id":23673,"depth":333,"text":23676},{"id":24474,"depth":333,"text":24477},{"id":24539,"depth":333,"text":24542},{"id":24579,"depth":333,"text":24582},{"id":24935,"depth":333,"text":24938},{"id":24946,"depth":333,"text":24949},"content:ckeefer:2016-6:goFetch1.md","ckeefer/2016-6/goFetch1.md","ckeefer/2016-6/goFetch1",{"user":16604,"name":16605},{"_path":24998,"_dir":24999,"_draft":7,"_partial":7,"_locale":8,"title":25000,"description":25001,"publishDate":25002,"tags":25003,"excerpt":25001,"body":25005,"_type":343,"_id":25505,"_source":345,"_file":25506,"_stem":25507,"_extension":348,"author":25508},"/avogan/2012-07/salt-2","2012-07","What Your Users Don't Know (Part 2)","In my last post we saw that what your users don't know can hurt them. In other words, how securely you handle your users' private data behind the scenes can have profound implications both for your business and your users' well being. To put it bluntly, it's bad for your business to be publicly shamed over your handling of sensitive data, and it's bad for your users to have their bank accounts pilfered -- those being some of the worse case scenarios.","2012-07-26",[25004,1302,15],"cryptography",{"type":18,"children":25006,"toc":25503},[25007,25019,25034,25039,25047,25052,25070,25078,25083,25091,25096,25201,25206,25211,25219,25224,25232,25237,25250,25274,25279,25371,25376,25381,25386,25394,25399,25442,25452,25460,25475,25483,25488,25493,25498],{"type":21,"tag":22,"props":25008,"children":25009},{},[25010,25012,25017],{"type":26,"value":25011},"In my last post we saw that what your users don't know ",{"type":21,"tag":2310,"props":25013,"children":25014},{},[25015],{"type":26,"value":25016},"can",{"type":26,"value":25018}," hurt them. In other words, how securely you handle your users' private data behind the scenes can have profound implications both for your business and your users' well being. To put it bluntly, it's bad for your business to be publicly shamed over your handling of sensitive data, and it's bad for your users to have their bank accounts pilfered -- those being some of the worse case scenarios.",{"type":21,"tag":22,"props":25020,"children":25021},{},[25022],{"type":21,"tag":33,"props":25023,"children":25024},{},[25025,25027,25033],{"type":26,"value":25026},"(This is part 2 of a series on web security; see ",{"type":21,"tag":322,"props":25028,"children":25030},{"href":25029},"/search/user:avogan/what/your/users/don't/know",[25031],{"type":26,"value":25032},"the whole series",{"type":26,"value":5081},{"type":21,"tag":22,"props":25035,"children":25036},{},[25037],{"type":26,"value":25038},"So today I'd like to resume our discussion of secure password storage. Let's put our black hat back on, and see what we can break.",{"type":21,"tag":22,"props":25040,"children":25041},{},[25042],{"type":21,"tag":2310,"props":25043,"children":25044},{},[25045],{"type":26,"value":25046},"Bad Assumptions",{"type":21,"tag":22,"props":25048,"children":25049},{},[25050],{"type":26,"value":25051},"I'll start with the easiest case. Sometimes developers assume that as long as their database is safely hidden behind a firewall and an ordinary web server, then it's OK to store everything in plaintext. But this is not true. There are many ways data can leak from your production database, including:",{"type":21,"tag":2953,"props":25053,"children":25054},{},[25055,25060,25065],{"type":21,"tag":187,"props":25056,"children":25057},{},[25058],{"type":26,"value":25059},"performing a SQL injection attack through your website",{"type":21,"tag":187,"props":25061,"children":25062},{},[25063],{"type":26,"value":25064},"digging through a backup or archive of your database -- therefore as long as you don't create backups, you're safe*",{"type":21,"tag":187,"props":25066,"children":25067},{},[25068],{"type":26,"value":25069},"gaining access to the server file system, e.g. through telnet/SSH/RDP",{"type":21,"tag":22,"props":25071,"children":25072},{},[25073],{"type":21,"tag":33,"props":25074,"children":25075},{},[25076],{"type":26,"value":25077},"* That was a joke.",{"type":21,"tag":22,"props":25079,"children":25080},{},[25081],{"type":26,"value":25082},"It is a good rule of thumb that you should design your database with the assumption that malicious users may gain unrestricted access to it at some point. But even if that happens, you should be prepared to breathe a sigh of (slightly nervous) relief, knowing that they still won't be able to use the information maliciously. That doesn't mean you have to encrypt everything, but you should definitely encrypt anything sensitive, such as credit card numbers, passwords, and so on.",{"type":21,"tag":22,"props":25084,"children":25085},{},[25086],{"type":21,"tag":2310,"props":25087,"children":25088},{},[25089],{"type":26,"value":25090},"Naïve Hashing",{"type":21,"tag":22,"props":25092,"children":25093},{},[25094],{"type":26,"value":25095},"A natural first step is to perform a one-way encryption, or \"hash\" on passwords so they are no longer readable in the database. Here's an example:",{"type":21,"tag":25097,"props":25098,"children":25099},"table",{},[25100,25119],{"type":21,"tag":25101,"props":25102,"children":25103},"thead",{},[25104],{"type":21,"tag":25105,"props":25106,"children":25107},"tr",{},[25108,25114],{"type":21,"tag":25109,"props":25110,"children":25111},"th",{},[25112],{"type":26,"value":25113},"UserName",{"type":21,"tag":25109,"props":25115,"children":25116},{},[25117],{"type":26,"value":25118},"PasswordHash",{"type":21,"tag":25120,"props":25121,"children":25122},"tbody",{},[25123,25137,25150,25163,25175,25188],{"type":21,"tag":25105,"props":25124,"children":25125},{},[25126,25132],{"type":21,"tag":25127,"props":25128,"children":25129},"td",{},[25130],{"type":26,"value":25131},"JSmith",{"type":21,"tag":25127,"props":25133,"children":25134},{},[25135],{"type":26,"value":25136},"0xF49A",{"type":21,"tag":25105,"props":25138,"children":25139},{},[25140,25145],{"type":21,"tag":25127,"props":25141,"children":25142},{},[25143],{"type":26,"value":25144},"AJones",{"type":21,"tag":25127,"props":25146,"children":25147},{},[25148],{"type":26,"value":25149},"0x923E",{"type":21,"tag":25105,"props":25151,"children":25152},{},[25153,25158],{"type":21,"tag":25127,"props":25154,"children":25155},{},[25156],{"type":26,"value":25157},"PHensley",{"type":21,"tag":25127,"props":25159,"children":25160},{},[25161],{"type":26,"value":25162},"0x14D0",{"type":21,"tag":25105,"props":25164,"children":25165},{},[25166,25171],{"type":21,"tag":25127,"props":25167,"children":25168},{},[25169],{"type":26,"value":25170},"MRandolph",{"type":21,"tag":25127,"props":25172,"children":25173},{},[25174],{"type":26,"value":25136},{"type":21,"tag":25105,"props":25176,"children":25177},{},[25178,25183],{"type":21,"tag":25127,"props":25179,"children":25180},{},[25181],{"type":26,"value":25182},"DGrove",{"type":21,"tag":25127,"props":25184,"children":25185},{},[25186],{"type":26,"value":25187},"0xE8DB",{"type":21,"tag":25105,"props":25189,"children":25190},{},[25191,25196],{"type":21,"tag":25127,"props":25192,"children":25193},{},[25194],{"type":26,"value":25195},"RSchneider",{"type":21,"tag":25127,"props":25197,"children":25198},{},[25199],{"type":26,"value":25200},"0x551E",{"type":21,"tag":22,"props":25202,"children":25203},{},[25204],{"type":26,"value":25205},"Do you see any problems with the above? OK, ignore the fact that the hashes are very small numbers. This is just pseudo-data for illustration.",{"type":21,"tag":22,"props":25207,"children":25208},{},[25209],{"type":26,"value":25210},"Observant readers will notice that one of the hashes occurs more than once (JSmith and MRandolph). Did you catch that? This is one of the problems with storing password hashes in your database - it's still very easy to see which users chose the same password. Remember, users won't protect themselves, and a surprising number of users may have a password of \"12345\" or \"password\" (or \"Password1\", just to anticipate and refute a well-intentioned, but ultimately insufficient attempt to solve this problem via a more strict password selection process).",{"type":21,"tag":22,"props":25212,"children":25213},{},[25214],{"type":21,"tag":2310,"props":25215,"children":25216},{},[25217],{"type":26,"value":25218},"Beware the Dictionary",{"type":21,"tag":22,"props":25220,"children":25221},{},[25222],{"type":26,"value":25223},"An even deeper problem here is that a hashing scheme like the above is susceptible to a dictionary attack using a large, pre-calculated collection of hashes of common passwords. All it takes is one successful match to positively identify the hashing scheme used, and then start doing damage.",{"type":21,"tag":22,"props":25225,"children":25226},{},[25227],{"type":21,"tag":2310,"props":25228,"children":25229},{},[25230],{"type":26,"value":25231},"Don't Follow This Recipe",{"type":21,"tag":22,"props":25233,"children":25234},{},[25235],{"type":26,"value":25236},"So we have to make sure the hashes we store are unique. We don't want an attacker to be able to recognize any of them. To do this, people use what's called a \"salt\" to make the output more random.",{"type":21,"tag":22,"props":25238,"children":25239},{},[25240],{"type":21,"tag":322,"props":25241,"children":25244},{"href":25242,"rel":25243},"https://artandlogic.com/wp-content/uploads/2012/07/potatohash1.jpg",[326],[25245],{"type":21,"tag":510,"props":25246,"children":25249},{"alt":25247,"src":25248,"title":25247},"Image of Potato Hash","/avogan/2012-07/img/potatohash.jpg",[],{"type":21,"tag":22,"props":25251,"children":25252},{},[25253,25258,25260],{"type":21,"tag":33,"props":25254,"children":25255},{},[25256],{"type":26,"value":25257},"A well-salted hash.",{"type":26,"value":25259}," ",{"type":21,"tag":33,"props":25261,"children":25262},{},[25263,25264,25272],{"type":26,"value":489},{"type":21,"tag":322,"props":25265,"children":25269},{"href":25266,"rel":25267,"title":25268},"http://www.flickr.com/photos/tavallai/7583913946/in/photostream/",[326],"Flickr photo source",[25270],{"type":26,"value":25271},"photo by Tavallai",{"type":26,"value":25273},", CC BY-ND 2.0)",{"type":21,"tag":22,"props":25275,"children":25276},{},[25277],{"type":26,"value":25278},"The salt is just a random number, and you can combine it with the hash process to get a more random looking output. Here's how one person did that, showing the same data from the table above, but with salt included. Pay special attention to the JSmith and MRandolph records, as before:",{"type":21,"tag":25097,"props":25280,"children":25281},{},[25282,25296],{"type":21,"tag":25101,"props":25283,"children":25284},{},[25285],{"type":21,"tag":25105,"props":25286,"children":25287},{},[25288,25292],{"type":21,"tag":25109,"props":25289,"children":25290},{},[25291],{"type":26,"value":25113},{"type":21,"tag":25109,"props":25293,"children":25294},{},[25295],{"type":26,"value":25118},{"type":21,"tag":25120,"props":25297,"children":25298},{},[25299,25311,25323,25335,25347,25359],{"type":21,"tag":25105,"props":25300,"children":25301},{},[25302,25306],{"type":21,"tag":25127,"props":25303,"children":25304},{},[25305],{"type":26,"value":25131},{"type":21,"tag":25127,"props":25307,"children":25308},{},[25309],{"type":26,"value":25310},"0x832B F49A",{"type":21,"tag":25105,"props":25312,"children":25313},{},[25314,25318],{"type":21,"tag":25127,"props":25315,"children":25316},{},[25317],{"type":26,"value":25144},{"type":21,"tag":25127,"props":25319,"children":25320},{},[25321],{"type":26,"value":25322},"0xDC3C 923E",{"type":21,"tag":25105,"props":25324,"children":25325},{},[25326,25330],{"type":21,"tag":25127,"props":25327,"children":25328},{},[25329],{"type":26,"value":25157},{"type":21,"tag":25127,"props":25331,"children":25332},{},[25333],{"type":26,"value":25334},"0x09A4 14D0",{"type":21,"tag":25105,"props":25336,"children":25337},{},[25338,25342],{"type":21,"tag":25127,"props":25339,"children":25340},{},[25341],{"type":26,"value":25170},{"type":21,"tag":25127,"props":25343,"children":25344},{},[25345],{"type":26,"value":25346},"0xF12E F49A",{"type":21,"tag":25105,"props":25348,"children":25349},{},[25350,25354],{"type":21,"tag":25127,"props":25351,"children":25352},{},[25353],{"type":26,"value":25182},{"type":21,"tag":25127,"props":25355,"children":25356},{},[25357],{"type":26,"value":25358},"0x4C88 E8DB",{"type":21,"tag":25105,"props":25360,"children":25361},{},[25362,25366],{"type":21,"tag":25127,"props":25363,"children":25364},{},[25365],{"type":26,"value":25195},{"type":21,"tag":25127,"props":25367,"children":25368},{},[25369],{"type":26,"value":25370},"0xA4FC 551E",{"type":21,"tag":22,"props":25372,"children":25373},{},[25374],{"type":26,"value":25375},"Whoa, wait a minute. Do you see a new problem here? It is true that each \"PasswordHash\" attribute is now unique since a random number has been prefixed. And the developers may run a few simple SQL queries and verify that no two PasswordHash attributes are the same, and pat themselves on the back. But that is merely a dangerous illusion, and this is a very wrong implementation.",{"type":21,"tag":22,"props":25377,"children":25378},{},[25379],{"type":26,"value":25380},"Since you have your black hat on, it will be obvious to you that a hacker can just bit mask out the part of the hash they are interested in, sort of like performing a Python slice, and exclude the \"salt\" that way. So this erroneous approach has no meaningful improvement over the \"simple hash only\" example above.",{"type":21,"tag":22,"props":25382,"children":25383},{},[25384],{"type":26,"value":25385},"Note: I actually found this erroneous approach used in an online source code recipe, several years ago. Of course it seems absurd to us under this analysis, but somebody thought it was correct enough to post on a source code recipe sharing website, so I think the point was worth belaboring here a bit.",{"type":21,"tag":22,"props":25387,"children":25388},{},[25389],{"type":21,"tag":2310,"props":25390,"children":25391},{},[25392],{"type":26,"value":25393},"A Better Seasoned Recipe",{"type":21,"tag":22,"props":25395,"children":25396},{},[25397],{"type":26,"value":25398},"Here is a more accurate description of how to use salt to protect your password hashes:",{"type":21,"tag":183,"props":25400,"children":25401},{},[25402,25407,25412,25427,25432,25437],{"type":21,"tag":187,"props":25403,"children":25404},{},[25405],{"type":26,"value":25406},"In context of creating a new user record or updating a password, receive the plaintext password from the user.",{"type":21,"tag":187,"props":25408,"children":25409},{},[25410],{"type":26,"value":25411},"Generate a strongly random number to use as the unique salt value for this user record.",{"type":21,"tag":187,"props":25413,"children":25414},{},[25415,25417,25425],{"type":26,"value":25416},"Compute: a hash of (the salt concatenated with a hash of (the salt concatenated with the password)). Here's ",{"type":21,"tag":322,"props":25418,"children":25422},{"href":25419,"rel":25420,"title":25421},"https://blog.whitehatsec.com/hash-length-extension-attacks/",[326],"Hash length extension attacks",[25423],{"type":26,"value":25424},"a link",{"type":26,"value":25426}," explaining why this expression needs to be this complex, instead of simply a hash of the concatenation.",{"type":21,"tag":187,"props":25428,"children":25429},{},[25430],{"type":26,"value":25431},"Store both the result of that final hash calculation, and also the unmodified salt value in your database in the user record. I personally like to concatenate the final hash and salt and store them in the same record attribute, just to be obscure. But that doesn't really matter. Note that it's OK to store the salt in plaintext; in fact, that's required.",{"type":21,"tag":187,"props":25433,"children":25434},{},[25435],{"type":26,"value":25436},"After we are finished with this process, deliberately forget the plaintext password. Depending on the overall architecture, maybe it is was provided by the user, or maybe it was system-generated and must now be emailed to the user. Either way, it must not be stored as plaintext.",{"type":21,"tag":187,"props":25438,"children":25439},{},[25440],{"type":26,"value":25441},"Later on, when the user enters their user name and password to log in, look up the record by user name, then repeat the calculation in step 3 using the salt value retrieved from the record. The resulting hash (using the password being entered) can be checked against the stored hash from the database to determine if the user entered the right password.",{"type":21,"tag":22,"props":25443,"children":25444},{},[25445,25447],{"type":26,"value":25446},"If you do it right, your database's hashes should now look totally scrambled and inscrutable to an unauthorized reader.  ",{"type":21,"tag":33,"props":25448,"children":25449},{},[25450],{"type":26,"value":25451},"(Reminder to self: next time must avoid blogging while hungry, especially about recipes for salted hashes and ordering crackable things as scrambled.)",{"type":21,"tag":22,"props":25453,"children":25454},{},[25455],{"type":21,"tag":2310,"props":25456,"children":25457},{},[25458],{"type":26,"value":25459},"Choosing a Hash Function",{"type":21,"tag":22,"props":25461,"children":25462},{},[25463,25465,25473],{"type":26,"value":25464},"This blog post is not a complete treatment of the subject of server side salting and password hashing. Another important decision is what hashing function to use. A hash function in this context is typically chosen to be both secure and slow. But it's also a moving target, as cryptographic standards must continually respond to rapidly advancing cracking capabilities. Somehow the very weak MD5 ended up as an entrenched hash function in very widespread use in the 90's and aughts. (Boy, was that a short sighted mistake.) Many people are still using SHA-1, which wasn't considered horrible just a few years ago, but really needs to be deprecated in favor of stronger options. I recommend you spend some time reading the links in ",{"type":21,"tag":322,"props":25466,"children":25470},{"href":25467,"rel":25468,"title":25469},"http://stackoverflow.com/questions/2549988/whats-the-recommended-hashing-algorithm-to-use-for-stored-passwords",[326],"Stack Overflow discussion of what hash function to choose",[25471],{"type":26,"value":25472},"this discussion",{"type":26,"value":25474}," to get a sense of what's out there. I'm deliberately not giving a specific recommendation here, in order to reinforce that there is actually more than one possible answer, and also that the \"correct answers\" periodically change.",{"type":21,"tag":22,"props":25476,"children":25477},{},[25478],{"type":21,"tag":2310,"props":25479,"children":25480},{},[25481],{"type":26,"value":25482},"Don't Try This At Home",{"type":21,"tag":22,"props":25484,"children":25485},{},[25486],{"type":26,"value":25487},"My final advice may sound like it contradicts everything I've said thus far. But that's OK. :)",{"type":21,"tag":22,"props":25489,"children":25490},{},[25491],{"type":26,"value":25492},"If at all possible, you should not come up with your own implementation of these approaches. Ideally, you should rely on your framework libraries to provide high level, complete authentication and authorization wrappers. Or if not, at least you should find and integrate a secure implementation from a trusted source. I already showed you how some guy on the internet thought they were salting their passwords, but got it totally wrong; so definitely don't trust random stuff you google up.",{"type":21,"tag":22,"props":25494,"children":25495},{},[25496],{"type":26,"value":25497},"Be very cautious if you're not a cryptographic expert. Certainly, you can and should learn the basics of information security, and use your knowledge to audit and critique your own systems. But any implementations you deploy to production should be from trusted frameworks, or at least closely follow standard industry best practices. Don't assemble something off the top of your head, or it will almost certainly be cryptographically weak and defective.",{"type":21,"tag":22,"props":25499,"children":25500},{},[25501],{"type":26,"value":25502},"Thanks for reading! You can take the black hat off now. Hopefully this was informative for somebody; if you have any questions or want to share your own advice for readers, I'd love to read your comments below.",{"title":8,"searchDepth":333,"depth":333,"links":25504},[],"content:avogan:2012-07:salt-2.md","avogan/2012-07/salt-2.md","avogan/2012-07/salt-2",{"user":25509,"name":25510},"avogan","Andrew Vogan",{"_path":25512,"_dir":24999,"_draft":7,"_partial":7,"_locale":8,"title":25513,"description":25514,"publishDate":25515,"tags":25516,"excerpt":25514,"body":25517,"_type":343,"_id":25868,"_source":345,"_file":25869,"_stem":25870,"_extension":348,"author":25871},"/avogan/2012-07/salt","What Your Users Don't Know (Part 1)","What's wrong with this code?","2012-07-13",[25004,1302,15],{"type":18,"children":25518,"toc":25866},[25519,25523,25536,25706,25721,25744,25751,25766,25778,25784,25789,25795,25837,25843,25857,25862],{"type":21,"tag":22,"props":25520,"children":25521},{},[25522],{"type":26,"value":25514},{"type":21,"tag":22,"props":25524,"children":25525},{},[25526],{"type":21,"tag":33,"props":25527,"children":25528},{},[25529,25531,25535],{"type":26,"value":25530},"(This is part 1 of a series on web security; see ",{"type":21,"tag":322,"props":25532,"children":25533},{"href":25029},[25534],{"type":26,"value":25032},{"type":26,"value":5081},{"type":21,"tag":239,"props":25537,"children":25539},{"className":1337,"code":25538,"language":1339,"meta":8,"style":8},"bool IsValidUser(string userName, string password)\n{\n   string sql = string.Format(\"SELECT 1 FROM person \" + \n                              \"WHERE userName = '{0}' AND password = '{1}'\",\n                              userName,\n                              password);\n   return ExecuteQuery(sql).Rows > 0;\n}\n",[25540],{"type":21,"tag":246,"props":25541,"children":25542},{"__ignoreMap":8},[25543,25586,25593,25641,25653,25661,25669,25699],{"type":21,"tag":455,"props":25544,"children":25545},{"class":457,"line":458},[25546,25551,25556,25560,25564,25569,25573,25577,25582],{"type":21,"tag":455,"props":25547,"children":25548},{"style":1349},[25549],{"type":26,"value":25550},"bool",{"type":21,"tag":455,"props":25552,"children":25553},{"style":1365},[25554],{"type":26,"value":25555}," IsValidUser",{"type":21,"tag":455,"props":25557,"children":25558},{"style":486},[25559],{"type":26,"value":489},{"type":21,"tag":455,"props":25561,"children":25562},{"style":1349},[25563],{"type":26,"value":549},{"type":21,"tag":455,"props":25565,"children":25566},{"style":1365},[25567],{"type":26,"value":25568}," userName",{"type":21,"tag":455,"props":25570,"children":25571},{"style":486},[25572],{"type":26,"value":2228},{"type":21,"tag":455,"props":25574,"children":25575},{"style":1349},[25576],{"type":26,"value":549},{"type":21,"tag":455,"props":25578,"children":25579},{"style":1365},[25580],{"type":26,"value":25581}," password",{"type":21,"tag":455,"props":25583,"children":25584},{"style":486},[25585],{"type":26,"value":500},{"type":21,"tag":455,"props":25587,"children":25588},{"class":457,"line":336},[25589],{"type":21,"tag":455,"props":25590,"children":25591},{"style":486},[25592],{"type":26,"value":1394},{"type":21,"tag":455,"props":25594,"children":25595},{"class":457,"line":333},[25596,25601,25606,25610,25614,25618,25623,25627,25632,25636],{"type":21,"tag":455,"props":25597,"children":25598},{"style":1349},[25599],{"type":26,"value":25600},"   string",{"type":21,"tag":455,"props":25602,"children":25603},{"style":1365},[25604],{"type":26,"value":25605}," sql",{"type":21,"tag":455,"props":25607,"children":25608},{"style":1349},[25609],{"type":26,"value":2131},{"type":21,"tag":455,"props":25611,"children":25612},{"style":1349},[25613],{"type":26,"value":1362},{"type":21,"tag":455,"props":25615,"children":25616},{"style":486},[25617],{"type":26,"value":551},{"type":21,"tag":455,"props":25619,"children":25620},{"style":1365},[25621],{"type":26,"value":25622},"Format",{"type":21,"tag":455,"props":25624,"children":25625},{"style":486},[25626],{"type":26,"value":489},{"type":21,"tag":455,"props":25628,"children":25629},{"style":492},[25630],{"type":26,"value":25631},"\"SELECT 1 FROM person \"",{"type":21,"tag":455,"props":25633,"children":25634},{"style":1349},[25635],{"type":26,"value":3929},{"type":21,"tag":455,"props":25637,"children":25638},{"style":486},[25639],{"type":26,"value":25640}," \n",{"type":21,"tag":455,"props":25642,"children":25643},{"class":457,"line":1424},[25644,25649],{"type":21,"tag":455,"props":25645,"children":25646},{"style":492},[25647],{"type":26,"value":25648},"                              \"WHERE userName = '{0}' AND password = '{1}'\"",{"type":21,"tag":455,"props":25650,"children":25651},{"style":486},[25652],{"type":26,"value":2483},{"type":21,"tag":455,"props":25654,"children":25655},{"class":457,"line":1443},[25656],{"type":21,"tag":455,"props":25657,"children":25658},{"style":486},[25659],{"type":26,"value":25660},"                              userName,\n",{"type":21,"tag":455,"props":25662,"children":25663},{"class":457,"line":1489},[25664],{"type":21,"tag":455,"props":25665,"children":25666},{"style":486},[25667],{"type":26,"value":25668},"                              password);\n",{"type":21,"tag":455,"props":25670,"children":25671},{"class":457,"line":1506},[25672,25677,25682,25687,25691,25695],{"type":21,"tag":455,"props":25673,"children":25674},{"style":1349},[25675],{"type":26,"value":25676},"   return",{"type":21,"tag":455,"props":25678,"children":25679},{"style":1365},[25680],{"type":26,"value":25681}," ExecuteQuery",{"type":21,"tag":455,"props":25683,"children":25684},{"style":486},[25685],{"type":26,"value":25686},"(sql).Rows ",{"type":21,"tag":455,"props":25688,"children":25689},{"style":1349},[25690],{"type":26,"value":1457},{"type":21,"tag":455,"props":25692,"children":25693},{"style":480},[25694],{"type":26,"value":1417},{"type":21,"tag":455,"props":25696,"children":25697},{"style":486},[25698],{"type":26,"value":1440},{"type":21,"tag":455,"props":25700,"children":25701},{"class":457,"line":1547},[25702],{"type":21,"tag":455,"props":25703,"children":25704},{"style":486},[25705],{"type":26,"value":2002},{"type":21,"tag":22,"props":25707,"children":25708},{},[25709,25711,25719],{"type":26,"value":25710},"Any jokester who says \"it looks fine to me\" will be sent to the ",{"type":21,"tag":322,"props":25712,"children":25716},{"href":25713,"rel":25714,"title":25715},"http://starwars.wikia.com/wiki/Spice_Mines_of_Kessel",[326],"Spice Mines of Kessel",[25717],{"type":26,"value":25718},"spice mines of Kessel",{"type":26,"value":25720},". But I think for observant readers, a couple of critical security errors will practically jump off the screen:",{"type":21,"tag":2953,"props":25722,"children":25723},{},[25724,25739],{"type":21,"tag":187,"props":25725,"children":25726},{},[25727,25729,25737],{"type":26,"value":25728},"User inputs are being concatenated directly into a SQL query string, risking a ",{"type":21,"tag":322,"props":25730,"children":25734},{"href":25731,"rel":25732,"title":25733},"http://en.wikipedia.org/wiki/SQL_injection",[326],"SQL Injection Attack",[25735],{"type":26,"value":25736},"SQL injection",{"type":26,"value":25738}," attack.",{"type":21,"tag":187,"props":25740,"children":25741},{},[25742],{"type":26,"value":25743},"Passwords are stored in plaintext, exposing users to further harm in the event the database is accessed.",{"type":21,"tag":25745,"props":25746,"children":25748},"h1",{"id":25747},"in-the-real-world",[25749],{"type":26,"value":25750},"In the Real World",{"type":21,"tag":22,"props":25752,"children":25753},{},[25754,25756,25764],{"type":26,"value":25755},"If you keep up with the news, you may have seen that no less an internet giant than Yahoo may have been guilty of both of the above mistakes, leading to ",{"type":21,"tag":322,"props":25757,"children":25761},{"href":25758,"rel":25759,"title":25760},"http://arstechnica.com/security/2012/07/yahoo-service-hacked/",[326],"Yahoo security breach",[25762],{"type":26,"value":25763},"a breach of 435K user credentials",{"type":26,"value":25765},". This is a disaster for any company that safeguards private user data.",{"type":21,"tag":22,"props":25767,"children":25768},{},[25769,25771,25776],{"type":26,"value":25770},"You can't depend on users to protect themselves; somewhere out in the world today is a person who set up the same password on a Justin Bieber mailing list website and also their online banking. So if even a fluffy, non-important website drops the ball, users may see their bank accounts emptied. That's probably an extreme worst case, though there is a whole range of other mischief you don't want to enable either. The point is, ",{"type":21,"tag":2310,"props":25772,"children":25773},{},[25774],{"type":26,"value":25775},"your users trust you, and they don't know what you are doing with their data.",{"type":26,"value":25777}," Be worthy of their trust.",{"type":21,"tag":25745,"props":25779,"children":25781},{"id":25780},"get-in-the-mindset",[25782],{"type":26,"value":25783},"Get In the Mindset",{"type":21,"tag":22,"props":25785,"children":25786},{},[25787],{"type":26,"value":25788},"A technique I find helpful for programmers is to temporarily put down their shining knight helmet and try on a black hat for size. Put yourself in a creative trouble-maker frame of mind. Ask the question: how could somebody compromise my website and harm my users or my organization?",{"type":21,"tag":25745,"props":25790,"children":25792},{"id":25791},"sql-injection",[25793],{"type":26,"value":25794},"SQL Injection",{"type":21,"tag":22,"props":25796,"children":25797},{},[25798,25800,25808,25810,25818,25819,25826,25828,25835],{"type":26,"value":25799},"In the case of the code snippet at the top of this article, it is trivially easy for a hacker to enter a user name or password that will disrupt the query. By placing complex SQL expressions (perhaps cleverly including SQL comments) in the user name and password inputs, you can easily ",{"type":21,"tag":322,"props":25801,"children":25805},{"href":25802,"rel":25803,"title":25804},"http://xkcd.com/327/",[326],"Little Bobby Tables",[25806],{"type":26,"value":25807},"delete or insert",{"type":26,"value":25809}," records. What would totally frustrate your hacking attempts, though, is if the programmer used their framework's feature for formal query value parameters (see for ",{"type":21,"tag":322,"props":25811,"children":25815},{"href":25812,"rel":25813,"title":25814},"http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters.aspx",[326],"framework query params",[25816],{"type":26,"value":25817},"C#",{"type":26,"value":2228},{"type":21,"tag":322,"props":25820,"children":25823},{"href":25821,"rel":25822,"title":25814},"http://stackoverflow.com/questions/3410455/how-do-i-use-sql-parameters-with-python",[326],[25824],{"type":26,"value":25825},"Python",{"type":26,"value":25827},", etc). And that, of course, is the correct answer here. Never concatenate user inputs into SQL strings. It's even a bad idea to write your own sanitizing functions, because you'll probably get a detail wrong, even if you're a smart person. Just use the framework, all the time, or else use a mainstream ",{"type":21,"tag":322,"props":25829,"children":25833},{"href":25830,"rel":25831,"title":25832},"http://en.wikipedia.org/wiki/Object-relational_mapping",[326],"ORM",[25834],{"type":26,"value":25832},{"type":26,"value":25836}," that hides the query string safely out of your code's sight.",{"type":21,"tag":25745,"props":25838,"children":25840},{"id":25839},"plaintext-passwords",[25841],{"type":26,"value":25842},"Plaintext Passwords",{"type":21,"tag":22,"props":25844,"children":25845},{},[25846,25848,25855],{"type":26,"value":25847},"The other major problem is the use of plaintext password storage. That's obviously a really bad thing if the database is accessed, and many programmers are aware that if you just do a one-way hash of the password, it's more secure. You do lose the ability to remind the user of their password, but it's usually OK, because they can just create a new one when needed via an email challenge/response. But even hashing itself doesn't cut it. If you are not ",{"type":21,"tag":322,"props":25849,"children":25853},{"href":25850,"rel":25851,"title":25852},"http://en.wikipedia.org/wiki/Salt_(cryptography)",[326],"salting",[25854],{"type":26,"value":25852},{"type":26,"value":25856}," your password hashes, your users are exposed to unacceptable risk.",{"type":21,"tag":22,"props":25858,"children":25859},{},[25860],{"type":26,"value":25861},"I'll take a closer look at how to salt passwords in a future post. It really sounds more difficult than it is. So to summarize: do some reading, and also occasionally just put yourself in a black hat frame of mind, and you can work out how to close common loopholes.",{"type":21,"tag":1279,"props":25863,"children":25864},{},[25865],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":25867},[],"content:avogan:2012-07:salt.md","avogan/2012-07/salt.md","avogan/2012-07/salt",{"user":25509,"name":25510},{"_path":25873,"_dir":25874,"_draft":7,"_partial":7,"_locale":8,"title":25875,"description":25876,"publishDate":25877,"tags":25878,"excerpt":25876,"body":25882,"_type":343,"_id":25996,"_source":345,"_file":25997,"_stem":25998,"_extension":348,"author":25999},"/rbrubaker/2012-06/coffee-backbone-3","2012-06","Fun with CoffeeScript and Backbone.js : Part 3","In this post I’ll discuss my thoughts on CoffeeScript and Backbone.js.","2012-06-08",[25879,25880,25881,15],"backbone-js","coffeescript","html5",{"type":18,"children":25883,"toc":25994},[25884,25906,25926,25938,25959,25975,25984,25989],{"type":21,"tag":22,"props":25885,"children":25886},{},[25887,25889,25896,25898,25905],{"type":26,"value":25888},"In this post I’ll discuss my thoughts on ",{"type":21,"tag":322,"props":25890,"children":25893},{"href":25891,"rel":25892},"http://coffeescript.org/",[326],[25894],{"type":26,"value":25895},"CoffeeScript",{"type":26,"value":25897}," and ",{"type":21,"tag":322,"props":25899,"children":25902},{"href":25900,"rel":25901},"http://backbonejs.org/",[326],[25903],{"type":26,"value":25904},"Backbone.js",{"type":26,"value":551},{"type":21,"tag":22,"props":25907,"children":25908},{},[25909,25911,25917,25919,25925],{"type":26,"value":25910},"The project is a simplified morse code simulator that animates morse code being sent over a telegraph line. The complete source is available ",{"type":21,"tag":322,"props":25912,"children":25915},{"href":25913,"rel":25914},"https://github.com/rmb177/MorseCode",[326],[25916],{"type":26,"value":2869},{"type":26,"value":25918}," and the running code can be seen ",{"type":21,"tag":322,"props":25920,"children":25923},{"href":25921,"rel":25922},"http://www.ryan-brubaker.com/code/MorseCode",[326],[25924],{"type":26,"value":2869},{"type":26,"value":551},{"type":21,"tag":22,"props":25927,"children":25928},{},[25929,25931,25937],{"type":26,"value":25930},"See the ",{"type":21,"tag":322,"props":25932,"children":25934},{"href":25933},"/search/fun/coffeescript/user:rmb",[25935],{"type":26,"value":25936},"rest of this series",{"type":26,"value":551},{"type":21,"tag":22,"props":25939,"children":25940},{},[25941,25943,25950,25952,25957],{"type":26,"value":25942},"JavaScript has slowly become my favorite programming language as I've done more web development. As you learn its ",{"type":21,"tag":322,"props":25944,"children":25947},{"href":25945,"rel":25946},"http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742/ref=sr_1_1?ie=UTF8&qid=1339122559&sr=8-1",[326],[25948],{"type":26,"value":25949},"Good Parts",{"type":26,"value":25951},", you start to realize how powerful of a language it is. I'll never forget the ",{"type":21,"tag":33,"props":25953,"children":25954},{},[25955],{"type":26,"value":25956},"Aha",{"type":26,"value":25958},"! moment I had when I wrote my first closure.",{"type":21,"tag":22,"props":25960,"children":25961},{},[25962,25973],{"type":21,"tag":322,"props":25963,"children":25966},{"href":25964,"rel":25965},"https://artandlogic.com/wp-content/uploads/2012/06/coffeescript-logo1.png",[326],[25967],{"type":21,"tag":510,"props":25968,"children":25972},{"alt":25969,"src":25970,"title":25971},"Coffeescript logo","assets/images/coffeescript-logo1.png","CoffeeScript-logo",[],{"type":26,"value":25974},"CoffeeScript takes a great language and makes it even better. It hides the bad parts of JavaScript and adds the expressiveness of Ruby and Python. The addition of classes shields the developer from JavaScript's prototypal inheritance and is much easier to understand for developers used to class-based inheritance. Admittedly, this was a small demo, but tracking runtime errors in the JavaScript code back to the CoffeeScript code was trivial. I definitely plan to use CoffeeScript as much as possible in future projects.",{"type":21,"tag":22,"props":25976,"children":25977},{},[25978],{"type":21,"tag":510,"props":25979,"children":25983},{"alt":25980,"src":25981,"title":25982},"Backbone js","assets/images/backbone.png","backbone",[],{"type":21,"tag":22,"props":25985,"children":25986},{},[25987],{"type":26,"value":25988},"This demo only scratched the surface of Backbone.js. Views can be initialized with templates, making complex models easier to render. Backbone.js also provides functionality to automatically synch your objects to/from a server, provides support for collections and also provides routing functionality to ensure the back button works in single-page applications. I'm very interested in learning more about Backbone.js and using it to reduce the amount of front-end code I need to write.",{"type":21,"tag":22,"props":25990,"children":25991},{},[25992],{"type":26,"value":25993},"In general, I think the combination of CoffeeScript and Backbone.js results in a much cleaner application. View code is segregated from the model code and developers are forced into keeping things more modular. This style would also make unit testing easier. I'm anxious to continue experimenting with both of these technologies to see how they boost my productivity and allow me to develop larger web-apps in a more controlled manner.",{"title":8,"searchDepth":333,"depth":333,"links":25995},[],"content:rbrubaker:2012-06:coffee-backbone-3.md","rbrubaker/2012-06/coffee-backbone-3.md","rbrubaker/2012-06/coffee-backbone-3",{"user":26000,"name":26001},"rbrubaker","Ryan Brubaker",{"_path":26003,"_dir":25874,"_draft":7,"_partial":7,"_locale":8,"title":26004,"description":26005,"publishDate":26006,"tags":26007,"excerpt":26005,"body":26008,"_type":343,"_id":27556,"_source":345,"_file":27557,"_stem":27558,"_extension":348,"author":27559},"/rbrubaker/2012-06/coffee-backbone-2","Fun with CoffeeScript and Backbone.js : Part 2","In this post I’ll discuss the code that handles updating the UI.","2012-06-07",[25879,25880,25881,15],{"type":18,"children":26009,"toc":27554},[26010,26014,26023,26039,26050,26219,26239,26250,26790,26809,26820,27539,27550],{"type":21,"tag":22,"props":26011,"children":26012},{},[26013],{"type":26,"value":26005},{"type":21,"tag":22,"props":26015,"children":26016},{},[26017,26018,26022],{"type":26,"value":25930},{"type":21,"tag":322,"props":26019,"children":26020},{"href":25933},[26021],{"type":26,"value":25936},{"type":26,"value":551},{"type":21,"tag":22,"props":26024,"children":26025},{},[26026,26027,26032,26033,26038],{"type":26,"value":25910},{"type":21,"tag":322,"props":26028,"children":26030},{"href":25913,"rel":26029},[326],[26031],{"type":26,"value":2869},{"type":26,"value":25918},{"type":21,"tag":322,"props":26034,"children":26036},{"href":25921,"rel":26035},[326],[26037],{"type":26,"value":2869},{"type":26,"value":551},{"type":21,"tag":22,"props":26040,"children":26041},{},[26042],{"type":21,"tag":2310,"props":26043,"children":26044},{},[26045],{"type":21,"tag":33,"props":26046,"children":26047},{},[26048],{"type":26,"value":26049},"Communication Line View",{"type":21,"tag":239,"props":26051,"children":26055},{"className":26052,"code":26053,"language":26054,"meta":8,"style":8},"language-coffee shiki shiki-themes github-light github-dark","class DecoderView extends Backbone.View\n   initialize: ->\n      @model.bind('parsedCharacter', @render)\n\n   render: (token) =>\n      messageBox = $('#messageBox')\n      messageBox.val(messageBox.val() + token)\n","coffee",[26056],{"type":21,"tag":246,"props":26057,"children":26058},{"__ignoreMap":8},[26059,26082,26099,26125,26132,26154,26184],{"type":21,"tag":455,"props":26060,"children":26061},{"class":457,"line":458},[26062,26067,26072,26077],{"type":21,"tag":455,"props":26063,"children":26064},{"style":1349},[26065],{"type":26,"value":26066},"class",{"type":21,"tag":455,"props":26068,"children":26069},{"style":1365},[26070],{"type":26,"value":26071}," DecoderView",{"type":21,"tag":455,"props":26073,"children":26074},{"style":1349},[26075],{"type":26,"value":26076}," extends",{"type":21,"tag":455,"props":26078,"children":26079},{"style":1365},[26080],{"type":26,"value":26081}," Backbone.View\n",{"type":21,"tag":455,"props":26083,"children":26084},{"class":457,"line":336},[26085,26090,26094],{"type":21,"tag":455,"props":26086,"children":26087},{"style":1365},[26088],{"type":26,"value":26089},"   initialize",{"type":21,"tag":455,"props":26091,"children":26092},{"style":1349},[26093],{"type":26,"value":831},{"type":21,"tag":455,"props":26095,"children":26096},{"style":1349},[26097],{"type":26,"value":26098}," ->\n",{"type":21,"tag":455,"props":26100,"children":26101},{"class":457,"line":333},[26102,26107,26111,26115,26120],{"type":21,"tag":455,"props":26103,"children":26104},{"style":486},[26105],{"type":26,"value":26106},"      @model.",{"type":21,"tag":455,"props":26108,"children":26109},{"style":1365},[26110],{"type":26,"value":23473},{"type":21,"tag":455,"props":26112,"children":26113},{"style":486},[26114],{"type":26,"value":489},{"type":21,"tag":455,"props":26116,"children":26117},{"style":492},[26118],{"type":26,"value":26119},"'parsedCharacter'",{"type":21,"tag":455,"props":26121,"children":26122},{"style":486},[26123],{"type":26,"value":26124},", @render)\n",{"type":21,"tag":455,"props":26126,"children":26127},{"class":457,"line":1424},[26128],{"type":21,"tag":455,"props":26129,"children":26130},{"emptyLinePlaceholder":471},[26131],{"type":26,"value":474},{"type":21,"tag":455,"props":26133,"children":26134},{"class":457,"line":1443},[26135,26140,26144,26149],{"type":21,"tag":455,"props":26136,"children":26137},{"style":1365},[26138],{"type":26,"value":26139},"   render",{"type":21,"tag":455,"props":26141,"children":26142},{"style":1349},[26143],{"type":26,"value":831},{"type":21,"tag":455,"props":26145,"children":26146},{"style":486},[26147],{"type":26,"value":26148}," (token) ",{"type":21,"tag":455,"props":26150,"children":26151},{"style":1349},[26152],{"type":26,"value":26153},"=>\n",{"type":21,"tag":455,"props":26155,"children":26156},{"class":457,"line":1489},[26157,26162,26166,26171,26175,26180],{"type":21,"tag":455,"props":26158,"children":26159},{"style":4527},[26160],{"type":26,"value":26161},"      messageBox",{"type":21,"tag":455,"props":26163,"children":26164},{"style":1349},[26165],{"type":26,"value":2131},{"type":21,"tag":455,"props":26167,"children":26168},{"style":1365},[26169],{"type":26,"value":26170}," $",{"type":21,"tag":455,"props":26172,"children":26173},{"style":486},[26174],{"type":26,"value":489},{"type":21,"tag":455,"props":26176,"children":26177},{"style":492},[26178],{"type":26,"value":26179},"'#messageBox'",{"type":21,"tag":455,"props":26181,"children":26182},{"style":486},[26183],{"type":26,"value":500},{"type":21,"tag":455,"props":26185,"children":26186},{"class":457,"line":1506},[26187,26192,26197,26202,26206,26210,26214],{"type":21,"tag":455,"props":26188,"children":26189},{"style":486},[26190],{"type":26,"value":26191},"      messageBox.",{"type":21,"tag":455,"props":26193,"children":26194},{"style":1365},[26195],{"type":26,"value":26196},"val",{"type":21,"tag":455,"props":26198,"children":26199},{"style":486},[26200],{"type":26,"value":26201},"(messageBox.",{"type":21,"tag":455,"props":26203,"children":26204},{"style":1365},[26205],{"type":26,"value":26196},{"type":21,"tag":455,"props":26207,"children":26208},{"style":486},[26209],{"type":26,"value":20452},{"type":21,"tag":455,"props":26211,"children":26212},{"style":1349},[26213],{"type":26,"value":2206},{"type":21,"tag":455,"props":26215,"children":26216},{"style":486},[26217],{"type":26,"value":26218}," token)\n",{"type":21,"tag":22,"props":26220,"children":26221},{},[26222,26224,26230,26232,26237],{"type":26,"value":26223},"The CommunicationLineView class is a Backbone.js view class that handles the animation of signals sent across the communication line. In ",{"type":21,"tag":322,"props":26225,"children":26228},{"href":26226,"rel":26227},"http://blog.artlogic.com/2012/06/06/fun-with-coffeescript-and-backbone-js-part-1/",[326],[26229],{"type":26,"value":16582},{"type":26,"value":26231},", I mentioned that the model representing the communication line fires a ",{"type":21,"tag":33,"props":26233,"children":26234},{},[26235],{"type":26,"value":26236},"hasNewData",{"type":26,"value":26238}," event each time the tokens progress one step through the line. As you can see above, this view is bound to that event and rerenders itself every time it receives the event. The render method iterates over the current tokens and draws the appropriate token shape for each line position.",{"type":21,"tag":22,"props":26240,"children":26241},{},[26242],{"type":21,"tag":33,"props":26243,"children":26244},{},[26245],{"type":21,"tag":2310,"props":26246,"children":26247},{},[26248],{"type":26,"value":26249},"Decoder Model",{"type":21,"tag":239,"props":26251,"children":26253},{"className":26052,"code":26252,"language":26054,"meta":8,"style":8},"class MorseDecoder extends Backbone.Model\n\n   initializeKey: =>\n      \"\"\"\n      Omitting code that initializes the morse code key \n      dictionary.\n      \"\"\"\n\n   initialize: =>\n      @inputTokens = []\n      @numEmptyTokensInARow = 0\n      @initializeKey()\n\n   processToken: (token) =>\n      if token == ''\n         @parseTokens() if \n          ++@numEmptyTokensInARow >= 10\n      else if token == kWordStopToken\n         @parseTokens() if @inputTokens.length > 0\n         @trigger('parsedCharacter', ' ')\n      else\n         @inputTokens.push(token)\n         @numEmptyTokensInARow = 0\n\n   parseTokens: =>\n      if @inputTokens.length > 0\n         token = @key[@inputTokens.join('')]\n         @trigger('parsedCharacter', token) if \n          token != undefined\n      @numEmptyTokensInARow = 0\n      @inputTokens.length = 0\n",[26254],{"type":21,"tag":246,"props":26255,"children":26256},{"__ignoreMap":8},[26257,26278,26285,26302,26310,26318,26326,26333,26340,26355,26372,26389,26406,26413,26433,26454,26479,26501,26527,26559,26591,26599,26617,26633,26640,26656,26675,26710,26742,26759,26774],{"type":21,"tag":455,"props":26258,"children":26259},{"class":457,"line":458},[26260,26264,26269,26273],{"type":21,"tag":455,"props":26261,"children":26262},{"style":1349},[26263],{"type":26,"value":26066},{"type":21,"tag":455,"props":26265,"children":26266},{"style":1365},[26267],{"type":26,"value":26268}," MorseDecoder",{"type":21,"tag":455,"props":26270,"children":26271},{"style":1349},[26272],{"type":26,"value":26076},{"type":21,"tag":455,"props":26274,"children":26275},{"style":1365},[26276],{"type":26,"value":26277}," Backbone.Model\n",{"type":21,"tag":455,"props":26279,"children":26280},{"class":457,"line":336},[26281],{"type":21,"tag":455,"props":26282,"children":26283},{"emptyLinePlaceholder":471},[26284],{"type":26,"value":474},{"type":21,"tag":455,"props":26286,"children":26287},{"class":457,"line":333},[26288,26293,26297],{"type":21,"tag":455,"props":26289,"children":26290},{"style":1365},[26291],{"type":26,"value":26292},"   initializeKey",{"type":21,"tag":455,"props":26294,"children":26295},{"style":1349},[26296],{"type":26,"value":831},{"type":21,"tag":455,"props":26298,"children":26299},{"style":1349},[26300],{"type":26,"value":26301}," =>\n",{"type":21,"tag":455,"props":26303,"children":26304},{"class":457,"line":1424},[26305],{"type":21,"tag":455,"props":26306,"children":26307},{"style":492},[26308],{"type":26,"value":26309},"      \"\"\"\n",{"type":21,"tag":455,"props":26311,"children":26312},{"class":457,"line":1443},[26313],{"type":21,"tag":455,"props":26314,"children":26315},{"style":492},[26316],{"type":26,"value":26317},"      Omitting code that initializes the morse code key \n",{"type":21,"tag":455,"props":26319,"children":26320},{"class":457,"line":1489},[26321],{"type":21,"tag":455,"props":26322,"children":26323},{"style":492},[26324],{"type":26,"value":26325},"      dictionary.\n",{"type":21,"tag":455,"props":26327,"children":26328},{"class":457,"line":1506},[26329],{"type":21,"tag":455,"props":26330,"children":26331},{"style":492},[26332],{"type":26,"value":26309},{"type":21,"tag":455,"props":26334,"children":26335},{"class":457,"line":1547},[26336],{"type":21,"tag":455,"props":26337,"children":26338},{"emptyLinePlaceholder":471},[26339],{"type":26,"value":474},{"type":21,"tag":455,"props":26341,"children":26342},{"class":457,"line":1564},[26343,26347,26351],{"type":21,"tag":455,"props":26344,"children":26345},{"style":1365},[26346],{"type":26,"value":26089},{"type":21,"tag":455,"props":26348,"children":26349},{"style":1349},[26350],{"type":26,"value":831},{"type":21,"tag":455,"props":26352,"children":26353},{"style":1349},[26354],{"type":26,"value":26301},{"type":21,"tag":455,"props":26356,"children":26357},{"class":457,"line":1605},[26358,26363,26367],{"type":21,"tag":455,"props":26359,"children":26360},{"style":486},[26361],{"type":26,"value":26362},"      @inputTokens ",{"type":21,"tag":455,"props":26364,"children":26365},{"style":1349},[26366],{"type":26,"value":3193},{"type":21,"tag":455,"props":26368,"children":26369},{"style":486},[26370],{"type":26,"value":26371}," []\n",{"type":21,"tag":455,"props":26373,"children":26374},{"class":457,"line":1622},[26375,26380,26384],{"type":21,"tag":455,"props":26376,"children":26377},{"style":486},[26378],{"type":26,"value":26379},"      @numEmptyTokensInARow ",{"type":21,"tag":455,"props":26381,"children":26382},{"style":1349},[26383],{"type":26,"value":3193},{"type":21,"tag":455,"props":26385,"children":26386},{"style":480},[26387],{"type":26,"value":26388}," 0\n",{"type":21,"tag":455,"props":26390,"children":26391},{"class":457,"line":1663},[26392,26397,26402],{"type":21,"tag":455,"props":26393,"children":26394},{"style":486},[26395],{"type":26,"value":26396},"      @",{"type":21,"tag":455,"props":26398,"children":26399},{"style":1365},[26400],{"type":26,"value":26401},"initializeKey",{"type":21,"tag":455,"props":26403,"children":26404},{"style":486},[26405],{"type":26,"value":4506},{"type":21,"tag":455,"props":26407,"children":26408},{"class":457,"line":1680},[26409],{"type":21,"tag":455,"props":26410,"children":26411},{"emptyLinePlaceholder":471},[26412],{"type":26,"value":474},{"type":21,"tag":455,"props":26414,"children":26415},{"class":457,"line":1721},[26416,26421,26425,26429],{"type":21,"tag":455,"props":26417,"children":26418},{"style":1365},[26419],{"type":26,"value":26420},"   processToken",{"type":21,"tag":455,"props":26422,"children":26423},{"style":1349},[26424],{"type":26,"value":831},{"type":21,"tag":455,"props":26426,"children":26427},{"style":486},[26428],{"type":26,"value":26148},{"type":21,"tag":455,"props":26430,"children":26431},{"style":1349},[26432],{"type":26,"value":26153},{"type":21,"tag":455,"props":26434,"children":26435},{"class":457,"line":1738},[26436,26440,26445,26449],{"type":21,"tag":455,"props":26437,"children":26438},{"style":1349},[26439],{"type":26,"value":8273},{"type":21,"tag":455,"props":26441,"children":26442},{"style":486},[26443],{"type":26,"value":26444}," token ",{"type":21,"tag":455,"props":26446,"children":26447},{"style":1349},[26448],{"type":26,"value":1412},{"type":21,"tag":455,"props":26450,"children":26451},{"style":492},[26452],{"type":26,"value":26453}," ''\n",{"type":21,"tag":455,"props":26455,"children":26456},{"class":457,"line":1779},[26457,26462,26467,26471,26475],{"type":21,"tag":455,"props":26458,"children":26459},{"style":486},[26460],{"type":26,"value":26461},"         @",{"type":21,"tag":455,"props":26463,"children":26464},{"style":1365},[26465],{"type":26,"value":26466},"parseTokens",{"type":21,"tag":455,"props":26468,"children":26469},{"style":486},[26470],{"type":26,"value":20452},{"type":21,"tag":455,"props":26472,"children":26473},{"style":1349},[26474],{"type":26,"value":2041},{"type":21,"tag":455,"props":26476,"children":26477},{"style":486},[26478],{"type":26,"value":25640},{"type":21,"tag":455,"props":26480,"children":26481},{"class":457,"line":1796},[26482,26487,26492,26496],{"type":21,"tag":455,"props":26483,"children":26484},{"style":1349},[26485],{"type":26,"value":26486},"          ++",{"type":21,"tag":455,"props":26488,"children":26489},{"style":486},[26490],{"type":26,"value":26491},"@numEmptyTokensInARow ",{"type":21,"tag":455,"props":26493,"children":26494},{"style":1349},[26495],{"type":26,"value":11736},{"type":21,"tag":455,"props":26497,"children":26498},{"style":480},[26499],{"type":26,"value":26500}," 10\n",{"type":21,"tag":455,"props":26502,"children":26503},{"class":457,"line":1837},[26504,26509,26514,26518,26522],{"type":21,"tag":455,"props":26505,"children":26506},{"style":1349},[26507],{"type":26,"value":26508},"      else",{"type":21,"tag":455,"props":26510,"children":26511},{"style":1349},[26512],{"type":26,"value":26513}," if",{"type":21,"tag":455,"props":26515,"children":26516},{"style":486},[26517],{"type":26,"value":26444},{"type":21,"tag":455,"props":26519,"children":26520},{"style":1349},[26521],{"type":26,"value":1412},{"type":21,"tag":455,"props":26523,"children":26524},{"style":486},[26525],{"type":26,"value":26526}," kWordStopToken\n",{"type":21,"tag":455,"props":26528,"children":26529},{"class":457,"line":1854},[26530,26534,26538,26542,26546,26551,26555],{"type":21,"tag":455,"props":26531,"children":26532},{"style":486},[26533],{"type":26,"value":26461},{"type":21,"tag":455,"props":26535,"children":26536},{"style":1365},[26537],{"type":26,"value":26466},{"type":21,"tag":455,"props":26539,"children":26540},{"style":486},[26541],{"type":26,"value":20452},{"type":21,"tag":455,"props":26543,"children":26544},{"style":1349},[26545],{"type":26,"value":2041},{"type":21,"tag":455,"props":26547,"children":26548},{"style":486},[26549],{"type":26,"value":26550}," @inputTokens.length ",{"type":21,"tag":455,"props":26552,"children":26553},{"style":1349},[26554],{"type":26,"value":1457},{"type":21,"tag":455,"props":26556,"children":26557},{"style":480},[26558],{"type":26,"value":26388},{"type":21,"tag":455,"props":26560,"children":26561},{"class":457,"line":1895},[26562,26566,26571,26575,26579,26583,26587],{"type":21,"tag":455,"props":26563,"children":26564},{"style":486},[26565],{"type":26,"value":26461},{"type":21,"tag":455,"props":26567,"children":26568},{"style":1365},[26569],{"type":26,"value":26570},"trigger",{"type":21,"tag":455,"props":26572,"children":26573},{"style":486},[26574],{"type":26,"value":489},{"type":21,"tag":455,"props":26576,"children":26577},{"style":492},[26578],{"type":26,"value":26119},{"type":21,"tag":455,"props":26580,"children":26581},{"style":486},[26582],{"type":26,"value":2228},{"type":21,"tag":455,"props":26584,"children":26585},{"style":492},[26586],{"type":26,"value":5615},{"type":21,"tag":455,"props":26588,"children":26589},{"style":486},[26590],{"type":26,"value":500},{"type":21,"tag":455,"props":26592,"children":26593},{"class":457,"line":1912},[26594],{"type":21,"tag":455,"props":26595,"children":26596},{"style":1349},[26597],{"type":26,"value":26598},"      else\n",{"type":21,"tag":455,"props":26600,"children":26601},{"class":457,"line":1953},[26602,26607,26612],{"type":21,"tag":455,"props":26603,"children":26604},{"style":486},[26605],{"type":26,"value":26606},"         @inputTokens.",{"type":21,"tag":455,"props":26608,"children":26609},{"style":480},[26610],{"type":26,"value":26611},"push",{"type":21,"tag":455,"props":26613,"children":26614},{"style":486},[26615],{"type":26,"value":26616},"(token)\n",{"type":21,"tag":455,"props":26618,"children":26619},{"class":457,"line":1970},[26620,26625,26629],{"type":21,"tag":455,"props":26621,"children":26622},{"style":486},[26623],{"type":26,"value":26624},"         @numEmptyTokensInARow ",{"type":21,"tag":455,"props":26626,"children":26627},{"style":1349},[26628],{"type":26,"value":3193},{"type":21,"tag":455,"props":26630,"children":26631},{"style":480},[26632],{"type":26,"value":26388},{"type":21,"tag":455,"props":26634,"children":26635},{"class":457,"line":1978},[26636],{"type":21,"tag":455,"props":26637,"children":26638},{"emptyLinePlaceholder":471},[26639],{"type":26,"value":474},{"type":21,"tag":455,"props":26641,"children":26642},{"class":457,"line":1996},[26643,26648,26652],{"type":21,"tag":455,"props":26644,"children":26645},{"style":1365},[26646],{"type":26,"value":26647},"   parseTokens",{"type":21,"tag":455,"props":26649,"children":26650},{"style":1349},[26651],{"type":26,"value":831},{"type":21,"tag":455,"props":26653,"children":26654},{"style":1349},[26655],{"type":26,"value":26301},{"type":21,"tag":455,"props":26657,"children":26658},{"class":457,"line":4487},[26659,26663,26667,26671],{"type":21,"tag":455,"props":26660,"children":26661},{"style":1349},[26662],{"type":26,"value":8273},{"type":21,"tag":455,"props":26664,"children":26665},{"style":486},[26666],{"type":26,"value":26550},{"type":21,"tag":455,"props":26668,"children":26669},{"style":1349},[26670],{"type":26,"value":1457},{"type":21,"tag":455,"props":26672,"children":26673},{"style":480},[26674],{"type":26,"value":26388},{"type":21,"tag":455,"props":26676,"children":26677},{"class":457,"line":4495},[26678,26683,26687,26692,26697,26701,26705],{"type":21,"tag":455,"props":26679,"children":26680},{"style":4527},[26681],{"type":26,"value":26682},"         token",{"type":21,"tag":455,"props":26684,"children":26685},{"style":1349},[26686],{"type":26,"value":2131},{"type":21,"tag":455,"props":26688,"children":26689},{"style":486},[26690],{"type":26,"value":26691}," @key[@inputTokens.",{"type":21,"tag":455,"props":26693,"children":26694},{"style":480},[26695],{"type":26,"value":26696},"join",{"type":21,"tag":455,"props":26698,"children":26699},{"style":486},[26700],{"type":26,"value":489},{"type":21,"tag":455,"props":26702,"children":26703},{"style":492},[26704],{"type":26,"value":10085},{"type":21,"tag":455,"props":26706,"children":26707},{"style":486},[26708],{"type":26,"value":26709},")]\n",{"type":21,"tag":455,"props":26711,"children":26712},{"class":457,"line":4509},[26713,26717,26721,26725,26729,26734,26738],{"type":21,"tag":455,"props":26714,"children":26715},{"style":486},[26716],{"type":26,"value":26461},{"type":21,"tag":455,"props":26718,"children":26719},{"style":1365},[26720],{"type":26,"value":26570},{"type":21,"tag":455,"props":26722,"children":26723},{"style":486},[26724],{"type":26,"value":489},{"type":21,"tag":455,"props":26726,"children":26727},{"style":492},[26728],{"type":26,"value":26119},{"type":21,"tag":455,"props":26730,"children":26731},{"style":486},[26732],{"type":26,"value":26733},", token) ",{"type":21,"tag":455,"props":26735,"children":26736},{"style":1349},[26737],{"type":26,"value":2041},{"type":21,"tag":455,"props":26739,"children":26740},{"style":486},[26741],{"type":26,"value":25640},{"type":21,"tag":455,"props":26743,"children":26744},{"class":457,"line":4561},[26745,26750,26754],{"type":21,"tag":455,"props":26746,"children":26747},{"style":486},[26748],{"type":26,"value":26749},"          token ",{"type":21,"tag":455,"props":26751,"children":26752},{"style":1349},[26753],{"type":26,"value":11181},{"type":21,"tag":455,"props":26755,"children":26756},{"style":480},[26757],{"type":26,"value":26758}," undefined\n",{"type":21,"tag":455,"props":26760,"children":26761},{"class":457,"line":4596},[26762,26766,26770],{"type":21,"tag":455,"props":26763,"children":26764},{"style":486},[26765],{"type":26,"value":26379},{"type":21,"tag":455,"props":26767,"children":26768},{"style":1349},[26769],{"type":26,"value":3193},{"type":21,"tag":455,"props":26771,"children":26772},{"style":480},[26773],{"type":26,"value":26388},{"type":21,"tag":455,"props":26775,"children":26776},{"class":457,"line":4627},[26777,26782,26786],{"type":21,"tag":455,"props":26778,"children":26779},{"style":486},[26780],{"type":26,"value":26781},"      @inputTokens.length ",{"type":21,"tag":455,"props":26783,"children":26784},{"style":1349},[26785],{"type":26,"value":3193},{"type":21,"tag":455,"props":26787,"children":26788},{"style":480},[26789],{"type":26,"value":26388},{"type":21,"tag":22,"props":26791,"children":26792},{},[26793,26795,26800,26802,26807],{"type":26,"value":26794},"The Decoder model is responsible for determining what characters the user has sent across the line. The ",{"type":21,"tag":33,"props":26796,"children":26797},{},[26798],{"type":26,"value":26799},"processToken",{"type":26,"value":26801}," function is called each time the tokens on the communication line progress one step through the line. The decoder processes the current tokens sent by the user anytime it sees 10 or more empty tokens in a row or receives a word-break token. If the decoder parses a known morse code sequence it fires a ",{"type":21,"tag":33,"props":26803,"children":26804},{},[26805],{"type":26,"value":26806},"parsedCharacter",{"type":26,"value":26808}," event letting the message field know it needs to rerender itself.",{"type":21,"tag":22,"props":26810,"children":26811},{},[26812],{"type":21,"tag":33,"props":26813,"children":26814},{},[26815],{"type":21,"tag":2310,"props":26816,"children":26817},{},[26818],{"type":26,"value":26819},"Decoder View",{"type":21,"tag":239,"props":26821,"children":26823},{"className":26052,"code":26822,"language":26054,"meta":8,"style":8},"class CommunicationLineView extends Backbone.View\n   initialize: ->\n      @model.bind('hasNewData', @render)\n\n   render: (tokens) =>\n      context = document.getElementById(\n       \"communicationLineCanvas\").getContext('2d')\n\n      context.clearRect(0, 0, context.canvas.width, 29)\n      tokenNum = 0\n      for token in tokens\n         do (token) ->\n            if kDotToken == token\n               context.beginPath()\n               context.moveTo((50 * tokenNum) + 15, 15)\n\n               context.arc((50 * tokenNum) + 15, 15, 10, \n                0, Math.PI*2, false)\n\n               context.closePath()\n               context.fill()\n               context.stroke()\n            else if kDashToken == token\n               context.fillRect((50 * tokenNum) + 15, \n                15, 25, 10)\n            else if kWordStopToken == token\n               context.fillRect((50 * tokenNum) + 30, \n                5, 10, 20)\n            tokenNum += 1\n",[26824],{"type":21,"tag":246,"props":26825,"children":26826},{"__ignoreMap":8},[26827,26847,26862,26886,26893,26913,26943,26973,26980,27023,27039,27061,27078,27099,27116,27168,27175,27232,27279,27286,27302,27318,27334,27359,27399,27428,27452,27492,27521],{"type":21,"tag":455,"props":26828,"children":26829},{"class":457,"line":458},[26830,26834,26839,26843],{"type":21,"tag":455,"props":26831,"children":26832},{"style":1349},[26833],{"type":26,"value":26066},{"type":21,"tag":455,"props":26835,"children":26836},{"style":1365},[26837],{"type":26,"value":26838}," CommunicationLineView",{"type":21,"tag":455,"props":26840,"children":26841},{"style":1349},[26842],{"type":26,"value":26076},{"type":21,"tag":455,"props":26844,"children":26845},{"style":1365},[26846],{"type":26,"value":26081},{"type":21,"tag":455,"props":26848,"children":26849},{"class":457,"line":336},[26850,26854,26858],{"type":21,"tag":455,"props":26851,"children":26852},{"style":1365},[26853],{"type":26,"value":26089},{"type":21,"tag":455,"props":26855,"children":26856},{"style":1349},[26857],{"type":26,"value":831},{"type":21,"tag":455,"props":26859,"children":26860},{"style":1349},[26861],{"type":26,"value":26098},{"type":21,"tag":455,"props":26863,"children":26864},{"class":457,"line":333},[26865,26869,26873,26877,26882],{"type":21,"tag":455,"props":26866,"children":26867},{"style":486},[26868],{"type":26,"value":26106},{"type":21,"tag":455,"props":26870,"children":26871},{"style":1365},[26872],{"type":26,"value":23473},{"type":21,"tag":455,"props":26874,"children":26875},{"style":486},[26876],{"type":26,"value":489},{"type":21,"tag":455,"props":26878,"children":26879},{"style":492},[26880],{"type":26,"value":26881},"'hasNewData'",{"type":21,"tag":455,"props":26883,"children":26884},{"style":486},[26885],{"type":26,"value":26124},{"type":21,"tag":455,"props":26887,"children":26888},{"class":457,"line":1424},[26889],{"type":21,"tag":455,"props":26890,"children":26891},{"emptyLinePlaceholder":471},[26892],{"type":26,"value":474},{"type":21,"tag":455,"props":26894,"children":26895},{"class":457,"line":1443},[26896,26900,26904,26909],{"type":21,"tag":455,"props":26897,"children":26898},{"style":1365},[26899],{"type":26,"value":26139},{"type":21,"tag":455,"props":26901,"children":26902},{"style":1349},[26903],{"type":26,"value":831},{"type":21,"tag":455,"props":26905,"children":26906},{"style":486},[26907],{"type":26,"value":26908}," (tokens) ",{"type":21,"tag":455,"props":26910,"children":26911},{"style":1349},[26912],{"type":26,"value":26153},{"type":21,"tag":455,"props":26914,"children":26915},{"class":457,"line":1489},[26916,26921,26925,26930,26934,26939],{"type":21,"tag":455,"props":26917,"children":26918},{"style":4527},[26919],{"type":26,"value":26920},"      context",{"type":21,"tag":455,"props":26922,"children":26923},{"style":1349},[26924],{"type":26,"value":2131},{"type":21,"tag":455,"props":26926,"children":26927},{"style":480},[26928],{"type":26,"value":26929}," document",{"type":21,"tag":455,"props":26931,"children":26932},{"style":486},[26933],{"type":26,"value":551},{"type":21,"tag":455,"props":26935,"children":26936},{"style":480},[26937],{"type":26,"value":26938},"getElementById",{"type":21,"tag":455,"props":26940,"children":26941},{"style":486},[26942],{"type":26,"value":11391},{"type":21,"tag":455,"props":26944,"children":26945},{"class":457,"line":1506},[26946,26951,26955,26960,26964,26969],{"type":21,"tag":455,"props":26947,"children":26948},{"style":492},[26949],{"type":26,"value":26950},"       \"communicationLineCanvas\"",{"type":21,"tag":455,"props":26952,"children":26953},{"style":486},[26954],{"type":26,"value":5081},{"type":21,"tag":455,"props":26956,"children":26957},{"style":480},[26958],{"type":26,"value":26959},"getContext",{"type":21,"tag":455,"props":26961,"children":26962},{"style":486},[26963],{"type":26,"value":489},{"type":21,"tag":455,"props":26965,"children":26966},{"style":492},[26967],{"type":26,"value":26968},"'2d'",{"type":21,"tag":455,"props":26970,"children":26971},{"style":486},[26972],{"type":26,"value":500},{"type":21,"tag":455,"props":26974,"children":26975},{"class":457,"line":1547},[26976],{"type":21,"tag":455,"props":26977,"children":26978},{"emptyLinePlaceholder":471},[26979],{"type":26,"value":474},{"type":21,"tag":455,"props":26981,"children":26982},{"class":457,"line":1564},[26983,26988,26993,26997,27001,27005,27009,27014,27019],{"type":21,"tag":455,"props":26984,"children":26985},{"style":486},[26986],{"type":26,"value":26987},"      context.",{"type":21,"tag":455,"props":26989,"children":26990},{"style":1365},[26991],{"type":26,"value":26992},"clearRect",{"type":21,"tag":455,"props":26994,"children":26995},{"style":486},[26996],{"type":26,"value":489},{"type":21,"tag":455,"props":26998,"children":26999},{"style":480},[27000],{"type":26,"value":7863},{"type":21,"tag":455,"props":27002,"children":27003},{"style":486},[27004],{"type":26,"value":2228},{"type":21,"tag":455,"props":27006,"children":27007},{"style":480},[27008],{"type":26,"value":7863},{"type":21,"tag":455,"props":27010,"children":27011},{"style":486},[27012],{"type":26,"value":27013},", context.canvas.width, ",{"type":21,"tag":455,"props":27015,"children":27016},{"style":480},[27017],{"type":26,"value":27018},"29",{"type":21,"tag":455,"props":27020,"children":27021},{"style":486},[27022],{"type":26,"value":500},{"type":21,"tag":455,"props":27024,"children":27025},{"class":457,"line":1605},[27026,27031,27035],{"type":21,"tag":455,"props":27027,"children":27028},{"style":4527},[27029],{"type":26,"value":27030},"      tokenNum",{"type":21,"tag":455,"props":27032,"children":27033},{"style":1349},[27034],{"type":26,"value":2131},{"type":21,"tag":455,"props":27036,"children":27037},{"style":480},[27038],{"type":26,"value":26388},{"type":21,"tag":455,"props":27040,"children":27041},{"class":457,"line":1622},[27042,27047,27051,27056],{"type":21,"tag":455,"props":27043,"children":27044},{"style":1349},[27045],{"type":26,"value":27046},"      for",{"type":21,"tag":455,"props":27048,"children":27049},{"style":486},[27050],{"type":26,"value":26444},{"type":21,"tag":455,"props":27052,"children":27053},{"style":1349},[27054],{"type":26,"value":27055},"in",{"type":21,"tag":455,"props":27057,"children":27058},{"style":486},[27059],{"type":26,"value":27060}," tokens\n",{"type":21,"tag":455,"props":27062,"children":27063},{"class":457,"line":1663},[27064,27069,27073],{"type":21,"tag":455,"props":27065,"children":27066},{"style":1349},[27067],{"type":26,"value":27068},"         do",{"type":21,"tag":455,"props":27070,"children":27071},{"style":486},[27072],{"type":26,"value":26148},{"type":21,"tag":455,"props":27074,"children":27075},{"style":1349},[27076],{"type":26,"value":27077},"->\n",{"type":21,"tag":455,"props":27079,"children":27080},{"class":457,"line":1680},[27081,27085,27090,27094],{"type":21,"tag":455,"props":27082,"children":27083},{"style":1349},[27084],{"type":26,"value":12145},{"type":21,"tag":455,"props":27086,"children":27087},{"style":486},[27088],{"type":26,"value":27089}," kDotToken ",{"type":21,"tag":455,"props":27091,"children":27092},{"style":1349},[27093],{"type":26,"value":1412},{"type":21,"tag":455,"props":27095,"children":27096},{"style":486},[27097],{"type":26,"value":27098}," token\n",{"type":21,"tag":455,"props":27100,"children":27101},{"class":457,"line":1721},[27102,27107,27112],{"type":21,"tag":455,"props":27103,"children":27104},{"style":486},[27105],{"type":26,"value":27106},"               context.",{"type":21,"tag":455,"props":27108,"children":27109},{"style":1365},[27110],{"type":26,"value":27111},"beginPath",{"type":21,"tag":455,"props":27113,"children":27114},{"style":486},[27115],{"type":26,"value":4506},{"type":21,"tag":455,"props":27117,"children":27118},{"class":457,"line":1738},[27119,27123,27128,27132,27137,27141,27146,27150,27155,27159,27164],{"type":21,"tag":455,"props":27120,"children":27121},{"style":486},[27122],{"type":26,"value":27106},{"type":21,"tag":455,"props":27124,"children":27125},{"style":480},[27126],{"type":26,"value":27127},"moveTo",{"type":21,"tag":455,"props":27129,"children":27130},{"style":486},[27131],{"type":26,"value":4575},{"type":21,"tag":455,"props":27133,"children":27134},{"style":480},[27135],{"type":26,"value":27136},"50",{"type":21,"tag":455,"props":27138,"children":27139},{"style":1349},[27140],{"type":26,"value":11055},{"type":21,"tag":455,"props":27142,"children":27143},{"style":486},[27144],{"type":26,"value":27145}," tokenNum) ",{"type":21,"tag":455,"props":27147,"children":27148},{"style":1349},[27149],{"type":26,"value":2206},{"type":21,"tag":455,"props":27151,"children":27152},{"style":480},[27153],{"type":26,"value":27154}," 15",{"type":21,"tag":455,"props":27156,"children":27157},{"style":486},[27158],{"type":26,"value":2228},{"type":21,"tag":455,"props":27160,"children":27161},{"style":480},[27162],{"type":26,"value":27163},"15",{"type":21,"tag":455,"props":27165,"children":27166},{"style":486},[27167],{"type":26,"value":500},{"type":21,"tag":455,"props":27169,"children":27170},{"class":457,"line":1779},[27171],{"type":21,"tag":455,"props":27172,"children":27173},{"emptyLinePlaceholder":471},[27174],{"type":26,"value":474},{"type":21,"tag":455,"props":27176,"children":27177},{"class":457,"line":1796},[27178,27182,27187,27191,27195,27199,27203,27207,27211,27215,27219,27223,27227],{"type":21,"tag":455,"props":27179,"children":27180},{"style":486},[27181],{"type":26,"value":27106},{"type":21,"tag":455,"props":27183,"children":27184},{"style":1365},[27185],{"type":26,"value":27186},"arc",{"type":21,"tag":455,"props":27188,"children":27189},{"style":486},[27190],{"type":26,"value":4575},{"type":21,"tag":455,"props":27192,"children":27193},{"style":480},[27194],{"type":26,"value":27136},{"type":21,"tag":455,"props":27196,"children":27197},{"style":1349},[27198],{"type":26,"value":11055},{"type":21,"tag":455,"props":27200,"children":27201},{"style":486},[27202],{"type":26,"value":27145},{"type":21,"tag":455,"props":27204,"children":27205},{"style":1349},[27206],{"type":26,"value":2206},{"type":21,"tag":455,"props":27208,"children":27209},{"style":480},[27210],{"type":26,"value":27154},{"type":21,"tag":455,"props":27212,"children":27213},{"style":486},[27214],{"type":26,"value":2228},{"type":21,"tag":455,"props":27216,"children":27217},{"style":480},[27218],{"type":26,"value":27163},{"type":21,"tag":455,"props":27220,"children":27221},{"style":486},[27222],{"type":26,"value":2228},{"type":21,"tag":455,"props":27224,"children":27225},{"style":480},[27226],{"type":26,"value":2233},{"type":21,"tag":455,"props":27228,"children":27229},{"style":486},[27230],{"type":26,"value":27231},", \n",{"type":21,"tag":455,"props":27233,"children":27234},{"class":457,"line":1837},[27235,27240,27244,27249,27253,27258,27262,27267,27271,27275],{"type":21,"tag":455,"props":27236,"children":27237},{"style":480},[27238],{"type":26,"value":27239},"                0",{"type":21,"tag":455,"props":27241,"children":27242},{"style":486},[27243],{"type":26,"value":2228},{"type":21,"tag":455,"props":27245,"children":27246},{"style":480},[27247],{"type":26,"value":27248},"Math",{"type":21,"tag":455,"props":27250,"children":27251},{"style":486},[27252],{"type":26,"value":551},{"type":21,"tag":455,"props":27254,"children":27255},{"style":480},[27256],{"type":26,"value":27257},"PI",{"type":21,"tag":455,"props":27259,"children":27260},{"style":1349},[27261],{"type":26,"value":2161},{"type":21,"tag":455,"props":27263,"children":27264},{"style":480},[27265],{"type":26,"value":27266},"2",{"type":21,"tag":455,"props":27268,"children":27269},{"style":486},[27270],{"type":26,"value":2228},{"type":21,"tag":455,"props":27272,"children":27273},{"style":480},[27274],{"type":26,"value":3519},{"type":21,"tag":455,"props":27276,"children":27277},{"style":486},[27278],{"type":26,"value":500},{"type":21,"tag":455,"props":27280,"children":27281},{"class":457,"line":1854},[27282],{"type":21,"tag":455,"props":27283,"children":27284},{"emptyLinePlaceholder":471},[27285],{"type":26,"value":474},{"type":21,"tag":455,"props":27287,"children":27288},{"class":457,"line":1895},[27289,27293,27298],{"type":21,"tag":455,"props":27290,"children":27291},{"style":486},[27292],{"type":26,"value":27106},{"type":21,"tag":455,"props":27294,"children":27295},{"style":1365},[27296],{"type":26,"value":27297},"closePath",{"type":21,"tag":455,"props":27299,"children":27300},{"style":486},[27301],{"type":26,"value":4506},{"type":21,"tag":455,"props":27303,"children":27304},{"class":457,"line":1912},[27305,27309,27314],{"type":21,"tag":455,"props":27306,"children":27307},{"style":486},[27308],{"type":26,"value":27106},{"type":21,"tag":455,"props":27310,"children":27311},{"style":1365},[27312],{"type":26,"value":27313},"fill",{"type":21,"tag":455,"props":27315,"children":27316},{"style":486},[27317],{"type":26,"value":4506},{"type":21,"tag":455,"props":27319,"children":27320},{"class":457,"line":1953},[27321,27325,27330],{"type":21,"tag":455,"props":27322,"children":27323},{"style":486},[27324],{"type":26,"value":27106},{"type":21,"tag":455,"props":27326,"children":27327},{"style":1365},[27328],{"type":26,"value":27329},"stroke",{"type":21,"tag":455,"props":27331,"children":27332},{"style":486},[27333],{"type":26,"value":4506},{"type":21,"tag":455,"props":27335,"children":27336},{"class":457,"line":1970},[27337,27342,27346,27351,27355],{"type":21,"tag":455,"props":27338,"children":27339},{"style":1349},[27340],{"type":26,"value":27341},"            else",{"type":21,"tag":455,"props":27343,"children":27344},{"style":1349},[27345],{"type":26,"value":26513},{"type":21,"tag":455,"props":27347,"children":27348},{"style":486},[27349],{"type":26,"value":27350}," kDashToken ",{"type":21,"tag":455,"props":27352,"children":27353},{"style":1349},[27354],{"type":26,"value":1412},{"type":21,"tag":455,"props":27356,"children":27357},{"style":486},[27358],{"type":26,"value":27098},{"type":21,"tag":455,"props":27360,"children":27361},{"class":457,"line":1978},[27362,27366,27371,27375,27379,27383,27387,27391,27395],{"type":21,"tag":455,"props":27363,"children":27364},{"style":486},[27365],{"type":26,"value":27106},{"type":21,"tag":455,"props":27367,"children":27368},{"style":1365},[27369],{"type":26,"value":27370},"fillRect",{"type":21,"tag":455,"props":27372,"children":27373},{"style":486},[27374],{"type":26,"value":4575},{"type":21,"tag":455,"props":27376,"children":27377},{"style":480},[27378],{"type":26,"value":27136},{"type":21,"tag":455,"props":27380,"children":27381},{"style":1349},[27382],{"type":26,"value":11055},{"type":21,"tag":455,"props":27384,"children":27385},{"style":486},[27386],{"type":26,"value":27145},{"type":21,"tag":455,"props":27388,"children":27389},{"style":1349},[27390],{"type":26,"value":2206},{"type":21,"tag":455,"props":27392,"children":27393},{"style":480},[27394],{"type":26,"value":27154},{"type":21,"tag":455,"props":27396,"children":27397},{"style":486},[27398],{"type":26,"value":27231},{"type":21,"tag":455,"props":27400,"children":27401},{"class":457,"line":1996},[27402,27407,27411,27416,27420,27424],{"type":21,"tag":455,"props":27403,"children":27404},{"style":480},[27405],{"type":26,"value":27406},"                15",{"type":21,"tag":455,"props":27408,"children":27409},{"style":486},[27410],{"type":26,"value":2228},{"type":21,"tag":455,"props":27412,"children":27413},{"style":480},[27414],{"type":26,"value":27415},"25",{"type":21,"tag":455,"props":27417,"children":27418},{"style":486},[27419],{"type":26,"value":2228},{"type":21,"tag":455,"props":27421,"children":27422},{"style":480},[27423],{"type":26,"value":2233},{"type":21,"tag":455,"props":27425,"children":27426},{"style":486},[27427],{"type":26,"value":500},{"type":21,"tag":455,"props":27429,"children":27430},{"class":457,"line":4487},[27431,27435,27439,27444,27448],{"type":21,"tag":455,"props":27432,"children":27433},{"style":1349},[27434],{"type":26,"value":27341},{"type":21,"tag":455,"props":27436,"children":27437},{"style":1349},[27438],{"type":26,"value":26513},{"type":21,"tag":455,"props":27440,"children":27441},{"style":486},[27442],{"type":26,"value":27443}," kWordStopToken ",{"type":21,"tag":455,"props":27445,"children":27446},{"style":1349},[27447],{"type":26,"value":1412},{"type":21,"tag":455,"props":27449,"children":27450},{"style":486},[27451],{"type":26,"value":27098},{"type":21,"tag":455,"props":27453,"children":27454},{"class":457,"line":4495},[27455,27459,27463,27467,27471,27475,27479,27483,27488],{"type":21,"tag":455,"props":27456,"children":27457},{"style":486},[27458],{"type":26,"value":27106},{"type":21,"tag":455,"props":27460,"children":27461},{"style":1365},[27462],{"type":26,"value":27370},{"type":21,"tag":455,"props":27464,"children":27465},{"style":486},[27466],{"type":26,"value":4575},{"type":21,"tag":455,"props":27468,"children":27469},{"style":480},[27470],{"type":26,"value":27136},{"type":21,"tag":455,"props":27472,"children":27473},{"style":1349},[27474],{"type":26,"value":11055},{"type":21,"tag":455,"props":27476,"children":27477},{"style":486},[27478],{"type":26,"value":27145},{"type":21,"tag":455,"props":27480,"children":27481},{"style":1349},[27482],{"type":26,"value":2206},{"type":21,"tag":455,"props":27484,"children":27485},{"style":480},[27486],{"type":26,"value":27487}," 30",{"type":21,"tag":455,"props":27489,"children":27490},{"style":486},[27491],{"type":26,"value":27231},{"type":21,"tag":455,"props":27493,"children":27494},{"class":457,"line":4509},[27495,27500,27504,27508,27512,27517],{"type":21,"tag":455,"props":27496,"children":27497},{"style":480},[27498],{"type":26,"value":27499},"                5",{"type":21,"tag":455,"props":27501,"children":27502},{"style":486},[27503],{"type":26,"value":2228},{"type":21,"tag":455,"props":27505,"children":27506},{"style":480},[27507],{"type":26,"value":2233},{"type":21,"tag":455,"props":27509,"children":27510},{"style":486},[27511],{"type":26,"value":2228},{"type":21,"tag":455,"props":27513,"children":27514},{"style":480},[27515],{"type":26,"value":27516},"20",{"type":21,"tag":455,"props":27518,"children":27519},{"style":486},[27520],{"type":26,"value":500},{"type":21,"tag":455,"props":27522,"children":27523},{"class":457,"line":4561},[27524,27529,27534],{"type":21,"tag":455,"props":27525,"children":27526},{"style":4527},[27527],{"type":26,"value":27528},"            tokenNum",{"type":21,"tag":455,"props":27530,"children":27531},{"style":1349},[27532],{"type":26,"value":27533}," +=",{"type":21,"tag":455,"props":27535,"children":27536},{"style":480},[27537],{"type":26,"value":27538}," 1\n",{"type":21,"tag":22,"props":27540,"children":27541},{},[27542,27544,27548],{"type":26,"value":27543},"The Decoder view is a Backbone.js view class that represents a simple text box to display the characters the user has sent across the line. Anytime the Decoder model emits a ",{"type":21,"tag":33,"props":27545,"children":27546},{},[27547],{"type":26,"value":26806},{"type":26,"value":27549}," event, the Decoder view appends the parsed character to the end of its text box.",{"type":21,"tag":1279,"props":27551,"children":27552},{},[27553],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":27555},[],"content:rbrubaker:2012-06:coffee-backbone-2.md","rbrubaker/2012-06/coffee-backbone-2.md","rbrubaker/2012-06/coffee-backbone-2",{"user":26000,"name":26001},{"_path":27561,"_dir":25874,"_draft":7,"_partial":7,"_locale":8,"title":27562,"description":27563,"publishDate":27564,"tags":27565,"excerpt":27563,"body":27566,"_type":343,"_id":29327,"_source":345,"_file":29328,"_stem":29329,"_extension":348,"author":29330},"/rbrubaker/2012-06/coffe-backbone-1","Fun with CoffeeScript and Backbone.js : Part 1","CoffeeScript has been all the rage lately and I've been wanting to hop on board the bandwagon. I've also seen Backbone.js mentioned quite a bit and was even more intrigued after listening to this .NET Rocks podcast. I decided to convert some plain JavaScript code I had in a side project to use both CoffeeScript and Backbone.js and see how things went.","2012-06-06",[25879,25880,25881,15],{"type":18,"children":27567,"toc":29325},[27568,27595,27604,27622,27627,27638,28014,28024,28035,28280,28291,28296,28861,28866,28877,29311,29316,29321],{"type":21,"tag":22,"props":27569,"children":27570},{},[27571,27576,27578,27584,27586,27593],{"type":21,"tag":322,"props":27572,"children":27574},{"href":25891,"rel":27573},[326],[27575],{"type":26,"value":25895},{"type":26,"value":27577}," has been all the rage lately and I've been wanting to hop on board the bandwagon. I've also seen ",{"type":21,"tag":322,"props":27579,"children":27582},{"href":27580,"rel":27581},"http://backbonejs.org",[326],[27583],{"type":26,"value":25904},{"type":26,"value":27585}," mentioned quite a bit and was even more intrigued after listening to this ",{"type":21,"tag":322,"props":27587,"children":27590},{"href":27588,"rel":27589},"http://www.dotnetrocks.com/default.aspx?showNum=743",[326],[27591],{"type":26,"value":27592},".NET Rocks podcast",{"type":26,"value":27594},". I decided to convert some plain JavaScript code I had in a side project to use both CoffeeScript and Backbone.js and see how things went.",{"type":21,"tag":22,"props":27596,"children":27597},{},[27598,27599,27603],{"type":26,"value":25930},{"type":21,"tag":322,"props":27600,"children":27601},{"href":25933},[27602],{"type":26,"value":25936},{"type":26,"value":551},{"type":21,"tag":22,"props":27605,"children":27606},{},[27607,27609,27614,27616,27621],{"type":26,"value":27608},"The project is a simplified morse code simulator that animates morse code being sent over a telegraph line. The complete source is available ",{"type":21,"tag":322,"props":27610,"children":27612},{"href":25913,"rel":27611},[326],[27613],{"type":26,"value":2869},{"type":26,"value":27615}," and the running code can be seen ",{"type":21,"tag":322,"props":27617,"children":27619},{"href":25921,"rel":27618},[326],[27620],{"type":26,"value":2869},{"type":26,"value":551},{"type":21,"tag":22,"props":27623,"children":27624},{},[27625],{"type":26,"value":27626},"In this post I'll discuss the code that handles the user input.",{"type":21,"tag":22,"props":27628,"children":27629},{},[27630],{"type":21,"tag":33,"props":27631,"children":27632},{},[27633],{"type":21,"tag":2310,"props":27634,"children":27635},{},[27636],{"type":26,"value":27637},"Initialization",{"type":21,"tag":239,"props":27639,"children":27641},{"className":26052,"code":27640,"language":26054,"meta":8,"style":8},"init = ->\n   decoder = new MorseDecoder\n\n   communicationLine = new CommunicationLine(\n    'decoder': decoder)\n\n   straightKey = new StraightKeyInput(\n    'el': $('#straight-key-div'), \n    'model': communicationLine)\n\n   communicationLineView = new CommunicationLineView(\n    'el': $('#communication-line-div'), \n    'model': communicationLine)\n\n   decoderView = new DecoderView(\n    'el': $('messageBoxDiv'), \n    'model': decoder)\n\n   drawSignalLine()\n\n$(document).ready init\n",[27642],{"type":21,"tag":246,"props":27643,"children":27644},{"__ignoreMap":8},[27645,27661,27682,27689,27714,27731,27738,27763,27793,27810,27817,27841,27869,27884,27891,27915,27943,27958,27965,27977,27984],{"type":21,"tag":455,"props":27646,"children":27647},{"class":457,"line":458},[27648,27653,27657],{"type":21,"tag":455,"props":27649,"children":27650},{"style":1365},[27651],{"type":26,"value":27652},"init",{"type":21,"tag":455,"props":27654,"children":27655},{"style":1349},[27656],{"type":26,"value":2131},{"type":21,"tag":455,"props":27658,"children":27659},{"style":1349},[27660],{"type":26,"value":26098},{"type":21,"tag":455,"props":27662,"children":27663},{"class":457,"line":336},[27664,27669,27673,27677],{"type":21,"tag":455,"props":27665,"children":27666},{"style":4527},[27667],{"type":26,"value":27668},"   decoder",{"type":21,"tag":455,"props":27670,"children":27671},{"style":1349},[27672],{"type":26,"value":2131},{"type":21,"tag":455,"props":27674,"children":27675},{"style":1349},[27676],{"type":26,"value":2183},{"type":21,"tag":455,"props":27678,"children":27679},{"style":1365},[27680],{"type":26,"value":27681}," MorseDecoder\n",{"type":21,"tag":455,"props":27683,"children":27684},{"class":457,"line":333},[27685],{"type":21,"tag":455,"props":27686,"children":27687},{"emptyLinePlaceholder":471},[27688],{"type":26,"value":474},{"type":21,"tag":455,"props":27690,"children":27691},{"class":457,"line":1424},[27692,27697,27701,27705,27710],{"type":21,"tag":455,"props":27693,"children":27694},{"style":4527},[27695],{"type":26,"value":27696},"   communicationLine",{"type":21,"tag":455,"props":27698,"children":27699},{"style":1349},[27700],{"type":26,"value":2131},{"type":21,"tag":455,"props":27702,"children":27703},{"style":1349},[27704],{"type":26,"value":2183},{"type":21,"tag":455,"props":27706,"children":27707},{"style":1365},[27708],{"type":26,"value":27709}," CommunicationLine",{"type":21,"tag":455,"props":27711,"children":27712},{"style":486},[27713],{"type":26,"value":11391},{"type":21,"tag":455,"props":27715,"children":27716},{"class":457,"line":1443},[27717,27722,27726],{"type":21,"tag":455,"props":27718,"children":27719},{"style":492},[27720],{"type":26,"value":27721},"    'decoder'",{"type":21,"tag":455,"props":27723,"children":27724},{"style":1349},[27725],{"type":26,"value":831},{"type":21,"tag":455,"props":27727,"children":27728},{"style":486},[27729],{"type":26,"value":27730}," decoder)\n",{"type":21,"tag":455,"props":27732,"children":27733},{"class":457,"line":1489},[27734],{"type":21,"tag":455,"props":27735,"children":27736},{"emptyLinePlaceholder":471},[27737],{"type":26,"value":474},{"type":21,"tag":455,"props":27739,"children":27740},{"class":457,"line":1506},[27741,27746,27750,27754,27759],{"type":21,"tag":455,"props":27742,"children":27743},{"style":4527},[27744],{"type":26,"value":27745},"   straightKey",{"type":21,"tag":455,"props":27747,"children":27748},{"style":1349},[27749],{"type":26,"value":2131},{"type":21,"tag":455,"props":27751,"children":27752},{"style":1349},[27753],{"type":26,"value":2183},{"type":21,"tag":455,"props":27755,"children":27756},{"style":1365},[27757],{"type":26,"value":27758}," StraightKeyInput",{"type":21,"tag":455,"props":27760,"children":27761},{"style":486},[27762],{"type":26,"value":11391},{"type":21,"tag":455,"props":27764,"children":27765},{"class":457,"line":1547},[27766,27771,27775,27779,27783,27788],{"type":21,"tag":455,"props":27767,"children":27768},{"style":492},[27769],{"type":26,"value":27770},"    'el'",{"type":21,"tag":455,"props":27772,"children":27773},{"style":1349},[27774],{"type":26,"value":831},{"type":21,"tag":455,"props":27776,"children":27777},{"style":1365},[27778],{"type":26,"value":26170},{"type":21,"tag":455,"props":27780,"children":27781},{"style":486},[27782],{"type":26,"value":489},{"type":21,"tag":455,"props":27784,"children":27785},{"style":492},[27786],{"type":26,"value":27787},"'#straight-key-div'",{"type":21,"tag":455,"props":27789,"children":27790},{"style":486},[27791],{"type":26,"value":27792},"), \n",{"type":21,"tag":455,"props":27794,"children":27795},{"class":457,"line":1564},[27796,27801,27805],{"type":21,"tag":455,"props":27797,"children":27798},{"style":492},[27799],{"type":26,"value":27800},"    'model'",{"type":21,"tag":455,"props":27802,"children":27803},{"style":1349},[27804],{"type":26,"value":831},{"type":21,"tag":455,"props":27806,"children":27807},{"style":486},[27808],{"type":26,"value":27809}," communicationLine)\n",{"type":21,"tag":455,"props":27811,"children":27812},{"class":457,"line":1605},[27813],{"type":21,"tag":455,"props":27814,"children":27815},{"emptyLinePlaceholder":471},[27816],{"type":26,"value":474},{"type":21,"tag":455,"props":27818,"children":27819},{"class":457,"line":1622},[27820,27825,27829,27833,27837],{"type":21,"tag":455,"props":27821,"children":27822},{"style":4527},[27823],{"type":26,"value":27824},"   communicationLineView",{"type":21,"tag":455,"props":27826,"children":27827},{"style":1349},[27828],{"type":26,"value":2131},{"type":21,"tag":455,"props":27830,"children":27831},{"style":1349},[27832],{"type":26,"value":2183},{"type":21,"tag":455,"props":27834,"children":27835},{"style":1365},[27836],{"type":26,"value":26838},{"type":21,"tag":455,"props":27838,"children":27839},{"style":486},[27840],{"type":26,"value":11391},{"type":21,"tag":455,"props":27842,"children":27843},{"class":457,"line":1663},[27844,27848,27852,27856,27860,27865],{"type":21,"tag":455,"props":27845,"children":27846},{"style":492},[27847],{"type":26,"value":27770},{"type":21,"tag":455,"props":27849,"children":27850},{"style":1349},[27851],{"type":26,"value":831},{"type":21,"tag":455,"props":27853,"children":27854},{"style":1365},[27855],{"type":26,"value":26170},{"type":21,"tag":455,"props":27857,"children":27858},{"style":486},[27859],{"type":26,"value":489},{"type":21,"tag":455,"props":27861,"children":27862},{"style":492},[27863],{"type":26,"value":27864},"'#communication-line-div'",{"type":21,"tag":455,"props":27866,"children":27867},{"style":486},[27868],{"type":26,"value":27792},{"type":21,"tag":455,"props":27870,"children":27871},{"class":457,"line":1680},[27872,27876,27880],{"type":21,"tag":455,"props":27873,"children":27874},{"style":492},[27875],{"type":26,"value":27800},{"type":21,"tag":455,"props":27877,"children":27878},{"style":1349},[27879],{"type":26,"value":831},{"type":21,"tag":455,"props":27881,"children":27882},{"style":486},[27883],{"type":26,"value":27809},{"type":21,"tag":455,"props":27885,"children":27886},{"class":457,"line":1721},[27887],{"type":21,"tag":455,"props":27888,"children":27889},{"emptyLinePlaceholder":471},[27890],{"type":26,"value":474},{"type":21,"tag":455,"props":27892,"children":27893},{"class":457,"line":1738},[27894,27899,27903,27907,27911],{"type":21,"tag":455,"props":27895,"children":27896},{"style":4527},[27897],{"type":26,"value":27898},"   decoderView",{"type":21,"tag":455,"props":27900,"children":27901},{"style":1349},[27902],{"type":26,"value":2131},{"type":21,"tag":455,"props":27904,"children":27905},{"style":1349},[27906],{"type":26,"value":2183},{"type":21,"tag":455,"props":27908,"children":27909},{"style":1365},[27910],{"type":26,"value":26071},{"type":21,"tag":455,"props":27912,"children":27913},{"style":486},[27914],{"type":26,"value":11391},{"type":21,"tag":455,"props":27916,"children":27917},{"class":457,"line":1779},[27918,27922,27926,27930,27934,27939],{"type":21,"tag":455,"props":27919,"children":27920},{"style":492},[27921],{"type":26,"value":27770},{"type":21,"tag":455,"props":27923,"children":27924},{"style":1349},[27925],{"type":26,"value":831},{"type":21,"tag":455,"props":27927,"children":27928},{"style":1365},[27929],{"type":26,"value":26170},{"type":21,"tag":455,"props":27931,"children":27932},{"style":486},[27933],{"type":26,"value":489},{"type":21,"tag":455,"props":27935,"children":27936},{"style":492},[27937],{"type":26,"value":27938},"'messageBoxDiv'",{"type":21,"tag":455,"props":27940,"children":27941},{"style":486},[27942],{"type":26,"value":27792},{"type":21,"tag":455,"props":27944,"children":27945},{"class":457,"line":1796},[27946,27950,27954],{"type":21,"tag":455,"props":27947,"children":27948},{"style":492},[27949],{"type":26,"value":27800},{"type":21,"tag":455,"props":27951,"children":27952},{"style":1349},[27953],{"type":26,"value":831},{"type":21,"tag":455,"props":27955,"children":27956},{"style":486},[27957],{"type":26,"value":27730},{"type":21,"tag":455,"props":27959,"children":27960},{"class":457,"line":1837},[27961],{"type":21,"tag":455,"props":27962,"children":27963},{"emptyLinePlaceholder":471},[27964],{"type":26,"value":474},{"type":21,"tag":455,"props":27966,"children":27967},{"class":457,"line":1854},[27968,27973],{"type":21,"tag":455,"props":27969,"children":27970},{"style":1365},[27971],{"type":26,"value":27972},"   drawSignalLine",{"type":21,"tag":455,"props":27974,"children":27975},{"style":486},[27976],{"type":26,"value":4506},{"type":21,"tag":455,"props":27978,"children":27979},{"class":457,"line":1895},[27980],{"type":21,"tag":455,"props":27981,"children":27982},{"emptyLinePlaceholder":471},[27983],{"type":26,"value":474},{"type":21,"tag":455,"props":27985,"children":27986},{"class":457,"line":1912},[27987,27991,27995,28000,28004,28009],{"type":21,"tag":455,"props":27988,"children":27989},{"style":1365},[27990],{"type":26,"value":5770},{"type":21,"tag":455,"props":27992,"children":27993},{"style":486},[27994],{"type":26,"value":489},{"type":21,"tag":455,"props":27996,"children":27997},{"style":480},[27998],{"type":26,"value":27999},"document",{"type":21,"tag":455,"props":28001,"children":28002},{"style":486},[28003],{"type":26,"value":5081},{"type":21,"tag":455,"props":28005,"children":28006},{"style":1365},[28007],{"type":26,"value":28008},"ready",{"type":21,"tag":455,"props":28010,"children":28011},{"style":486},[28012],{"type":26,"value":28013}," init\n",{"type":21,"tag":22,"props":28015,"children":28016},{},[28017,28018,28022],{"type":26,"value":8737},{"type":21,"tag":33,"props":28019,"children":28020},{},[28021],{"type":26,"value":27652},{"type":26,"value":28023}," function sets up all of the Backbone.js model/view classes. Note that each Backbone view is initialized with both its corresponding HTML element and corresponding model object.",{"type":21,"tag":22,"props":28025,"children":28026},{},[28027,28029,28033],{"type":26,"value":28028},"The ",{"type":21,"tag":33,"props":28030,"children":28031},{},[28032],{"type":26,"value":27652},{"type":26,"value":28034}," function also draws the \"communication line\" in a canvas element.",{"type":21,"tag":239,"props":28036,"children":28038},{"className":26052,"code":28037,"language":26054,"meta":8,"style":8},"drawSignalLine = ->\n   context = document.getElementById(\n    \"communicationLineCanvas\").getContext('2d')\n\n   context.clearRect(0, 0, \n    context.canvas.width, context.canvas.height)\n\n   context.moveTo(0, 30)\n   context.lineTo(500, 30)\n   context.strokeStyle = \"#000\"\n   context.closePath()\n   context.stroke()\n",[28039],{"type":21,"tag":246,"props":28040,"children":28041},{"__ignoreMap":8},[28042,28058,28086,28114,28121,28153,28161,28168,28200,28233,28250,28265],{"type":21,"tag":455,"props":28043,"children":28044},{"class":457,"line":458},[28045,28050,28054],{"type":21,"tag":455,"props":28046,"children":28047},{"style":1365},[28048],{"type":26,"value":28049},"drawSignalLine",{"type":21,"tag":455,"props":28051,"children":28052},{"style":1349},[28053],{"type":26,"value":2131},{"type":21,"tag":455,"props":28055,"children":28056},{"style":1349},[28057],{"type":26,"value":26098},{"type":21,"tag":455,"props":28059,"children":28060},{"class":457,"line":336},[28061,28066,28070,28074,28078,28082],{"type":21,"tag":455,"props":28062,"children":28063},{"style":4527},[28064],{"type":26,"value":28065},"   context",{"type":21,"tag":455,"props":28067,"children":28068},{"style":1349},[28069],{"type":26,"value":2131},{"type":21,"tag":455,"props":28071,"children":28072},{"style":480},[28073],{"type":26,"value":26929},{"type":21,"tag":455,"props":28075,"children":28076},{"style":486},[28077],{"type":26,"value":551},{"type":21,"tag":455,"props":28079,"children":28080},{"style":480},[28081],{"type":26,"value":26938},{"type":21,"tag":455,"props":28083,"children":28084},{"style":486},[28085],{"type":26,"value":11391},{"type":21,"tag":455,"props":28087,"children":28088},{"class":457,"line":333},[28089,28094,28098,28102,28106,28110],{"type":21,"tag":455,"props":28090,"children":28091},{"style":492},[28092],{"type":26,"value":28093},"    \"communicationLineCanvas\"",{"type":21,"tag":455,"props":28095,"children":28096},{"style":486},[28097],{"type":26,"value":5081},{"type":21,"tag":455,"props":28099,"children":28100},{"style":480},[28101],{"type":26,"value":26959},{"type":21,"tag":455,"props":28103,"children":28104},{"style":486},[28105],{"type":26,"value":489},{"type":21,"tag":455,"props":28107,"children":28108},{"style":492},[28109],{"type":26,"value":26968},{"type":21,"tag":455,"props":28111,"children":28112},{"style":486},[28113],{"type":26,"value":500},{"type":21,"tag":455,"props":28115,"children":28116},{"class":457,"line":1424},[28117],{"type":21,"tag":455,"props":28118,"children":28119},{"emptyLinePlaceholder":471},[28120],{"type":26,"value":474},{"type":21,"tag":455,"props":28122,"children":28123},{"class":457,"line":1443},[28124,28129,28133,28137,28141,28145,28149],{"type":21,"tag":455,"props":28125,"children":28126},{"style":486},[28127],{"type":26,"value":28128},"   context.",{"type":21,"tag":455,"props":28130,"children":28131},{"style":1365},[28132],{"type":26,"value":26992},{"type":21,"tag":455,"props":28134,"children":28135},{"style":486},[28136],{"type":26,"value":489},{"type":21,"tag":455,"props":28138,"children":28139},{"style":480},[28140],{"type":26,"value":7863},{"type":21,"tag":455,"props":28142,"children":28143},{"style":486},[28144],{"type":26,"value":2228},{"type":21,"tag":455,"props":28146,"children":28147},{"style":480},[28148],{"type":26,"value":7863},{"type":21,"tag":455,"props":28150,"children":28151},{"style":486},[28152],{"type":26,"value":27231},{"type":21,"tag":455,"props":28154,"children":28155},{"class":457,"line":1489},[28156],{"type":21,"tag":455,"props":28157,"children":28158},{"style":486},[28159],{"type":26,"value":28160},"    context.canvas.width, context.canvas.height)\n",{"type":21,"tag":455,"props":28162,"children":28163},{"class":457,"line":1506},[28164],{"type":21,"tag":455,"props":28165,"children":28166},{"emptyLinePlaceholder":471},[28167],{"type":26,"value":474},{"type":21,"tag":455,"props":28169,"children":28170},{"class":457,"line":1547},[28171,28175,28179,28183,28187,28191,28196],{"type":21,"tag":455,"props":28172,"children":28173},{"style":486},[28174],{"type":26,"value":28128},{"type":21,"tag":455,"props":28176,"children":28177},{"style":480},[28178],{"type":26,"value":27127},{"type":21,"tag":455,"props":28180,"children":28181},{"style":486},[28182],{"type":26,"value":489},{"type":21,"tag":455,"props":28184,"children":28185},{"style":480},[28186],{"type":26,"value":7863},{"type":21,"tag":455,"props":28188,"children":28189},{"style":486},[28190],{"type":26,"value":2228},{"type":21,"tag":455,"props":28192,"children":28193},{"style":480},[28194],{"type":26,"value":28195},"30",{"type":21,"tag":455,"props":28197,"children":28198},{"style":486},[28199],{"type":26,"value":500},{"type":21,"tag":455,"props":28201,"children":28202},{"class":457,"line":1564},[28203,28207,28212,28216,28221,28225,28229],{"type":21,"tag":455,"props":28204,"children":28205},{"style":486},[28206],{"type":26,"value":28128},{"type":21,"tag":455,"props":28208,"children":28209},{"style":1365},[28210],{"type":26,"value":28211},"lineTo",{"type":21,"tag":455,"props":28213,"children":28214},{"style":486},[28215],{"type":26,"value":489},{"type":21,"tag":455,"props":28217,"children":28218},{"style":480},[28219],{"type":26,"value":28220},"500",{"type":21,"tag":455,"props":28222,"children":28223},{"style":486},[28224],{"type":26,"value":2228},{"type":21,"tag":455,"props":28226,"children":28227},{"style":480},[28228],{"type":26,"value":28195},{"type":21,"tag":455,"props":28230,"children":28231},{"style":486},[28232],{"type":26,"value":500},{"type":21,"tag":455,"props":28234,"children":28235},{"class":457,"line":1605},[28236,28241,28245],{"type":21,"tag":455,"props":28237,"children":28238},{"style":486},[28239],{"type":26,"value":28240},"   context.strokeStyle ",{"type":21,"tag":455,"props":28242,"children":28243},{"style":1349},[28244],{"type":26,"value":3193},{"type":21,"tag":455,"props":28246,"children":28247},{"style":492},[28248],{"type":26,"value":28249}," \"#000\"\n",{"type":21,"tag":455,"props":28251,"children":28252},{"class":457,"line":1622},[28253,28257,28261],{"type":21,"tag":455,"props":28254,"children":28255},{"style":486},[28256],{"type":26,"value":28128},{"type":21,"tag":455,"props":28258,"children":28259},{"style":1365},[28260],{"type":26,"value":27297},{"type":21,"tag":455,"props":28262,"children":28263},{"style":486},[28264],{"type":26,"value":4506},{"type":21,"tag":455,"props":28266,"children":28267},{"class":457,"line":1663},[28268,28272,28276],{"type":21,"tag":455,"props":28269,"children":28270},{"style":486},[28271],{"type":26,"value":28128},{"type":21,"tag":455,"props":28273,"children":28274},{"style":1365},[28275],{"type":26,"value":27329},{"type":21,"tag":455,"props":28277,"children":28278},{"style":486},[28279],{"type":26,"value":4506},{"type":21,"tag":22,"props":28281,"children":28282},{},[28283],{"type":21,"tag":33,"props":28284,"children":28285},{},[28286],{"type":21,"tag":2310,"props":28287,"children":28288},{},[28289],{"type":26,"value":28290},"Input View",{"type":21,"tag":22,"props":28292,"children":28293},{},[28294],{"type":26,"value":28295},"The application processes user input using the StraighKeyInput class, a Backbone view class associated with an input button:",{"type":21,"tag":239,"props":28297,"children":28299},{"className":20016,"code":28298,"language":20018,"meta":8,"style":8},"class StraightKeyInput extends Backbone.View\n\n   initialize: ->\n      @dashTimer = null\n      @dashFlag = false\n      @wordStopTimer = null\n      @wordStopFlag = false\n\n   events:\n      'mousedown #straight-key': 'startTimers',\n      'mouseup #straight-key': 'sendUserInput'\n\n   startTimers: =>\n      @dashTimer = setTimeout(@dashTimerExpired, 250)\n      @wordStopTimer = setTimeout(\n       @wordStopTimerExpired, 1000)\n\n   dashTimerExpired: =>\n      @dashFlag = true\n\n   wordStopTimerExpired: =>\n      @wordStopFlag = true\n\n   sendUserInput: =>\n      if @wordStopFlag\n         @model.addToken(kWordStopToken)\n      else if @dashFlag\n         @model.addToken(kDashToken)\n      else\n         @model.addToken(kDotToken)\n\n      clearTimeout(@dashTimer)\n      clearTimeout(@wordStopTimer)\n      @dashFlag = false\n      @wordStopFlag = false\n",[28300],{"type":21,"tag":246,"props":28301,"children":28302},{"__ignoreMap":8},[28303,28332,28339,28354,28374,28395,28415,28435,28442,28454,28475,28492,28499,28515,28549,28572,28589,28596,28612,28632,28639,28655,28674,28681,28697,28714,28732,28752,28768,28775,28791,28798,28811,28823,28842],{"type":21,"tag":455,"props":28304,"children":28305},{"class":457,"line":458},[28306,28310,28314,28318,28323,28327],{"type":21,"tag":455,"props":28307,"children":28308},{"style":1349},[28309],{"type":26,"value":26066},{"type":21,"tag":455,"props":28311,"children":28312},{"style":1365},[28313],{"type":26,"value":27758},{"type":21,"tag":455,"props":28315,"children":28316},{"style":1349},[28317],{"type":26,"value":26076},{"type":21,"tag":455,"props":28319,"children":28320},{"style":1365},[28321],{"type":26,"value":28322}," Backbone",{"type":21,"tag":455,"props":28324,"children":28325},{"style":486},[28326],{"type":26,"value":551},{"type":21,"tag":455,"props":28328,"children":28329},{"style":1365},[28330],{"type":26,"value":28331},"View\n",{"type":21,"tag":455,"props":28333,"children":28334},{"class":457,"line":336},[28335],{"type":21,"tag":455,"props":28336,"children":28337},{"emptyLinePlaceholder":471},[28338],{"type":26,"value":474},{"type":21,"tag":455,"props":28340,"children":28341},{"class":457,"line":333},[28342,28346,28350],{"type":21,"tag":455,"props":28343,"children":28344},{"style":1365},[28345],{"type":26,"value":26089},{"type":21,"tag":455,"props":28347,"children":28348},{"style":486},[28349],{"type":26,"value":7129},{"type":21,"tag":455,"props":28351,"children":28352},{"style":1349},[28353],{"type":26,"value":27077},{"type":21,"tag":455,"props":28355,"children":28356},{"class":457,"line":1424},[28357,28361,28366,28370],{"type":21,"tag":455,"props":28358,"children":28359},{"style":486},[28360],{"type":26,"value":26396},{"type":21,"tag":455,"props":28362,"children":28363},{"style":1365},[28364],{"type":26,"value":28365},"dashTimer",{"type":21,"tag":455,"props":28367,"children":28368},{"style":1349},[28369],{"type":26,"value":2131},{"type":21,"tag":455,"props":28371,"children":28372},{"style":480},[28373],{"type":26,"value":7628},{"type":21,"tag":455,"props":28375,"children":28376},{"class":457,"line":1443},[28377,28381,28386,28390],{"type":21,"tag":455,"props":28378,"children":28379},{"style":486},[28380],{"type":26,"value":26396},{"type":21,"tag":455,"props":28382,"children":28383},{"style":1365},[28384],{"type":26,"value":28385},"dashFlag",{"type":21,"tag":455,"props":28387,"children":28388},{"style":1349},[28389],{"type":26,"value":2131},{"type":21,"tag":455,"props":28391,"children":28392},{"style":480},[28393],{"type":26,"value":28394}," false\n",{"type":21,"tag":455,"props":28396,"children":28397},{"class":457,"line":1489},[28398,28402,28407,28411],{"type":21,"tag":455,"props":28399,"children":28400},{"style":486},[28401],{"type":26,"value":26396},{"type":21,"tag":455,"props":28403,"children":28404},{"style":1365},[28405],{"type":26,"value":28406},"wordStopTimer",{"type":21,"tag":455,"props":28408,"children":28409},{"style":1349},[28410],{"type":26,"value":2131},{"type":21,"tag":455,"props":28412,"children":28413},{"style":480},[28414],{"type":26,"value":7628},{"type":21,"tag":455,"props":28416,"children":28417},{"class":457,"line":1506},[28418,28422,28427,28431],{"type":21,"tag":455,"props":28419,"children":28420},{"style":486},[28421],{"type":26,"value":26396},{"type":21,"tag":455,"props":28423,"children":28424},{"style":1365},[28425],{"type":26,"value":28426},"wordStopFlag",{"type":21,"tag":455,"props":28428,"children":28429},{"style":1349},[28430],{"type":26,"value":2131},{"type":21,"tag":455,"props":28432,"children":28433},{"style":480},[28434],{"type":26,"value":28394},{"type":21,"tag":455,"props":28436,"children":28437},{"class":457,"line":1547},[28438],{"type":21,"tag":455,"props":28439,"children":28440},{"emptyLinePlaceholder":471},[28441],{"type":26,"value":474},{"type":21,"tag":455,"props":28443,"children":28444},{"class":457,"line":1564},[28445,28450],{"type":21,"tag":455,"props":28446,"children":28447},{"style":1365},[28448],{"type":26,"value":28449},"   events",{"type":21,"tag":455,"props":28451,"children":28452},{"style":486},[28453],{"type":26,"value":17151},{"type":21,"tag":455,"props":28455,"children":28456},{"class":457,"line":1605},[28457,28462,28466,28471],{"type":21,"tag":455,"props":28458,"children":28459},{"style":492},[28460],{"type":26,"value":28461},"      'mousedown #straight-key'",{"type":21,"tag":455,"props":28463,"children":28464},{"style":486},[28465],{"type":26,"value":7129},{"type":21,"tag":455,"props":28467,"children":28468},{"style":492},[28469],{"type":26,"value":28470},"'startTimers'",{"type":21,"tag":455,"props":28472,"children":28473},{"style":486},[28474],{"type":26,"value":2483},{"type":21,"tag":455,"props":28476,"children":28477},{"class":457,"line":1622},[28478,28483,28487],{"type":21,"tag":455,"props":28479,"children":28480},{"style":492},[28481],{"type":26,"value":28482},"      'mouseup #straight-key'",{"type":21,"tag":455,"props":28484,"children":28485},{"style":486},[28486],{"type":26,"value":7129},{"type":21,"tag":455,"props":28488,"children":28489},{"style":492},[28490],{"type":26,"value":28491},"'sendUserInput'\n",{"type":21,"tag":455,"props":28493,"children":28494},{"class":457,"line":1663},[28495],{"type":21,"tag":455,"props":28496,"children":28497},{"emptyLinePlaceholder":471},[28498],{"type":26,"value":474},{"type":21,"tag":455,"props":28500,"children":28501},{"class":457,"line":1680},[28502,28507,28511],{"type":21,"tag":455,"props":28503,"children":28504},{"style":1365},[28505],{"type":26,"value":28506},"   startTimers",{"type":21,"tag":455,"props":28508,"children":28509},{"style":486},[28510],{"type":26,"value":7129},{"type":21,"tag":455,"props":28512,"children":28513},{"style":1349},[28514],{"type":26,"value":26153},{"type":21,"tag":455,"props":28516,"children":28517},{"class":457,"line":1721},[28518,28522,28526,28530,28535,28540,28545],{"type":21,"tag":455,"props":28519,"children":28520},{"style":486},[28521],{"type":26,"value":26396},{"type":21,"tag":455,"props":28523,"children":28524},{"style":1365},[28525],{"type":26,"value":28365},{"type":21,"tag":455,"props":28527,"children":28528},{"style":1349},[28529],{"type":26,"value":2131},{"type":21,"tag":455,"props":28531,"children":28532},{"style":1365},[28533],{"type":26,"value":28534}," setTimeout",{"type":21,"tag":455,"props":28536,"children":28537},{"style":486},[28538],{"type":26,"value":28539},"(@dashTimerExpired, ",{"type":21,"tag":455,"props":28541,"children":28542},{"style":480},[28543],{"type":26,"value":28544},"250",{"type":21,"tag":455,"props":28546,"children":28547},{"style":486},[28548],{"type":26,"value":500},{"type":21,"tag":455,"props":28550,"children":28551},{"class":457,"line":1738},[28552,28556,28560,28564,28568],{"type":21,"tag":455,"props":28553,"children":28554},{"style":486},[28555],{"type":26,"value":26396},{"type":21,"tag":455,"props":28557,"children":28558},{"style":1365},[28559],{"type":26,"value":28406},{"type":21,"tag":455,"props":28561,"children":28562},{"style":1349},[28563],{"type":26,"value":2131},{"type":21,"tag":455,"props":28565,"children":28566},{"style":1365},[28567],{"type":26,"value":28534},{"type":21,"tag":455,"props":28569,"children":28570},{"style":486},[28571],{"type":26,"value":11391},{"type":21,"tag":455,"props":28573,"children":28574},{"class":457,"line":1779},[28575,28580,28585],{"type":21,"tag":455,"props":28576,"children":28577},{"style":486},[28578],{"type":26,"value":28579},"       @wordStopTimerExpired, ",{"type":21,"tag":455,"props":28581,"children":28582},{"style":480},[28583],{"type":26,"value":28584},"1000",{"type":21,"tag":455,"props":28586,"children":28587},{"style":486},[28588],{"type":26,"value":500},{"type":21,"tag":455,"props":28590,"children":28591},{"class":457,"line":1796},[28592],{"type":21,"tag":455,"props":28593,"children":28594},{"emptyLinePlaceholder":471},[28595],{"type":26,"value":474},{"type":21,"tag":455,"props":28597,"children":28598},{"class":457,"line":1837},[28599,28604,28608],{"type":21,"tag":455,"props":28600,"children":28601},{"style":1365},[28602],{"type":26,"value":28603},"   dashTimerExpired",{"type":21,"tag":455,"props":28605,"children":28606},{"style":486},[28607],{"type":26,"value":7129},{"type":21,"tag":455,"props":28609,"children":28610},{"style":1349},[28611],{"type":26,"value":26153},{"type":21,"tag":455,"props":28613,"children":28614},{"class":457,"line":1854},[28615,28619,28623,28627],{"type":21,"tag":455,"props":28616,"children":28617},{"style":486},[28618],{"type":26,"value":26396},{"type":21,"tag":455,"props":28620,"children":28621},{"style":1365},[28622],{"type":26,"value":28385},{"type":21,"tag":455,"props":28624,"children":28625},{"style":1349},[28626],{"type":26,"value":2131},{"type":21,"tag":455,"props":28628,"children":28629},{"style":480},[28630],{"type":26,"value":28631}," true\n",{"type":21,"tag":455,"props":28633,"children":28634},{"class":457,"line":1895},[28635],{"type":21,"tag":455,"props":28636,"children":28637},{"emptyLinePlaceholder":471},[28638],{"type":26,"value":474},{"type":21,"tag":455,"props":28640,"children":28641},{"class":457,"line":1912},[28642,28647,28651],{"type":21,"tag":455,"props":28643,"children":28644},{"style":1365},[28645],{"type":26,"value":28646},"   wordStopTimerExpired",{"type":21,"tag":455,"props":28648,"children":28649},{"style":486},[28650],{"type":26,"value":7129},{"type":21,"tag":455,"props":28652,"children":28653},{"style":1349},[28654],{"type":26,"value":26153},{"type":21,"tag":455,"props":28656,"children":28657},{"class":457,"line":1953},[28658,28662,28666,28670],{"type":21,"tag":455,"props":28659,"children":28660},{"style":486},[28661],{"type":26,"value":26396},{"type":21,"tag":455,"props":28663,"children":28664},{"style":1365},[28665],{"type":26,"value":28426},{"type":21,"tag":455,"props":28667,"children":28668},{"style":1349},[28669],{"type":26,"value":2131},{"type":21,"tag":455,"props":28671,"children":28672},{"style":480},[28673],{"type":26,"value":28631},{"type":21,"tag":455,"props":28675,"children":28676},{"class":457,"line":1970},[28677],{"type":21,"tag":455,"props":28678,"children":28679},{"emptyLinePlaceholder":471},[28680],{"type":26,"value":474},{"type":21,"tag":455,"props":28682,"children":28683},{"class":457,"line":1978},[28684,28689,28693],{"type":21,"tag":455,"props":28685,"children":28686},{"style":1365},[28687],{"type":26,"value":28688},"   sendUserInput",{"type":21,"tag":455,"props":28690,"children":28691},{"style":486},[28692],{"type":26,"value":7129},{"type":21,"tag":455,"props":28694,"children":28695},{"style":1349},[28696],{"type":26,"value":26153},{"type":21,"tag":455,"props":28698,"children":28699},{"class":457,"line":1996},[28700,28704,28709],{"type":21,"tag":455,"props":28701,"children":28702},{"style":1365},[28703],{"type":26,"value":8273},{"type":21,"tag":455,"props":28705,"children":28706},{"style":486},[28707],{"type":26,"value":28708}," @",{"type":21,"tag":455,"props":28710,"children":28711},{"style":1365},[28712],{"type":26,"value":28713},"wordStopFlag\n",{"type":21,"tag":455,"props":28715,"children":28716},{"class":457,"line":4487},[28717,28722,28727],{"type":21,"tag":455,"props":28718,"children":28719},{"style":486},[28720],{"type":26,"value":28721},"         @model.",{"type":21,"tag":455,"props":28723,"children":28724},{"style":1365},[28725],{"type":26,"value":28726},"addToken",{"type":21,"tag":455,"props":28728,"children":28729},{"style":486},[28730],{"type":26,"value":28731},"(kWordStopToken)\n",{"type":21,"tag":455,"props":28733,"children":28734},{"class":457,"line":4495},[28735,28739,28743,28747],{"type":21,"tag":455,"props":28736,"children":28737},{"style":1365},[28738],{"type":26,"value":26508},{"type":21,"tag":455,"props":28740,"children":28741},{"style":1365},[28742],{"type":26,"value":26513},{"type":21,"tag":455,"props":28744,"children":28745},{"style":486},[28746],{"type":26,"value":28708},{"type":21,"tag":455,"props":28748,"children":28749},{"style":1365},[28750],{"type":26,"value":28751},"dashFlag\n",{"type":21,"tag":455,"props":28753,"children":28754},{"class":457,"line":4509},[28755,28759,28763],{"type":21,"tag":455,"props":28756,"children":28757},{"style":486},[28758],{"type":26,"value":28721},{"type":21,"tag":455,"props":28760,"children":28761},{"style":1365},[28762],{"type":26,"value":28726},{"type":21,"tag":455,"props":28764,"children":28765},{"style":486},[28766],{"type":26,"value":28767},"(kDashToken)\n",{"type":21,"tag":455,"props":28769,"children":28770},{"class":457,"line":4561},[28771],{"type":21,"tag":455,"props":28772,"children":28773},{"style":1365},[28774],{"type":26,"value":26598},{"type":21,"tag":455,"props":28776,"children":28777},{"class":457,"line":4596},[28778,28782,28786],{"type":21,"tag":455,"props":28779,"children":28780},{"style":486},[28781],{"type":26,"value":28721},{"type":21,"tag":455,"props":28783,"children":28784},{"style":1365},[28785],{"type":26,"value":28726},{"type":21,"tag":455,"props":28787,"children":28788},{"style":486},[28789],{"type":26,"value":28790},"(kDotToken)\n",{"type":21,"tag":455,"props":28792,"children":28793},{"class":457,"line":4627},[28794],{"type":21,"tag":455,"props":28795,"children":28796},{"emptyLinePlaceholder":471},[28797],{"type":26,"value":474},{"type":21,"tag":455,"props":28799,"children":28800},{"class":457,"line":5942},[28801,28806],{"type":21,"tag":455,"props":28802,"children":28803},{"style":1365},[28804],{"type":26,"value":28805},"      clearTimeout",{"type":21,"tag":455,"props":28807,"children":28808},{"style":486},[28809],{"type":26,"value":28810},"(@dashTimer)\n",{"type":21,"tag":455,"props":28812,"children":28813},{"class":457,"line":5965},[28814,28818],{"type":21,"tag":455,"props":28815,"children":28816},{"style":1365},[28817],{"type":26,"value":28805},{"type":21,"tag":455,"props":28819,"children":28820},{"style":486},[28821],{"type":26,"value":28822},"(@wordStopTimer)\n",{"type":21,"tag":455,"props":28824,"children":28825},{"class":457,"line":5983},[28826,28830,28834,28838],{"type":21,"tag":455,"props":28827,"children":28828},{"style":486},[28829],{"type":26,"value":26396},{"type":21,"tag":455,"props":28831,"children":28832},{"style":1365},[28833],{"type":26,"value":28385},{"type":21,"tag":455,"props":28835,"children":28836},{"style":1349},[28837],{"type":26,"value":2131},{"type":21,"tag":455,"props":28839,"children":28840},{"style":480},[28841],{"type":26,"value":28394},{"type":21,"tag":455,"props":28843,"children":28844},{"class":457,"line":6002},[28845,28849,28853,28857],{"type":21,"tag":455,"props":28846,"children":28847},{"style":486},[28848],{"type":26,"value":26396},{"type":21,"tag":455,"props":28850,"children":28851},{"style":1365},[28852],{"type":26,"value":28426},{"type":21,"tag":455,"props":28854,"children":28855},{"style":1349},[28856],{"type":26,"value":2131},{"type":21,"tag":455,"props":28858,"children":28859},{"style":480},[28860],{"type":26,"value":28394},{"type":21,"tag":22,"props":28862,"children":28863},{},[28864],{"type":26,"value":28865},"The class uses two timers to determine which \"token\" a user intends to send across the line. Using Backbone's declarative style for event binding, I bind the mousedown event to a function that starts both timers. The mouseup event is bound to a function that pushes the appropriate token to the model based on how long the user held down the input button.",{"type":21,"tag":22,"props":28867,"children":28868},{},[28869],{"type":21,"tag":33,"props":28870,"children":28871},{},[28872],{"type":21,"tag":2310,"props":28873,"children":28874},{},[28875],{"type":26,"value":28876},"Input Model",{"type":21,"tag":239,"props":28878,"children":28880},{"className":26052,"code":28879,"language":26054,"meta":8,"style":8},"class CommunicationLine extends Backbone.Model\n   initialize: (options) =>\n      @inputQueue = []\n\n      @communicationLine = \n       ['', '', '', '', '', '', '', '', '', '']\n\n      @decoder = options.decoder\n      setInterval(@moveDataOneStep, 100)\n\n   addToken: (token) =>\n      @inputQueue.push(token)\n\n   moveDataOneStep: =>\n      @decoder.processToken(@communicationLine.pop())\n      nextToken = @inputQueue.shift()\n\n      @communicationLine.unshift(\n       if undefined == nextToken then '' else nextToken)\n\n      @trigger('hasNewData', @communicationLine)\n",[28881],{"type":21,"tag":246,"props":28882,"children":28883},{"__ignoreMap":8},[28884,28903,28923,28939,28946,28962,29050,29057,29074,29096,29103,29123,29139,29146,29162,29189,29215,29222,29239,29280,29287],{"type":21,"tag":455,"props":28885,"children":28886},{"class":457,"line":458},[28887,28891,28895,28899],{"type":21,"tag":455,"props":28888,"children":28889},{"style":1349},[28890],{"type":26,"value":26066},{"type":21,"tag":455,"props":28892,"children":28893},{"style":1365},[28894],{"type":26,"value":27709},{"type":21,"tag":455,"props":28896,"children":28897},{"style":1349},[28898],{"type":26,"value":26076},{"type":21,"tag":455,"props":28900,"children":28901},{"style":1365},[28902],{"type":26,"value":26277},{"type":21,"tag":455,"props":28904,"children":28905},{"class":457,"line":336},[28906,28910,28914,28919],{"type":21,"tag":455,"props":28907,"children":28908},{"style":1365},[28909],{"type":26,"value":26089},{"type":21,"tag":455,"props":28911,"children":28912},{"style":1349},[28913],{"type":26,"value":831},{"type":21,"tag":455,"props":28915,"children":28916},{"style":486},[28917],{"type":26,"value":28918}," (options) ",{"type":21,"tag":455,"props":28920,"children":28921},{"style":1349},[28922],{"type":26,"value":26153},{"type":21,"tag":455,"props":28924,"children":28925},{"class":457,"line":333},[28926,28931,28935],{"type":21,"tag":455,"props":28927,"children":28928},{"style":486},[28929],{"type":26,"value":28930},"      @inputQueue ",{"type":21,"tag":455,"props":28932,"children":28933},{"style":1349},[28934],{"type":26,"value":3193},{"type":21,"tag":455,"props":28936,"children":28937},{"style":486},[28938],{"type":26,"value":26371},{"type":21,"tag":455,"props":28940,"children":28941},{"class":457,"line":1424},[28942],{"type":21,"tag":455,"props":28943,"children":28944},{"emptyLinePlaceholder":471},[28945],{"type":26,"value":474},{"type":21,"tag":455,"props":28947,"children":28948},{"class":457,"line":1443},[28949,28954,28958],{"type":21,"tag":455,"props":28950,"children":28951},{"style":486},[28952],{"type":26,"value":28953},"      @communicationLine ",{"type":21,"tag":455,"props":28955,"children":28956},{"style":1349},[28957],{"type":26,"value":3193},{"type":21,"tag":455,"props":28959,"children":28960},{"style":486},[28961],{"type":26,"value":25640},{"type":21,"tag":455,"props":28963,"children":28964},{"class":457,"line":1489},[28965,28970,28974,28978,28982,28986,28990,28994,28998,29002,29006,29010,29014,29018,29022,29026,29030,29034,29038,29042,29046],{"type":21,"tag":455,"props":28966,"children":28967},{"style":486},[28968],{"type":26,"value":28969},"       [",{"type":21,"tag":455,"props":28971,"children":28972},{"style":492},[28973],{"type":26,"value":10085},{"type":21,"tag":455,"props":28975,"children":28976},{"style":486},[28977],{"type":26,"value":2228},{"type":21,"tag":455,"props":28979,"children":28980},{"style":492},[28981],{"type":26,"value":10085},{"type":21,"tag":455,"props":28983,"children":28984},{"style":486},[28985],{"type":26,"value":2228},{"type":21,"tag":455,"props":28987,"children":28988},{"style":492},[28989],{"type":26,"value":10085},{"type":21,"tag":455,"props":28991,"children":28992},{"style":486},[28993],{"type":26,"value":2228},{"type":21,"tag":455,"props":28995,"children":28996},{"style":492},[28997],{"type":26,"value":10085},{"type":21,"tag":455,"props":28999,"children":29000},{"style":486},[29001],{"type":26,"value":2228},{"type":21,"tag":455,"props":29003,"children":29004},{"style":492},[29005],{"type":26,"value":10085},{"type":21,"tag":455,"props":29007,"children":29008},{"style":486},[29009],{"type":26,"value":2228},{"type":21,"tag":455,"props":29011,"children":29012},{"style":492},[29013],{"type":26,"value":10085},{"type":21,"tag":455,"props":29015,"children":29016},{"style":486},[29017],{"type":26,"value":2228},{"type":21,"tag":455,"props":29019,"children":29020},{"style":492},[29021],{"type":26,"value":10085},{"type":21,"tag":455,"props":29023,"children":29024},{"style":486},[29025],{"type":26,"value":2228},{"type":21,"tag":455,"props":29027,"children":29028},{"style":492},[29029],{"type":26,"value":10085},{"type":21,"tag":455,"props":29031,"children":29032},{"style":486},[29033],{"type":26,"value":2228},{"type":21,"tag":455,"props":29035,"children":29036},{"style":492},[29037],{"type":26,"value":10085},{"type":21,"tag":455,"props":29039,"children":29040},{"style":486},[29041],{"type":26,"value":2228},{"type":21,"tag":455,"props":29043,"children":29044},{"style":492},[29045],{"type":26,"value":10085},{"type":21,"tag":455,"props":29047,"children":29048},{"style":486},[29049],{"type":26,"value":17393},{"type":21,"tag":455,"props":29051,"children":29052},{"class":457,"line":1506},[29053],{"type":21,"tag":455,"props":29054,"children":29055},{"emptyLinePlaceholder":471},[29056],{"type":26,"value":474},{"type":21,"tag":455,"props":29058,"children":29059},{"class":457,"line":1547},[29060,29065,29069],{"type":21,"tag":455,"props":29061,"children":29062},{"style":486},[29063],{"type":26,"value":29064},"      @decoder ",{"type":21,"tag":455,"props":29066,"children":29067},{"style":1349},[29068],{"type":26,"value":3193},{"type":21,"tag":455,"props":29070,"children":29071},{"style":486},[29072],{"type":26,"value":29073}," options.decoder\n",{"type":21,"tag":455,"props":29075,"children":29076},{"class":457,"line":1564},[29077,29082,29087,29092],{"type":21,"tag":455,"props":29078,"children":29079},{"style":480},[29080],{"type":26,"value":29081},"      setInterval",{"type":21,"tag":455,"props":29083,"children":29084},{"style":486},[29085],{"type":26,"value":29086},"(@moveDataOneStep, ",{"type":21,"tag":455,"props":29088,"children":29089},{"style":480},[29090],{"type":26,"value":29091},"100",{"type":21,"tag":455,"props":29093,"children":29094},{"style":486},[29095],{"type":26,"value":500},{"type":21,"tag":455,"props":29097,"children":29098},{"class":457,"line":1605},[29099],{"type":21,"tag":455,"props":29100,"children":29101},{"emptyLinePlaceholder":471},[29102],{"type":26,"value":474},{"type":21,"tag":455,"props":29104,"children":29105},{"class":457,"line":1622},[29106,29111,29115,29119],{"type":21,"tag":455,"props":29107,"children":29108},{"style":1365},[29109],{"type":26,"value":29110},"   addToken",{"type":21,"tag":455,"props":29112,"children":29113},{"style":1349},[29114],{"type":26,"value":831},{"type":21,"tag":455,"props":29116,"children":29117},{"style":486},[29118],{"type":26,"value":26148},{"type":21,"tag":455,"props":29120,"children":29121},{"style":1349},[29122],{"type":26,"value":26153},{"type":21,"tag":455,"props":29124,"children":29125},{"class":457,"line":1663},[29126,29131,29135],{"type":21,"tag":455,"props":29127,"children":29128},{"style":486},[29129],{"type":26,"value":29130},"      @inputQueue.",{"type":21,"tag":455,"props":29132,"children":29133},{"style":480},[29134],{"type":26,"value":26611},{"type":21,"tag":455,"props":29136,"children":29137},{"style":486},[29138],{"type":26,"value":26616},{"type":21,"tag":455,"props":29140,"children":29141},{"class":457,"line":1680},[29142],{"type":21,"tag":455,"props":29143,"children":29144},{"emptyLinePlaceholder":471},[29145],{"type":26,"value":474},{"type":21,"tag":455,"props":29147,"children":29148},{"class":457,"line":1721},[29149,29154,29158],{"type":21,"tag":455,"props":29150,"children":29151},{"style":1365},[29152],{"type":26,"value":29153},"   moveDataOneStep",{"type":21,"tag":455,"props":29155,"children":29156},{"style":1349},[29157],{"type":26,"value":831},{"type":21,"tag":455,"props":29159,"children":29160},{"style":1349},[29161],{"type":26,"value":26301},{"type":21,"tag":455,"props":29163,"children":29164},{"class":457,"line":1738},[29165,29170,29174,29179,29184],{"type":21,"tag":455,"props":29166,"children":29167},{"style":486},[29168],{"type":26,"value":29169},"      @decoder.",{"type":21,"tag":455,"props":29171,"children":29172},{"style":1365},[29173],{"type":26,"value":26799},{"type":21,"tag":455,"props":29175,"children":29176},{"style":486},[29177],{"type":26,"value":29178},"(@communicationLine.",{"type":21,"tag":455,"props":29180,"children":29181},{"style":480},[29182],{"type":26,"value":29183},"pop",{"type":21,"tag":455,"props":29185,"children":29186},{"style":486},[29187],{"type":26,"value":29188},"())\n",{"type":21,"tag":455,"props":29190,"children":29191},{"class":457,"line":1779},[29192,29197,29201,29206,29211],{"type":21,"tag":455,"props":29193,"children":29194},{"style":4527},[29195],{"type":26,"value":29196},"      nextToken",{"type":21,"tag":455,"props":29198,"children":29199},{"style":1349},[29200],{"type":26,"value":2131},{"type":21,"tag":455,"props":29202,"children":29203},{"style":486},[29204],{"type":26,"value":29205}," @inputQueue.",{"type":21,"tag":455,"props":29207,"children":29208},{"style":480},[29209],{"type":26,"value":29210},"shift",{"type":21,"tag":455,"props":29212,"children":29213},{"style":486},[29214],{"type":26,"value":4506},{"type":21,"tag":455,"props":29216,"children":29217},{"class":457,"line":1796},[29218],{"type":21,"tag":455,"props":29219,"children":29220},{"emptyLinePlaceholder":471},[29221],{"type":26,"value":474},{"type":21,"tag":455,"props":29223,"children":29224},{"class":457,"line":1837},[29225,29230,29235],{"type":21,"tag":455,"props":29226,"children":29227},{"style":486},[29228],{"type":26,"value":29229},"      @communicationLine.",{"type":21,"tag":455,"props":29231,"children":29232},{"style":480},[29233],{"type":26,"value":29234},"unshift",{"type":21,"tag":455,"props":29236,"children":29237},{"style":486},[29238],{"type":26,"value":11391},{"type":21,"tag":455,"props":29240,"children":29241},{"class":457,"line":1854},[29242,29247,29252,29257,29262,29266,29270,29275],{"type":21,"tag":455,"props":29243,"children":29244},{"style":1349},[29245],{"type":26,"value":29246},"       if",{"type":21,"tag":455,"props":29248,"children":29249},{"style":480},[29250],{"type":26,"value":29251}," undefined",{"type":21,"tag":455,"props":29253,"children":29254},{"style":1349},[29255],{"type":26,"value":29256}," ==",{"type":21,"tag":455,"props":29258,"children":29259},{"style":486},[29260],{"type":26,"value":29261}," nextToken ",{"type":21,"tag":455,"props":29263,"children":29264},{"style":1349},[29265],{"type":26,"value":4520},{"type":21,"tag":455,"props":29267,"children":29268},{"style":492},[29269],{"type":26,"value":21357},{"type":21,"tag":455,"props":29271,"children":29272},{"style":1349},[29273],{"type":26,"value":29274}," else",{"type":21,"tag":455,"props":29276,"children":29277},{"style":486},[29278],{"type":26,"value":29279}," nextToken)\n",{"type":21,"tag":455,"props":29281,"children":29282},{"class":457,"line":1895},[29283],{"type":21,"tag":455,"props":29284,"children":29285},{"emptyLinePlaceholder":471},[29286],{"type":26,"value":474},{"type":21,"tag":455,"props":29288,"children":29289},{"class":457,"line":1912},[29290,29294,29298,29302,29306],{"type":21,"tag":455,"props":29291,"children":29292},{"style":486},[29293],{"type":26,"value":26396},{"type":21,"tag":455,"props":29295,"children":29296},{"style":1365},[29297],{"type":26,"value":26570},{"type":21,"tag":455,"props":29299,"children":29300},{"style":486},[29301],{"type":26,"value":489},{"type":21,"tag":455,"props":29303,"children":29304},{"style":492},[29305],{"type":26,"value":26881},{"type":21,"tag":455,"props":29307,"children":29308},{"style":486},[29309],{"type":26,"value":29310},", @communicationLine)\n",{"type":21,"tag":22,"props":29312,"children":29313},{},[29314],{"type":26,"value":29315},"The CommunicationLine class is a Backbone model class that represents the tokens currently moving across the telegraph line. It also contains a queue that holds the input tokens generated by the user. The class also holds a reference to the object responsible for decoding the user input (more on that tomorrow).",{"type":21,"tag":22,"props":29317,"children":29318},{},[29319],{"type":26,"value":29320},"Every 100 milliseconds, the communication line shifts its tokens over one spot. The token popped off the line is passed to the decoder. The model then checks the input queue to determine if a user-generated token is available to be pushed onto the line. If not, the model pushes a default token (an empty string). The model then triggers an event to indicate the communication line state has changed and that the object representing the communication line view needs to be re-rendered (more on that tomorrow).",{"type":21,"tag":1279,"props":29322,"children":29323},{},[29324],{"type":26,"value":1283},{"title":8,"searchDepth":333,"depth":333,"links":29326},[],"content:rbrubaker:2012-06:coffe-backbone-1.md","rbrubaker/2012-06/coffe-backbone-1.md","rbrubaker/2012-06/coffe-backbone-1",{"user":26000,"name":26001},1780330267742]