[{"data":1,"prerenderedAt":6467},["ShallowReactive",2],{"page-\u002Fjs\u002F30-exercises-and-projects":3},{"id":4,"title":5,"body":6,"description":6460,"extension":6461,"meta":6462,"navigation":46,"path":6463,"seo":6464,"stem":6465,"__hash__":6466},"content\u002Fjs\u002F30-exercises-and-projects.md","JavaScript 30 — Exercises & Projects: Building Production Systems",{"type":7,"value":8,"toc":6452},"minimark",[9,14,19,1309,1313,2526,2530,3842,3846,5012,5016,6084,6088,6233,6448],[10,11,13],"h1",{"id":12},"_30-exercises-projects-building-production-systems","30 — Exercises & Projects: Building Production Systems",[15,16,18],"h2",{"id":17},"project-1-reactive-state-store-redux-like","Project 1: Reactive State Store (Redux-like)",[20,21,23],"code-wrapper",{"language":22},"javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F ── A minimal reactive state store with reducers, middleware, and subscriptions ──\n\nfunction createStore(reducer, initialState, enhancer) {\n    if (typeof enhancer === \"function\") {  \u002F\u002F store enhancer (e.g., applyMiddleware)\n        return enhancer(createStore)(reducer, initialState);\n    }\n\n    let state = initialState;\n    const listeners = new Set();\n    let isDispatching = false;\n\n    function getState() { return state; }\n\n    function dispatch(action) {\n        if (typeof action !== \"object\" || action === null || typeof action.type === \"undefined\") {\n            throw new Error(\"Actions must be plain objects with a `type` property\");\n        }\n        if (isDispatching) throw new Error(\"Reducers may not dispatch actions\");\n        try {\n            isDispatching = true;\n            state = reducer(state, action);  \u002F\u002F run the reducer\n        } finally {\n            isDispatching = false;\n        }\n        listeners.forEach(fn => fn());  \u002F\u002F notify subscribers\n        return action;\n    }\n\n    function subscribe(listener) {\n        listeners.add(listener);\n        return () => listeners.delete(listener);  \u002F\u002F unsubscribe function\n    }\n\n    \u002F\u002F Initialize state by dispatching a dummy action\n    dispatch({ type: \"@@INIT\" });\n\n    return { getState, dispatch, subscribe };\n}\n\n\u002F\u002F ── Middleware: intercept actions before they reach the reducer ──\nfunction applyMiddleware(...middlewares) {\n    return (createStore) => (reducer, initialState) => {\n        const store = createStore(reducer, initialState);\n        \u002F\u002F Middleware receives dispatch and getState (with a restricted API)\n        const api = {\n            getState: store.getState,\n            dispatch: (action) => enhancedDispatch(action),\n        };\n        const chain = middlewares.map(mw => mw(api)(store.dispatch));\n        \u002F\u002F Each middleware: (store) => (next) => (action) => result\n        const enhancedDispatch = chain.reduceRight((next, mw) => mw(next), store.dispatch);\n        return { ...store, dispatch: enhancedDispatch };\n    };\n}\n\n\u002F\u002F ── Logger middleware ──\nconst logger = store => next => action => {\n    console.log(\"dispatching:\", action.type, action);\n    const result = next(action);  \u002F\u002F pass to the next middleware \u002F reducer\n    console.log(\"next state:\", store.getState());\n    return result;\n};\n\n\u002F\u002F ── Thunk middleware (async actions) ──\nconst thunk = store => next => action => {\n    if (typeof action === \"function\") {\n        return action(store.dispatch, store.getState);  \u002F\u002F call the thunk\n    }\n    return next(action);  \u002F\u002F pass through plain actions\n};\n\n\u002F\u002F ── Reducer (pure function: (state, action) → newState) ──\nfunction counterReducer(state = { count: 0 }, action) {\n    switch (action.type) {\n        case \"INCREMENT\": return { ...state, count: state.count + (action.by || 1) };\n        case \"DECREMENT\": return { ...state, count: state.count - (action.by || 1) };\n        case \"RESET\":     return { ...state, count: 0 };\n        default:          return state;  \u002F\u002F must return state for unknown actions\n    }\n}\n\n\u002F\u002F ── Usage ──\nconst store = createStore(counterReducer, undefined, applyMiddleware(thunk, logger));\nstore.subscribe(() => console.log(\"state changed:\", store.getState().count));\nstore.dispatch({ type: \"INCREMENT\" });  \u002F\u002F count: 1\nstore.dispatch({ type: \"INCREMENT\", by: 5 });  \u002F\u002F count: 6\nstore.dispatch({ type: \"RESET\" });  \u002F\u002F count: 0\n\n\u002F\u002F Async action (thunk):\nconst asyncIncrement = (dispatch, getState) => {\n    setTimeout(() => dispatch({ type: \"INCREMENT\" }), 1000);\n};\nstore.dispatch(asyncIncrement);  \u002F\u002F increments after 1s (thunk intercepts)\n","",[30,31,32,41,48,81,109,121,127,132,147,169,185,190,208,213,228,272,291,297,319,328,341,358,369,380,385,411,419,424,429,444,455,478,483,488,494,509,514,523,529,534,540,558,587,603,609,621,627,648,654,683,689,723,736,742,747,752,758,785,802,820,841,849,855,860,866,890,907,920,925,937,942,947,953,981,990,1026,1055,1080,1097,1102,1107,1112,1118,1143,1174,1193,1215,1232,1237,1243,1267,1291,1296],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F ── A minimal reactive state store with reducers, middleware, and subscriptions ──\n",[33,42,44],{"class":35,"line":43},2,[33,45,47],{"emptyLinePlaceholder":46},true,"\n",[33,49,51,55,59,63,67,70,73,75,78],{"class":35,"line":50},3,[33,52,54],{"class":53},"svdQ7","function",[33,56,58],{"class":57},"sIsaT"," createStore",[33,60,62],{"class":61},"ssxIu","(",[33,64,66],{"class":65},"sCrzJ","reducer",[33,68,69],{"class":61},", ",[33,71,72],{"class":65},"initialState",[33,74,69],{"class":61},[33,76,77],{"class":65},"enhancer",[33,79,80],{"class":61},") {\n",[33,82,84,87,90,93,96,99,103,106],{"class":35,"line":83},4,[33,85,86],{"class":53},"    if",[33,88,89],{"class":61}," (",[33,91,92],{"class":53},"typeof",[33,94,95],{"class":61}," enhancer ",[33,97,98],{"class":53},"===",[33,100,102],{"class":101},"sJ6F3"," \"function\"",[33,104,105],{"class":61},") {  ",[33,107,108],{"class":39},"\u002F\u002F store enhancer (e.g., applyMiddleware)\n",[33,110,112,115,118],{"class":35,"line":111},5,[33,113,114],{"class":53},"        return",[33,116,117],{"class":57}," enhancer",[33,119,120],{"class":61},"(createStore)(reducer, initialState);\n",[33,122,124],{"class":35,"line":123},6,[33,125,126],{"class":61},"    }\n",[33,128,130],{"class":35,"line":129},7,[33,131,47],{"emptyLinePlaceholder":46},[33,133,135,138,141,144],{"class":35,"line":134},8,[33,136,137],{"class":53},"    let",[33,139,140],{"class":61}," state ",[33,142,143],{"class":53},"=",[33,145,146],{"class":61}," initialState;\n",[33,148,150,153,157,160,163,166],{"class":35,"line":149},9,[33,151,152],{"class":53},"    const",[33,154,156],{"class":155},"snvgF"," listeners",[33,158,159],{"class":53}," =",[33,161,162],{"class":53}," new",[33,164,165],{"class":57}," Set",[33,167,168],{"class":61},"();\n",[33,170,172,174,177,179,182],{"class":35,"line":171},10,[33,173,137],{"class":53},[33,175,176],{"class":61}," isDispatching ",[33,178,143],{"class":53},[33,180,181],{"class":155}," false",[33,183,184],{"class":61},";\n",[33,186,188],{"class":35,"line":187},11,[33,189,47],{"emptyLinePlaceholder":46},[33,191,193,196,199,202,205],{"class":35,"line":192},12,[33,194,195],{"class":53},"    function",[33,197,198],{"class":57}," getState",[33,200,201],{"class":61},"() { ",[33,203,204],{"class":53},"return",[33,206,207],{"class":61}," state; }\n",[33,209,211],{"class":35,"line":210},13,[33,212,47],{"emptyLinePlaceholder":46},[33,214,216,218,221,223,226],{"class":35,"line":215},14,[33,217,195],{"class":53},[33,219,220],{"class":57}," dispatch",[33,222,62],{"class":61},[33,224,225],{"class":65},"action",[33,227,80],{"class":61},[33,229,231,234,236,238,241,244,247,250,252,254,257,259,262,265,267,270],{"class":35,"line":230},15,[33,232,233],{"class":53},"        if",[33,235,89],{"class":61},[33,237,92],{"class":53},[33,239,240],{"class":61}," action ",[33,242,243],{"class":53},"!==",[33,245,246],{"class":101}," \"object\"",[33,248,249],{"class":53}," ||",[33,251,240],{"class":61},[33,253,98],{"class":53},[33,255,256],{"class":155}," null",[33,258,249],{"class":53},[33,260,261],{"class":53}," typeof",[33,263,264],{"class":61}," action.type ",[33,266,98],{"class":53},[33,268,269],{"class":101}," \"undefined\"",[33,271,80],{"class":61},[33,273,275,278,280,283,285,288],{"class":35,"line":274},16,[33,276,277],{"class":53},"            throw",[33,279,162],{"class":53},[33,281,282],{"class":57}," Error",[33,284,62],{"class":61},[33,286,287],{"class":101},"\"Actions must be plain objects with a `type` property\"",[33,289,290],{"class":61},");\n",[33,292,294],{"class":35,"line":293},17,[33,295,296],{"class":61},"        }\n",[33,298,300,302,305,308,310,312,314,317],{"class":35,"line":299},18,[33,301,233],{"class":53},[33,303,304],{"class":61}," (isDispatching) ",[33,306,307],{"class":53},"throw",[33,309,162],{"class":53},[33,311,282],{"class":57},[33,313,62],{"class":61},[33,315,316],{"class":101},"\"Reducers may not dispatch actions\"",[33,318,290],{"class":61},[33,320,322,325],{"class":35,"line":321},19,[33,323,324],{"class":53},"        try",[33,326,327],{"class":61}," {\n",[33,329,331,334,336,339],{"class":35,"line":330},20,[33,332,333],{"class":61},"            isDispatching ",[33,335,143],{"class":53},[33,337,338],{"class":155}," true",[33,340,184],{"class":61},[33,342,344,347,349,352,355],{"class":35,"line":343},21,[33,345,346],{"class":61},"            state ",[33,348,143],{"class":53},[33,350,351],{"class":57}," reducer",[33,353,354],{"class":61},"(state, action);  ",[33,356,357],{"class":39},"\u002F\u002F run the reducer\n",[33,359,361,364,367],{"class":35,"line":360},22,[33,362,363],{"class":61},"        } ",[33,365,366],{"class":53},"finally",[33,368,327],{"class":61},[33,370,372,374,376,378],{"class":35,"line":371},23,[33,373,333],{"class":61},[33,375,143],{"class":53},[33,377,181],{"class":155},[33,379,184],{"class":61},[33,381,383],{"class":35,"line":382},24,[33,384,296],{"class":61},[33,386,388,391,394,396,399,402,405,408],{"class":35,"line":387},25,[33,389,390],{"class":61},"        listeners.",[33,392,393],{"class":57},"forEach",[33,395,62],{"class":61},[33,397,398],{"class":65},"fn",[33,400,401],{"class":53}," =>",[33,403,404],{"class":57}," fn",[33,406,407],{"class":61},"());  ",[33,409,410],{"class":39},"\u002F\u002F notify subscribers\n",[33,412,414,416],{"class":35,"line":413},26,[33,415,114],{"class":53},[33,417,418],{"class":61}," action;\n",[33,420,422],{"class":35,"line":421},27,[33,423,126],{"class":61},[33,425,427],{"class":35,"line":426},28,[33,428,47],{"emptyLinePlaceholder":46},[33,430,432,434,437,439,442],{"class":35,"line":431},29,[33,433,195],{"class":53},[33,435,436],{"class":57}," subscribe",[33,438,62],{"class":61},[33,440,441],{"class":65},"listener",[33,443,80],{"class":61},[33,445,447,449,452],{"class":35,"line":446},30,[33,448,390],{"class":61},[33,450,451],{"class":57},"add",[33,453,454],{"class":61},"(listener);\n",[33,456,458,460,463,466,469,472,475],{"class":35,"line":457},31,[33,459,114],{"class":53},[33,461,462],{"class":61}," () ",[33,464,465],{"class":53},"=>",[33,467,468],{"class":61}," listeners.",[33,470,471],{"class":57},"delete",[33,473,474],{"class":61},"(listener);  ",[33,476,477],{"class":39},"\u002F\u002F unsubscribe function\n",[33,479,481],{"class":35,"line":480},32,[33,482,126],{"class":61},[33,484,486],{"class":35,"line":485},33,[33,487,47],{"emptyLinePlaceholder":46},[33,489,491],{"class":35,"line":490},34,[33,492,493],{"class":39},"    \u002F\u002F Initialize state by dispatching a dummy action\n",[33,495,497,500,503,506],{"class":35,"line":496},35,[33,498,499],{"class":57},"    dispatch",[33,501,502],{"class":61},"({ type: ",[33,504,505],{"class":101},"\"@@INIT\"",[33,507,508],{"class":61}," });\n",[33,510,512],{"class":35,"line":511},36,[33,513,47],{"emptyLinePlaceholder":46},[33,515,517,520],{"class":35,"line":516},37,[33,518,519],{"class":53},"    return",[33,521,522],{"class":61}," { getState, dispatch, subscribe };\n",[33,524,526],{"class":35,"line":525},38,[33,527,528],{"class":61},"}\n",[33,530,532],{"class":35,"line":531},39,[33,533,47],{"emptyLinePlaceholder":46},[33,535,537],{"class":35,"line":536},40,[33,538,539],{"class":39},"\u002F\u002F ── Middleware: intercept actions before they reach the reducer ──\n",[33,541,543,545,548,550,553,556],{"class":35,"line":542},41,[33,544,54],{"class":53},[33,546,547],{"class":57}," applyMiddleware",[33,549,62],{"class":61},[33,551,552],{"class":53},"...",[33,554,555],{"class":65},"middlewares",[33,557,80],{"class":61},[33,559,561,563,565,568,571,573,575,577,579,581,583,585],{"class":35,"line":560},42,[33,562,519],{"class":53},[33,564,89],{"class":61},[33,566,567],{"class":65},"createStore",[33,569,570],{"class":61},") ",[33,572,465],{"class":53},[33,574,89],{"class":61},[33,576,66],{"class":65},[33,578,69],{"class":61},[33,580,72],{"class":65},[33,582,570],{"class":61},[33,584,465],{"class":53},[33,586,327],{"class":61},[33,588,590,593,596,598,600],{"class":35,"line":589},43,[33,591,592],{"class":53},"        const",[33,594,595],{"class":155}," store",[33,597,159],{"class":53},[33,599,58],{"class":57},[33,601,602],{"class":61},"(reducer, initialState);\n",[33,604,606],{"class":35,"line":605},44,[33,607,608],{"class":39},"        \u002F\u002F Middleware receives dispatch and getState (with a restricted API)\n",[33,610,612,614,617,619],{"class":35,"line":611},45,[33,613,592],{"class":53},[33,615,616],{"class":155}," api",[33,618,159],{"class":53},[33,620,327],{"class":61},[33,622,624],{"class":35,"line":623},46,[33,625,626],{"class":61},"            getState: store.getState,\n",[33,628,630,633,636,638,640,642,645],{"class":35,"line":629},47,[33,631,632],{"class":57},"            dispatch",[33,634,635],{"class":61},": (",[33,637,225],{"class":65},[33,639,570],{"class":61},[33,641,465],{"class":53},[33,643,644],{"class":57}," enhancedDispatch",[33,646,647],{"class":61},"(action),\n",[33,649,651],{"class":35,"line":650},48,[33,652,653],{"class":61},"        };\n",[33,655,657,659,662,664,667,670,672,675,677,680],{"class":35,"line":656},49,[33,658,592],{"class":53},[33,660,661],{"class":155}," chain",[33,663,159],{"class":53},[33,665,666],{"class":61}," middlewares.",[33,668,669],{"class":57},"map",[33,671,62],{"class":61},[33,673,674],{"class":65},"mw",[33,676,401],{"class":53},[33,678,679],{"class":57}," mw",[33,681,682],{"class":61},"(api)(store.dispatch));\n",[33,684,686],{"class":35,"line":685},50,[33,687,688],{"class":39},"        \u002F\u002F Each middleware: (store) => (next) => (action) => result\n",[33,690,692,694,696,698,701,704,707,710,712,714,716,718,720],{"class":35,"line":691},51,[33,693,592],{"class":53},[33,695,644],{"class":155},[33,697,159],{"class":53},[33,699,700],{"class":61}," chain.",[33,702,703],{"class":57},"reduceRight",[33,705,706],{"class":61},"((",[33,708,709],{"class":65},"next",[33,711,69],{"class":61},[33,713,674],{"class":65},[33,715,570],{"class":61},[33,717,465],{"class":53},[33,719,679],{"class":57},[33,721,722],{"class":61},"(next), store.dispatch);\n",[33,724,726,728,731,733],{"class":35,"line":725},52,[33,727,114],{"class":53},[33,729,730],{"class":61}," { ",[33,732,552],{"class":53},[33,734,735],{"class":61},"store, dispatch: enhancedDispatch };\n",[33,737,739],{"class":35,"line":738},53,[33,740,741],{"class":61},"    };\n",[33,743,745],{"class":35,"line":744},54,[33,746,528],{"class":61},[33,748,750],{"class":35,"line":749},55,[33,751,47],{"emptyLinePlaceholder":46},[33,753,755],{"class":35,"line":754},56,[33,756,757],{"class":39},"\u002F\u002F ── Logger middleware ──\n",[33,759,761,764,767,769,771,773,776,778,781,783],{"class":35,"line":760},57,[33,762,763],{"class":53},"const",[33,765,766],{"class":57}," logger",[33,768,159],{"class":53},[33,770,595],{"class":65},[33,772,401],{"class":53},[33,774,775],{"class":65}," next",[33,777,401],{"class":53},[33,779,780],{"class":65}," action",[33,782,401],{"class":53},[33,784,327],{"class":61},[33,786,788,791,794,796,799],{"class":35,"line":787},58,[33,789,790],{"class":61},"    console.",[33,792,793],{"class":57},"log",[33,795,62],{"class":61},[33,797,798],{"class":101},"\"dispatching:\"",[33,800,801],{"class":61},", action.type, action);\n",[33,803,805,807,810,812,814,817],{"class":35,"line":804},59,[33,806,152],{"class":53},[33,808,809],{"class":155}," result",[33,811,159],{"class":53},[33,813,775],{"class":57},[33,815,816],{"class":61},"(action);  ",[33,818,819],{"class":39},"\u002F\u002F pass to the next middleware \u002F reducer\n",[33,821,823,825,827,829,832,835,838],{"class":35,"line":822},60,[33,824,790],{"class":61},[33,826,793],{"class":57},[33,828,62],{"class":61},[33,830,831],{"class":101},"\"next state:\"",[33,833,834],{"class":61},", store.",[33,836,837],{"class":57},"getState",[33,839,840],{"class":61},"());\n",[33,842,844,846],{"class":35,"line":843},61,[33,845,519],{"class":53},[33,847,848],{"class":61}," result;\n",[33,850,852],{"class":35,"line":851},62,[33,853,854],{"class":61},"};\n",[33,856,858],{"class":35,"line":857},63,[33,859,47],{"emptyLinePlaceholder":46},[33,861,863],{"class":35,"line":862},64,[33,864,865],{"class":39},"\u002F\u002F ── Thunk middleware (async actions) ──\n",[33,867,869,871,874,876,878,880,882,884,886,888],{"class":35,"line":868},65,[33,870,763],{"class":53},[33,872,873],{"class":57}," thunk",[33,875,159],{"class":53},[33,877,595],{"class":65},[33,879,401],{"class":53},[33,881,775],{"class":65},[33,883,401],{"class":53},[33,885,780],{"class":65},[33,887,401],{"class":53},[33,889,327],{"class":61},[33,891,893,895,897,899,901,903,905],{"class":35,"line":892},66,[33,894,86],{"class":53},[33,896,89],{"class":61},[33,898,92],{"class":53},[33,900,240],{"class":61},[33,902,98],{"class":53},[33,904,102],{"class":101},[33,906,80],{"class":61},[33,908,910,912,914,917],{"class":35,"line":909},67,[33,911,114],{"class":53},[33,913,780],{"class":57},[33,915,916],{"class":61},"(store.dispatch, store.getState);  ",[33,918,919],{"class":39},"\u002F\u002F call the thunk\n",[33,921,923],{"class":35,"line":922},68,[33,924,126],{"class":61},[33,926,928,930,932,934],{"class":35,"line":927},69,[33,929,519],{"class":53},[33,931,775],{"class":57},[33,933,816],{"class":61},[33,935,936],{"class":39},"\u002F\u002F pass through plain actions\n",[33,938,940],{"class":35,"line":939},70,[33,941,854],{"class":61},[33,943,945],{"class":35,"line":944},71,[33,946,47],{"emptyLinePlaceholder":46},[33,948,950],{"class":35,"line":949},72,[33,951,952],{"class":39},"\u002F\u002F ── Reducer (pure function: (state, action) → newState) ──\n",[33,954,956,958,961,963,966,968,971,974,977,979],{"class":35,"line":955},73,[33,957,54],{"class":53},[33,959,960],{"class":57}," counterReducer",[33,962,62],{"class":61},[33,964,965],{"class":65},"state",[33,967,159],{"class":53},[33,969,970],{"class":61}," { count: ",[33,972,973],{"class":155},"0",[33,975,976],{"class":61}," }, ",[33,978,225],{"class":65},[33,980,80],{"class":61},[33,982,984,987],{"class":35,"line":983},74,[33,985,986],{"class":53},"    switch",[33,988,989],{"class":61}," (action.type) {\n",[33,991,993,996,999,1002,1004,1006,1008,1011,1014,1017,1020,1023],{"class":35,"line":992},75,[33,994,995],{"class":53},"        case",[33,997,998],{"class":101}," \"INCREMENT\"",[33,1000,1001],{"class":61},": ",[33,1003,204],{"class":53},[33,1005,730],{"class":61},[33,1007,552],{"class":53},[33,1009,1010],{"class":61},"state, count: state.count ",[33,1012,1013],{"class":53},"+",[33,1015,1016],{"class":61}," (action.by ",[33,1018,1019],{"class":53},"||",[33,1021,1022],{"class":155}," 1",[33,1024,1025],{"class":61},") };\n",[33,1027,1029,1031,1034,1036,1038,1040,1042,1044,1047,1049,1051,1053],{"class":35,"line":1028},76,[33,1030,995],{"class":53},[33,1032,1033],{"class":101}," \"DECREMENT\"",[33,1035,1001],{"class":61},[33,1037,204],{"class":53},[33,1039,730],{"class":61},[33,1041,552],{"class":53},[33,1043,1010],{"class":61},[33,1045,1046],{"class":53},"-",[33,1048,1016],{"class":61},[33,1050,1019],{"class":53},[33,1052,1022],{"class":155},[33,1054,1025],{"class":61},[33,1056,1058,1060,1063,1066,1068,1070,1072,1075,1077],{"class":35,"line":1057},77,[33,1059,995],{"class":53},[33,1061,1062],{"class":101}," \"RESET\"",[33,1064,1065],{"class":61},":     ",[33,1067,204],{"class":53},[33,1069,730],{"class":61},[33,1071,552],{"class":53},[33,1073,1074],{"class":61},"state, count: ",[33,1076,973],{"class":155},[33,1078,1079],{"class":61}," };\n",[33,1081,1083,1086,1089,1091,1094],{"class":35,"line":1082},78,[33,1084,1085],{"class":53},"        default",[33,1087,1088],{"class":61},":          ",[33,1090,204],{"class":53},[33,1092,1093],{"class":61}," state;  ",[33,1095,1096],{"class":39},"\u002F\u002F must return state for unknown actions\n",[33,1098,1100],{"class":35,"line":1099},79,[33,1101,126],{"class":61},[33,1103,1105],{"class":35,"line":1104},80,[33,1106,528],{"class":61},[33,1108,1110],{"class":35,"line":1109},81,[33,1111,47],{"emptyLinePlaceholder":46},[33,1113,1115],{"class":35,"line":1114},82,[33,1116,1117],{"class":39},"\u002F\u002F ── Usage ──\n",[33,1119,1121,1123,1125,1127,1129,1132,1135,1137,1140],{"class":35,"line":1120},83,[33,1122,763],{"class":53},[33,1124,595],{"class":155},[33,1126,159],{"class":53},[33,1128,58],{"class":57},[33,1130,1131],{"class":61},"(counterReducer, ",[33,1133,1134],{"class":155},"undefined",[33,1136,69],{"class":61},[33,1138,1139],{"class":57},"applyMiddleware",[33,1141,1142],{"class":61},"(thunk, logger));\n",[33,1144,1146,1149,1152,1155,1157,1160,1162,1164,1167,1169,1171],{"class":35,"line":1145},84,[33,1147,1148],{"class":61},"store.",[33,1150,1151],{"class":57},"subscribe",[33,1153,1154],{"class":61},"(() ",[33,1156,465],{"class":53},[33,1158,1159],{"class":61}," console.",[33,1161,793],{"class":57},[33,1163,62],{"class":61},[33,1165,1166],{"class":101},"\"state changed:\"",[33,1168,834],{"class":61},[33,1170,837],{"class":57},[33,1172,1173],{"class":61},"().count));\n",[33,1175,1177,1179,1182,1184,1187,1190],{"class":35,"line":1176},85,[33,1178,1148],{"class":61},[33,1180,1181],{"class":57},"dispatch",[33,1183,502],{"class":61},[33,1185,1186],{"class":101},"\"INCREMENT\"",[33,1188,1189],{"class":61}," });  ",[33,1191,1192],{"class":39},"\u002F\u002F count: 1\n",[33,1194,1196,1198,1200,1202,1204,1207,1210,1212],{"class":35,"line":1195},86,[33,1197,1148],{"class":61},[33,1199,1181],{"class":57},[33,1201,502],{"class":61},[33,1203,1186],{"class":101},[33,1205,1206],{"class":61},", by: ",[33,1208,1209],{"class":155},"5",[33,1211,1189],{"class":61},[33,1213,1214],{"class":39},"\u002F\u002F count: 6\n",[33,1216,1218,1220,1222,1224,1227,1229],{"class":35,"line":1217},87,[33,1219,1148],{"class":61},[33,1221,1181],{"class":57},[33,1223,502],{"class":61},[33,1225,1226],{"class":101},"\"RESET\"",[33,1228,1189],{"class":61},[33,1230,1231],{"class":39},"\u002F\u002F count: 0\n",[33,1233,1235],{"class":35,"line":1234},88,[33,1236,47],{"emptyLinePlaceholder":46},[33,1238,1240],{"class":35,"line":1239},89,[33,1241,1242],{"class":39},"\u002F\u002F Async action (thunk):\n",[33,1244,1246,1248,1251,1253,1255,1257,1259,1261,1263,1265],{"class":35,"line":1245},90,[33,1247,763],{"class":53},[33,1249,1250],{"class":57}," asyncIncrement",[33,1252,159],{"class":53},[33,1254,89],{"class":61},[33,1256,1181],{"class":65},[33,1258,69],{"class":61},[33,1260,837],{"class":65},[33,1262,570],{"class":61},[33,1264,465],{"class":53},[33,1266,327],{"class":61},[33,1268,1270,1273,1275,1277,1279,1281,1283,1286,1289],{"class":35,"line":1269},91,[33,1271,1272],{"class":57},"    setTimeout",[33,1274,1154],{"class":61},[33,1276,465],{"class":53},[33,1278,220],{"class":57},[33,1280,502],{"class":61},[33,1282,1186],{"class":101},[33,1284,1285],{"class":61}," }), ",[33,1287,1288],{"class":155},"1000",[33,1290,290],{"class":61},[33,1292,1294],{"class":35,"line":1293},92,[33,1295,854],{"class":61},[33,1297,1299,1301,1303,1306],{"class":35,"line":1298},93,[33,1300,1148],{"class":61},[33,1302,1181],{"class":57},[33,1304,1305],{"class":61},"(asyncIncrement);  ",[33,1307,1308],{"class":39},"\u002F\u002F increments after 1s (thunk intercepts)\n",[15,1310,1312],{"id":1311},"project-2-promise-based-api-client-with-retry-and-cancellation","Project 2: Promise-Based API Client with Retry and Cancellation",[20,1314,1315],{"language":22},[24,1316,1318],{"className":26,"code":1317,"language":22,"meta":28,"style":28},"\u002F\u002F ── Production API client: retry with exponential backoff, timeout, cancellation ──\n\nclass ApiClient {\n    constructor(baseURL, defaultOptions = {}) {\n        this.baseURL = baseURL;\n        this.defaultOptions = {\n            timeout: 10000,\n            retries: 3,\n            backoffFactor: 2,\n            initialDelay: 100,\n            ...defaultOptions,\n        };\n    }\n\n    async request(path, options = {}) {\n        const opts = { ...this.defaultOptions, ...options };\n        const controller = new AbortController();\n        const timeoutId = setTimeout(() => controller.abort(), opts.timeout);\n\n        let lastError;\n        for (let attempt = 0; attempt \u003C= opts.retries; attempt++) {\n            try {\n                const response = await fetch(`${this.baseURL}${path}`, {\n                    ...opts,\n                    signal: opts.signal ?? controller.signal,  \u002F\u002F allow external cancellation\n                    headers: { \"Content-Type\": \"application\u002Fjson\", ...opts.headers },\n                });\n\n                if (!response.ok) {\n                    \u002F\u002F Retry on 5xx (server errors), throw on 4xx (client errors)\n                    if (response.status >= 500 && attempt \u003C opts.retries) {\n                        throw new Error(`Server error: ${response.status}`);\n                    }\n                    const body = await response.json().catch(() => null);\n                    throw new ApiError(response.status, body?.message || response.statusText, body);\n                }\n\n                clearTimeout(timeoutId);\n                return response.status === 204 ? null : await response.json();\n            } catch (error) {\n                clearTimeout(timeoutId);\n                if (error.name === \"AbortError\") throw new Error(\"Request cancelled or timed out\");\n                if (error instanceof ApiError) throw error;  \u002F\u002F don't retry client errors\n\n                lastError = error;\n                if (attempt \u003C opts.retries) {\n                    const delay = opts.initialDelay * Math.pow(opts.backoffFactor, attempt);\n                    await new Promise(r => setTimeout(r, delay));  \u002F\u002F exponential backoff\n                }\n            }\n        }\n        throw lastError;  \u002F\u002F all retries exhausted\n    }\n\n    get(path, options)    { return this.request(path, { ...options, method: \"GET\" }); }\n    post(path, body, options)   { return this.request(path, { ...options, method: \"POST\", body: JSON.stringify(body) }); }\n    put(path, body, options)    { return this.request(path, { ...options, method: \"PUT\", body: JSON.stringify(body) }); }\n    delete(path, options) { return this.request(path, { ...options, method: \"DELETE\" }); }\n}\n\nclass ApiError extends Error {\n    constructor(status, message, body) {\n        super(message);\n        this.name = \"ApiError\";\n        this.status = status;\n        this.body = body;\n    }\n}\n\n\u002F\u002F ── Usage ──\nconst api = new ApiClient(\"https:\u002F\u002Fapi.example.com\", { retries: 3, timeout: 5000 });\ntry {\n    const user = await api.get(\"\u002Fusers\u002F1\");\n    const created = await api.post(\"\u002Fusers\", { name: \"Alice\" });\n} catch (error) {\n    if (error instanceof ApiError) console.error(`API ${error.status}: ${error.message}`);\n    else console.error(\"Network error:\", error.message);\n}\n\n\u002F\u002F Cancellation (AbortController):\nconst cancelController = new AbortController();\napi.get(\"\u002Fslow-endpoint\", { signal: cancelController.signal })\n    .catch(err => console.log(\"cancelled:\", err.message));\ncancelController.abort();  \u002F\u002F cancel the request\n",[30,1319,1320,1325,1329,1339,1359,1372,1383,1394,1404,1414,1424,1432,1436,1440,1444,1466,1490,1506,1531,1535,1543,1575,1582,1621,1629,1643,1663,1668,1672,1685,1690,1715,1741,1746,1778,1796,1801,1805,1813,1842,1852,1858,1885,1907,1911,1921,1932,1956,1981,1985,1990,1994,2005,2009,2013,2053,2105,2151,2186,2190,2194,2207,2226,2234,2248,2260,2272,2276,2280,2284,2288,2318,2325,2349,2378,2387,2427,2444,2448,2452,2457,2472,2487,2513],{"__ignoreMap":28},[33,1321,1322],{"class":35,"line":36},[33,1323,1324],{"class":39},"\u002F\u002F ── Production API client: retry with exponential backoff, timeout, cancellation ──\n",[33,1326,1327],{"class":35,"line":43},[33,1328,47],{"emptyLinePlaceholder":46},[33,1330,1331,1334,1337],{"class":35,"line":50},[33,1332,1333],{"class":53},"class",[33,1335,1336],{"class":57}," ApiClient",[33,1338,327],{"class":61},[33,1340,1341,1344,1346,1349,1351,1354,1356],{"class":35,"line":83},[33,1342,1343],{"class":53},"    constructor",[33,1345,62],{"class":61},[33,1347,1348],{"class":65},"baseURL",[33,1350,69],{"class":61},[33,1352,1353],{"class":65},"defaultOptions",[33,1355,159],{"class":53},[33,1357,1358],{"class":61}," {}) {\n",[33,1360,1361,1364,1367,1369],{"class":35,"line":111},[33,1362,1363],{"class":155},"        this",[33,1365,1366],{"class":61},".baseURL ",[33,1368,143],{"class":53},[33,1370,1371],{"class":61}," baseURL;\n",[33,1373,1374,1376,1379,1381],{"class":35,"line":123},[33,1375,1363],{"class":155},[33,1377,1378],{"class":61},".defaultOptions ",[33,1380,143],{"class":53},[33,1382,327],{"class":61},[33,1384,1385,1388,1391],{"class":35,"line":129},[33,1386,1387],{"class":61},"            timeout: ",[33,1389,1390],{"class":155},"10000",[33,1392,1393],{"class":61},",\n",[33,1395,1396,1399,1402],{"class":35,"line":134},[33,1397,1398],{"class":61},"            retries: ",[33,1400,1401],{"class":155},"3",[33,1403,1393],{"class":61},[33,1405,1406,1409,1412],{"class":35,"line":149},[33,1407,1408],{"class":61},"            backoffFactor: ",[33,1410,1411],{"class":155},"2",[33,1413,1393],{"class":61},[33,1415,1416,1419,1422],{"class":35,"line":171},[33,1417,1418],{"class":61},"            initialDelay: ",[33,1420,1421],{"class":155},"100",[33,1423,1393],{"class":61},[33,1425,1426,1429],{"class":35,"line":187},[33,1427,1428],{"class":53},"            ...",[33,1430,1431],{"class":61},"defaultOptions,\n",[33,1433,1434],{"class":35,"line":192},[33,1435,653],{"class":61},[33,1437,1438],{"class":35,"line":210},[33,1439,126],{"class":61},[33,1441,1442],{"class":35,"line":215},[33,1443,47],{"emptyLinePlaceholder":46},[33,1445,1446,1449,1452,1454,1457,1459,1462,1464],{"class":35,"line":230},[33,1447,1448],{"class":53},"    async",[33,1450,1451],{"class":57}," request",[33,1453,62],{"class":61},[33,1455,1456],{"class":65},"path",[33,1458,69],{"class":61},[33,1460,1461],{"class":65},"options",[33,1463,159],{"class":53},[33,1465,1358],{"class":61},[33,1467,1468,1470,1473,1475,1477,1479,1482,1485,1487],{"class":35,"line":274},[33,1469,592],{"class":53},[33,1471,1472],{"class":155}," opts",[33,1474,159],{"class":53},[33,1476,730],{"class":61},[33,1478,552],{"class":53},[33,1480,1481],{"class":155},"this",[33,1483,1484],{"class":61},".defaultOptions, ",[33,1486,552],{"class":53},[33,1488,1489],{"class":61},"options };\n",[33,1491,1492,1494,1497,1499,1501,1504],{"class":35,"line":293},[33,1493,592],{"class":53},[33,1495,1496],{"class":155}," controller",[33,1498,159],{"class":53},[33,1500,162],{"class":53},[33,1502,1503],{"class":57}," AbortController",[33,1505,168],{"class":61},[33,1507,1508,1510,1513,1515,1518,1520,1522,1525,1528],{"class":35,"line":299},[33,1509,592],{"class":53},[33,1511,1512],{"class":155}," timeoutId",[33,1514,159],{"class":53},[33,1516,1517],{"class":57}," setTimeout",[33,1519,1154],{"class":61},[33,1521,465],{"class":53},[33,1523,1524],{"class":61}," controller.",[33,1526,1527],{"class":57},"abort",[33,1529,1530],{"class":61},"(), opts.timeout);\n",[33,1532,1533],{"class":35,"line":321},[33,1534,47],{"emptyLinePlaceholder":46},[33,1536,1537,1540],{"class":35,"line":330},[33,1538,1539],{"class":53},"        let",[33,1541,1542],{"class":61}," lastError;\n",[33,1544,1545,1548,1550,1553,1556,1558,1561,1564,1567,1570,1573],{"class":35,"line":343},[33,1546,1547],{"class":53},"        for",[33,1549,89],{"class":61},[33,1551,1552],{"class":53},"let",[33,1554,1555],{"class":61}," attempt ",[33,1557,143],{"class":53},[33,1559,1560],{"class":155}," 0",[33,1562,1563],{"class":61},"; attempt ",[33,1565,1566],{"class":53},"\u003C=",[33,1568,1569],{"class":61}," opts.retries; attempt",[33,1571,1572],{"class":53},"++",[33,1574,80],{"class":61},[33,1576,1577,1580],{"class":35,"line":360},[33,1578,1579],{"class":53},"            try",[33,1581,327],{"class":61},[33,1583,1584,1587,1590,1592,1595,1598,1600,1603,1605,1608,1610,1613,1615,1618],{"class":35,"line":371},[33,1585,1586],{"class":53},"                const",[33,1588,1589],{"class":155}," response",[33,1591,159],{"class":53},[33,1593,1594],{"class":53}," await",[33,1596,1597],{"class":57}," fetch",[33,1599,62],{"class":61},[33,1601,1602],{"class":101},"`${",[33,1604,1481],{"class":155},[33,1606,1607],{"class":101},".",[33,1609,1348],{"class":61},[33,1611,1612],{"class":101},"}${",[33,1614,1456],{"class":61},[33,1616,1617],{"class":101},"}`",[33,1619,1620],{"class":61},", {\n",[33,1622,1623,1626],{"class":35,"line":382},[33,1624,1625],{"class":53},"                    ...",[33,1627,1628],{"class":61},"opts,\n",[33,1630,1631,1634,1637,1640],{"class":35,"line":387},[33,1632,1633],{"class":61},"                    signal: opts.signal ",[33,1635,1636],{"class":53},"??",[33,1638,1639],{"class":61}," controller.signal,  ",[33,1641,1642],{"class":39},"\u002F\u002F allow external cancellation\n",[33,1644,1645,1648,1651,1653,1656,1658,1660],{"class":35,"line":413},[33,1646,1647],{"class":61},"                    headers: { ",[33,1649,1650],{"class":101},"\"Content-Type\"",[33,1652,1001],{"class":61},[33,1654,1655],{"class":101},"\"application\u002Fjson\"",[33,1657,69],{"class":61},[33,1659,552],{"class":53},[33,1661,1662],{"class":61},"opts.headers },\n",[33,1664,1665],{"class":35,"line":421},[33,1666,1667],{"class":61},"                });\n",[33,1669,1670],{"class":35,"line":426},[33,1671,47],{"emptyLinePlaceholder":46},[33,1673,1674,1677,1679,1682],{"class":35,"line":431},[33,1675,1676],{"class":53},"                if",[33,1678,89],{"class":61},[33,1680,1681],{"class":53},"!",[33,1683,1684],{"class":61},"response.ok) {\n",[33,1686,1687],{"class":35,"line":446},[33,1688,1689],{"class":39},"                    \u002F\u002F Retry on 5xx (server errors), throw on 4xx (client errors)\n",[33,1691,1692,1695,1698,1701,1704,1707,1709,1712],{"class":35,"line":457},[33,1693,1694],{"class":53},"                    if",[33,1696,1697],{"class":61}," (response.status ",[33,1699,1700],{"class":53},">=",[33,1702,1703],{"class":155}," 500",[33,1705,1706],{"class":53}," &&",[33,1708,1555],{"class":61},[33,1710,1711],{"class":53},"\u003C",[33,1713,1714],{"class":61}," opts.retries) {\n",[33,1716,1717,1720,1722,1724,1726,1729,1732,1734,1737,1739],{"class":35,"line":480},[33,1718,1719],{"class":53},"                        throw",[33,1721,162],{"class":53},[33,1723,282],{"class":57},[33,1725,62],{"class":61},[33,1727,1728],{"class":101},"`Server error: ${",[33,1730,1731],{"class":61},"response",[33,1733,1607],{"class":101},[33,1735,1736],{"class":61},"status",[33,1738,1617],{"class":101},[33,1740,290],{"class":61},[33,1742,1743],{"class":35,"line":485},[33,1744,1745],{"class":61},"                    }\n",[33,1747,1748,1751,1754,1756,1758,1761,1764,1767,1770,1772,1774,1776],{"class":35,"line":490},[33,1749,1750],{"class":53},"                    const",[33,1752,1753],{"class":155}," body",[33,1755,159],{"class":53},[33,1757,1594],{"class":53},[33,1759,1760],{"class":61}," response.",[33,1762,1763],{"class":57},"json",[33,1765,1766],{"class":61},"().",[33,1768,1769],{"class":57},"catch",[33,1771,1154],{"class":61},[33,1773,465],{"class":53},[33,1775,256],{"class":155},[33,1777,290],{"class":61},[33,1779,1780,1783,1785,1788,1791,1793],{"class":35,"line":496},[33,1781,1782],{"class":53},"                    throw",[33,1784,162],{"class":53},[33,1786,1787],{"class":57}," ApiError",[33,1789,1790],{"class":61},"(response.status, body?.message ",[33,1792,1019],{"class":53},[33,1794,1795],{"class":61}," response.statusText, body);\n",[33,1797,1798],{"class":35,"line":511},[33,1799,1800],{"class":61},"                }\n",[33,1802,1803],{"class":35,"line":516},[33,1804,47],{"emptyLinePlaceholder":46},[33,1806,1807,1810],{"class":35,"line":525},[33,1808,1809],{"class":57},"                clearTimeout",[33,1811,1812],{"class":61},"(timeoutId);\n",[33,1814,1815,1818,1821,1823,1826,1829,1831,1834,1836,1838,1840],{"class":35,"line":531},[33,1816,1817],{"class":53},"                return",[33,1819,1820],{"class":61}," response.status ",[33,1822,98],{"class":53},[33,1824,1825],{"class":155}," 204",[33,1827,1828],{"class":53}," ?",[33,1830,256],{"class":155},[33,1832,1833],{"class":53}," :",[33,1835,1594],{"class":53},[33,1837,1760],{"class":61},[33,1839,1763],{"class":57},[33,1841,168],{"class":61},[33,1843,1844,1847,1849],{"class":35,"line":536},[33,1845,1846],{"class":61},"            } ",[33,1848,1769],{"class":53},[33,1850,1851],{"class":61}," (error) {\n",[33,1853,1854,1856],{"class":35,"line":542},[33,1855,1809],{"class":57},[33,1857,1812],{"class":61},[33,1859,1860,1862,1865,1867,1870,1872,1874,1876,1878,1880,1883],{"class":35,"line":560},[33,1861,1676],{"class":53},[33,1863,1864],{"class":61}," (error.name ",[33,1866,98],{"class":53},[33,1868,1869],{"class":101}," \"AbortError\"",[33,1871,570],{"class":61},[33,1873,307],{"class":53},[33,1875,162],{"class":53},[33,1877,282],{"class":57},[33,1879,62],{"class":61},[33,1881,1882],{"class":101},"\"Request cancelled or timed out\"",[33,1884,290],{"class":61},[33,1886,1887,1889,1892,1895,1897,1899,1901,1904],{"class":35,"line":589},[33,1888,1676],{"class":53},[33,1890,1891],{"class":61}," (error ",[33,1893,1894],{"class":53},"instanceof",[33,1896,1787],{"class":57},[33,1898,570],{"class":61},[33,1900,307],{"class":53},[33,1902,1903],{"class":61}," error;  ",[33,1905,1906],{"class":39},"\u002F\u002F don't retry client errors\n",[33,1908,1909],{"class":35,"line":605},[33,1910,47],{"emptyLinePlaceholder":46},[33,1912,1913,1916,1918],{"class":35,"line":611},[33,1914,1915],{"class":61},"                lastError ",[33,1917,143],{"class":53},[33,1919,1920],{"class":61}," error;\n",[33,1922,1923,1925,1928,1930],{"class":35,"line":623},[33,1924,1676],{"class":53},[33,1926,1927],{"class":61}," (attempt ",[33,1929,1711],{"class":53},[33,1931,1714],{"class":61},[33,1933,1934,1936,1939,1941,1944,1947,1950,1953],{"class":35,"line":629},[33,1935,1750],{"class":53},[33,1937,1938],{"class":155}," delay",[33,1940,159],{"class":53},[33,1942,1943],{"class":61}," opts.initialDelay ",[33,1945,1946],{"class":53},"*",[33,1948,1949],{"class":61}," Math.",[33,1951,1952],{"class":57},"pow",[33,1954,1955],{"class":61},"(opts.backoffFactor, attempt);\n",[33,1957,1958,1961,1963,1966,1968,1971,1973,1975,1978],{"class":35,"line":650},[33,1959,1960],{"class":53},"                    await",[33,1962,162],{"class":53},[33,1964,1965],{"class":155}," Promise",[33,1967,62],{"class":61},[33,1969,1970],{"class":65},"r",[33,1972,401],{"class":53},[33,1974,1517],{"class":57},[33,1976,1977],{"class":61},"(r, delay));  ",[33,1979,1980],{"class":39},"\u002F\u002F exponential backoff\n",[33,1982,1983],{"class":35,"line":656},[33,1984,1800],{"class":61},[33,1986,1987],{"class":35,"line":685},[33,1988,1989],{"class":61},"            }\n",[33,1991,1992],{"class":35,"line":691},[33,1993,296],{"class":61},[33,1995,1996,1999,2002],{"class":35,"line":725},[33,1997,1998],{"class":53},"        throw",[33,2000,2001],{"class":61}," lastError;  ",[33,2003,2004],{"class":39},"\u002F\u002F all retries exhausted\n",[33,2006,2007],{"class":35,"line":738},[33,2008,126],{"class":61},[33,2010,2011],{"class":35,"line":744},[33,2012,47],{"emptyLinePlaceholder":46},[33,2014,2015,2018,2020,2022,2024,2026,2029,2031,2034,2036,2039,2042,2044,2047,2050],{"class":35,"line":749},[33,2016,2017],{"class":57},"    get",[33,2019,62],{"class":61},[33,2021,1456],{"class":65},[33,2023,69],{"class":61},[33,2025,1461],{"class":65},[33,2027,2028],{"class":61},")    { ",[33,2030,204],{"class":53},[33,2032,2033],{"class":155}," this",[33,2035,1607],{"class":61},[33,2037,2038],{"class":57},"request",[33,2040,2041],{"class":61},"(path, { ",[33,2043,552],{"class":53},[33,2045,2046],{"class":61},"options, method: ",[33,2048,2049],{"class":101},"\"GET\"",[33,2051,2052],{"class":61}," }); }\n",[33,2054,2055,2058,2060,2062,2064,2067,2069,2071,2074,2076,2078,2080,2082,2084,2086,2088,2091,2094,2097,2099,2102],{"class":35,"line":754},[33,2056,2057],{"class":57},"    post",[33,2059,62],{"class":61},[33,2061,1456],{"class":65},[33,2063,69],{"class":61},[33,2065,2066],{"class":65},"body",[33,2068,69],{"class":61},[33,2070,1461],{"class":65},[33,2072,2073],{"class":61},")   { ",[33,2075,204],{"class":53},[33,2077,2033],{"class":155},[33,2079,1607],{"class":61},[33,2081,2038],{"class":57},[33,2083,2041],{"class":61},[33,2085,552],{"class":53},[33,2087,2046],{"class":61},[33,2089,2090],{"class":101},"\"POST\"",[33,2092,2093],{"class":61},", body: ",[33,2095,2096],{"class":155},"JSON",[33,2098,1607],{"class":61},[33,2100,2101],{"class":57},"stringify",[33,2103,2104],{"class":61},"(body) }); }\n",[33,2106,2107,2110,2112,2114,2116,2118,2120,2122,2124,2126,2128,2130,2132,2134,2136,2138,2141,2143,2145,2147,2149],{"class":35,"line":760},[33,2108,2109],{"class":57},"    put",[33,2111,62],{"class":61},[33,2113,1456],{"class":65},[33,2115,69],{"class":61},[33,2117,2066],{"class":65},[33,2119,69],{"class":61},[33,2121,1461],{"class":65},[33,2123,2028],{"class":61},[33,2125,204],{"class":53},[33,2127,2033],{"class":155},[33,2129,1607],{"class":61},[33,2131,2038],{"class":57},[33,2133,2041],{"class":61},[33,2135,552],{"class":53},[33,2137,2046],{"class":61},[33,2139,2140],{"class":101},"\"PUT\"",[33,2142,2093],{"class":61},[33,2144,2096],{"class":155},[33,2146,1607],{"class":61},[33,2148,2101],{"class":57},[33,2150,2104],{"class":61},[33,2152,2153,2156,2158,2160,2162,2164,2167,2169,2171,2173,2175,2177,2179,2181,2184],{"class":35,"line":787},[33,2154,2155],{"class":57},"    delete",[33,2157,62],{"class":61},[33,2159,1456],{"class":65},[33,2161,69],{"class":61},[33,2163,1461],{"class":65},[33,2165,2166],{"class":61},") { ",[33,2168,204],{"class":53},[33,2170,2033],{"class":155},[33,2172,1607],{"class":61},[33,2174,2038],{"class":57},[33,2176,2041],{"class":61},[33,2178,552],{"class":53},[33,2180,2046],{"class":61},[33,2182,2183],{"class":101},"\"DELETE\"",[33,2185,2052],{"class":61},[33,2187,2188],{"class":35,"line":804},[33,2189,528],{"class":61},[33,2191,2192],{"class":35,"line":822},[33,2193,47],{"emptyLinePlaceholder":46},[33,2195,2196,2198,2200,2203,2205],{"class":35,"line":843},[33,2197,1333],{"class":53},[33,2199,1787],{"class":57},[33,2201,2202],{"class":53}," extends",[33,2204,282],{"class":57},[33,2206,327],{"class":61},[33,2208,2209,2211,2213,2215,2217,2220,2222,2224],{"class":35,"line":851},[33,2210,1343],{"class":53},[33,2212,62],{"class":61},[33,2214,1736],{"class":65},[33,2216,69],{"class":61},[33,2218,2219],{"class":65},"message",[33,2221,69],{"class":61},[33,2223,2066],{"class":65},[33,2225,80],{"class":61},[33,2227,2228,2231],{"class":35,"line":857},[33,2229,2230],{"class":155},"        super",[33,2232,2233],{"class":61},"(message);\n",[33,2235,2236,2238,2241,2243,2246],{"class":35,"line":862},[33,2237,1363],{"class":155},[33,2239,2240],{"class":61},".name ",[33,2242,143],{"class":53},[33,2244,2245],{"class":101}," \"ApiError\"",[33,2247,184],{"class":61},[33,2249,2250,2252,2255,2257],{"class":35,"line":868},[33,2251,1363],{"class":155},[33,2253,2254],{"class":61},".status ",[33,2256,143],{"class":53},[33,2258,2259],{"class":61}," status;\n",[33,2261,2262,2264,2267,2269],{"class":35,"line":892},[33,2263,1363],{"class":155},[33,2265,2266],{"class":61},".body ",[33,2268,143],{"class":53},[33,2270,2271],{"class":61}," body;\n",[33,2273,2274],{"class":35,"line":909},[33,2275,126],{"class":61},[33,2277,2278],{"class":35,"line":922},[33,2279,528],{"class":61},[33,2281,2282],{"class":35,"line":927},[33,2283,47],{"emptyLinePlaceholder":46},[33,2285,2286],{"class":35,"line":939},[33,2287,1117],{"class":39},[33,2289,2290,2292,2294,2296,2298,2300,2302,2305,2308,2310,2313,2316],{"class":35,"line":944},[33,2291,763],{"class":53},[33,2293,616],{"class":155},[33,2295,159],{"class":53},[33,2297,162],{"class":53},[33,2299,1336],{"class":57},[33,2301,62],{"class":61},[33,2303,2304],{"class":101},"\"https:\u002F\u002Fapi.example.com\"",[33,2306,2307],{"class":61},", { retries: ",[33,2309,1401],{"class":155},[33,2311,2312],{"class":61},", timeout: ",[33,2314,2315],{"class":155},"5000",[33,2317,508],{"class":61},[33,2319,2320,2323],{"class":35,"line":949},[33,2321,2322],{"class":53},"try",[33,2324,327],{"class":61},[33,2326,2327,2329,2332,2334,2336,2339,2342,2344,2347],{"class":35,"line":955},[33,2328,152],{"class":53},[33,2330,2331],{"class":155}," user",[33,2333,159],{"class":53},[33,2335,1594],{"class":53},[33,2337,2338],{"class":61}," api.",[33,2340,2341],{"class":57},"get",[33,2343,62],{"class":61},[33,2345,2346],{"class":101},"\"\u002Fusers\u002F1\"",[33,2348,290],{"class":61},[33,2350,2351,2353,2356,2358,2360,2362,2365,2367,2370,2373,2376],{"class":35,"line":983},[33,2352,152],{"class":53},[33,2354,2355],{"class":155}," created",[33,2357,159],{"class":53},[33,2359,1594],{"class":53},[33,2361,2338],{"class":61},[33,2363,2364],{"class":57},"post",[33,2366,62],{"class":61},[33,2368,2369],{"class":101},"\"\u002Fusers\"",[33,2371,2372],{"class":61},", { name: ",[33,2374,2375],{"class":101},"\"Alice\"",[33,2377,508],{"class":61},[33,2379,2380,2383,2385],{"class":35,"line":992},[33,2381,2382],{"class":61},"} ",[33,2384,1769],{"class":53},[33,2386,1851],{"class":61},[33,2388,2389,2391,2393,2395,2397,2400,2403,2405,2408,2410,2412,2414,2417,2419,2421,2423,2425],{"class":35,"line":1028},[33,2390,86],{"class":53},[33,2392,1891],{"class":61},[33,2394,1894],{"class":53},[33,2396,1787],{"class":57},[33,2398,2399],{"class":61},") console.",[33,2401,2402],{"class":57},"error",[33,2404,62],{"class":61},[33,2406,2407],{"class":101},"`API ${",[33,2409,2402],{"class":61},[33,2411,1607],{"class":101},[33,2413,1736],{"class":61},[33,2415,2416],{"class":101},"}: ${",[33,2418,2402],{"class":61},[33,2420,1607],{"class":101},[33,2422,2219],{"class":61},[33,2424,1617],{"class":101},[33,2426,290],{"class":61},[33,2428,2429,2432,2434,2436,2438,2441],{"class":35,"line":1057},[33,2430,2431],{"class":53},"    else",[33,2433,1159],{"class":61},[33,2435,2402],{"class":57},[33,2437,62],{"class":61},[33,2439,2440],{"class":101},"\"Network error:\"",[33,2442,2443],{"class":61},", error.message);\n",[33,2445,2446],{"class":35,"line":1082},[33,2447,528],{"class":61},[33,2449,2450],{"class":35,"line":1099},[33,2451,47],{"emptyLinePlaceholder":46},[33,2453,2454],{"class":35,"line":1104},[33,2455,2456],{"class":39},"\u002F\u002F Cancellation (AbortController):\n",[33,2458,2459,2461,2464,2466,2468,2470],{"class":35,"line":1109},[33,2460,763],{"class":53},[33,2462,2463],{"class":155}," cancelController",[33,2465,159],{"class":53},[33,2467,162],{"class":53},[33,2469,1503],{"class":57},[33,2471,168],{"class":61},[33,2473,2474,2477,2479,2481,2484],{"class":35,"line":1114},[33,2475,2476],{"class":61},"api.",[33,2478,2341],{"class":57},[33,2480,62],{"class":61},[33,2482,2483],{"class":101},"\"\u002Fslow-endpoint\"",[33,2485,2486],{"class":61},", { signal: cancelController.signal })\n",[33,2488,2489,2492,2494,2496,2499,2501,2503,2505,2507,2510],{"class":35,"line":1120},[33,2490,2491],{"class":61},"    .",[33,2493,1769],{"class":57},[33,2495,62],{"class":61},[33,2497,2498],{"class":65},"err",[33,2500,401],{"class":53},[33,2502,1159],{"class":61},[33,2504,793],{"class":57},[33,2506,62],{"class":61},[33,2508,2509],{"class":101},"\"cancelled:\"",[33,2511,2512],{"class":61},", err.message));\n",[33,2514,2515,2518,2520,2523],{"class":35,"line":1145},[33,2516,2517],{"class":61},"cancelController.",[33,2519,1527],{"class":57},[33,2521,2522],{"class":61},"();  ",[33,2524,2525],{"class":39},"\u002F\u002F cancel the request\n",[15,2527,2529],{"id":2528},"project-3-virtual-dom-diffing-engine","Project 3: Virtual DOM Diffing Engine",[20,2531,2532],{"language":22},[24,2533,2535],{"className":26,"code":2534,"language":22,"meta":28,"style":28},"\u002F\u002F ── Minimal virtual DOM: createElement, diff, and patch the real DOM ──\n\n\u002F\u002F ── Create a virtual node (vnode) ──\nfunction h(tag, props = {}, ...children) {\n    return {\n        tag,\n        props: props || {},\n        children: children.flat().map(child =>\n            typeof child === \"string\" || typeof child === \"number\"\n                ? { tag: \"TEXT\", props: {}, text: String(child) }  \u002F\u002F wrap text\n                : child\n        ),\n    };\n}\n\n\u002F\u002F ── Render a vnode to a real DOM node ──\nfunction render(vnode) {\n    if (vnode.tag === \"TEXT\") return document.createTextNode(vnode.text);\n    const el = document.createElement(vnode.tag);\n    for (const [key, value] of Object.entries(vnode.props)) {\n        if (key.startsWith(\"on\") && typeof value === \"function\") {\n            el.addEventListener(key.slice(2).toLowerCase(), value);  \u002F\u002F event handler\n        } else if (key === \"style\" && typeof value === \"object\") {\n            Object.assign(el.style, value);  \u002F\u002F style object\n        } else if (key === \"className\") {\n            el.setAttribute(\"class\", value);\n        } else {\n            el.setAttribute(key, value);\n        }\n    }\n    for (const child of vnode.children) el.appendChild(render(child));\n    return el;\n}\n\n\u002F\u002F ── Diff two vnodes and patch the real DOM ──\nfunction patch(parent, oldVNode, newVNode, index = 0) {\n    const el = parent.childNodes[index];\n\n    if (!oldVNode) {\n        \u002F\u002F New node — append\n        parent.appendChild(render(newVNode));\n    } else if (!newVNode) {\n        \u002F\u002F Removed — delete\n        parent.removeChild(el);\n    } else if (isChanged(oldVNode, newVNode)) {\n        \u002F\u002F Changed — replace\n        parent.replaceChild(render(newVNode), el);\n    } else if (oldVNode.tag === newVNode.tag) {\n        \u002F\u002F Same tag — diff props and children\n        patchProps(el, oldVNode.props, newVNode.props);\n        const max = Math.max(oldVNode.children.length, newVNode.children.length);\n        for (let i = 0; i \u003C max; i++) {\n            patch(el, oldVNode.children[i], newVNode.children[i], i);\n        }\n    }\n}\n\nfunction isChanged(oldV, newV) {\n    return oldV.tag !== newV.tag ||\n        (oldV.tag === \"TEXT\" && oldV.text !== newV.text);\n}\n\nfunction patchProps(el, oldProps, newProps) {\n    \u002F\u002F Remove old props\n    for (const key of Object.keys(oldProps)) {\n        if (!(key in newProps)) {\n            if (key.startsWith(\"on\")) el.removeEventListener(key.slice(2).toLowerCase(), oldProps[key]);\n            else el.removeAttribute(key);\n        }\n    }\n    \u002F\u002F Set new\u002Fchanged props\n    for (const [key, value] of Object.entries(newProps)) {\n        if (oldProps[key] !== value) {\n            if (key.startsWith(\"on\") && typeof value === \"function\") {\n                if (oldProps[key]) el.removeEventListener(key.slice(2).toLowerCase(), oldProps[key]);\n                el.addEventListener(key.slice(2).toLowerCase(), value);\n            } else if (key === \"className\") {\n                el.setAttribute(\"class\", value);\n            } else {\n                el.setAttribute(key, value);\n            }\n        }\n    }\n}\n\n\u002F\u002F ── Usage ──\nconst oldTree = h(\"div\", { id: \"app\" },\n    h(\"h1\", {}, \"Hello\"),\n    h(\"p\", { className: \"desc\" }, \"World\"),\n);\nconst newTree = h(\"div\", { id: \"app\" },\n    h(\"h1\", {}, \"Hello Updated\"),\n    h(\"p\", { className: \"desc\", style: { color: \"red\" } }, \"World\"),\n    h(\"button\", { onClick: () => alert(\"clicked\") }, \"Click\"),\n);\nconst container = document.querySelector(\"#root\");\ncontainer.appendChild(render(oldTree));  \u002F\u002F initial render\npatch(container, oldTree, newTree);  \u002F\u002F diff and patch (minimal DOM updates)\n",[30,2536,2537,2542,2546,2551,2580,2586,2591,2601,2621,2645,2668,2676,2681,2685,2689,2693,2698,2712,2737,2754,2789,2820,2850,2880,2894,2911,2926,2934,2943,2947,2951,2979,2986,2990,2994,2999,3032,3043,3047,3058,3063,3077,3093,3098,3108,3124,3129,3143,3159,3164,3172,3199,3226,3234,3238,3242,3246,3250,3269,3284,3303,3307,3311,3335,3340,3361,3378,3412,3426,3430,3434,3439,3466,3478,3504,3527,3549,3565,3577,3585,3593,3597,3601,3605,3609,3613,3617,3642,3661,3683,3687,3708,3723,3748,3785,3790,3812,3830],{"__ignoreMap":28},[33,2538,2539],{"class":35,"line":36},[33,2540,2541],{"class":39},"\u002F\u002F ── Minimal virtual DOM: createElement, diff, and patch the real DOM ──\n",[33,2543,2544],{"class":35,"line":43},[33,2545,47],{"emptyLinePlaceholder":46},[33,2547,2548],{"class":35,"line":50},[33,2549,2550],{"class":39},"\u002F\u002F ── Create a virtual node (vnode) ──\n",[33,2552,2553,2555,2558,2560,2563,2565,2568,2570,2573,2575,2578],{"class":35,"line":83},[33,2554,54],{"class":53},[33,2556,2557],{"class":57}," h",[33,2559,62],{"class":61},[33,2561,2562],{"class":65},"tag",[33,2564,69],{"class":61},[33,2566,2567],{"class":65},"props",[33,2569,159],{"class":53},[33,2571,2572],{"class":61}," {}, ",[33,2574,552],{"class":53},[33,2576,2577],{"class":65},"children",[33,2579,80],{"class":61},[33,2581,2582,2584],{"class":35,"line":111},[33,2583,519],{"class":53},[33,2585,327],{"class":61},[33,2587,2588],{"class":35,"line":123},[33,2589,2590],{"class":61},"        tag,\n",[33,2592,2593,2596,2598],{"class":35,"line":129},[33,2594,2595],{"class":61},"        props: props ",[33,2597,1019],{"class":53},[33,2599,2600],{"class":61}," {},\n",[33,2602,2603,2606,2609,2611,2613,2615,2618],{"class":35,"line":134},[33,2604,2605],{"class":61},"        children: children.",[33,2607,2608],{"class":57},"flat",[33,2610,1766],{"class":61},[33,2612,669],{"class":57},[33,2614,62],{"class":61},[33,2616,2617],{"class":65},"child",[33,2619,2620],{"class":53}," =>\n",[33,2622,2623,2626,2629,2631,2634,2636,2638,2640,2642],{"class":35,"line":149},[33,2624,2625],{"class":53},"            typeof",[33,2627,2628],{"class":61}," child ",[33,2630,98],{"class":53},[33,2632,2633],{"class":101}," \"string\"",[33,2635,249],{"class":53},[33,2637,261],{"class":53},[33,2639,2628],{"class":61},[33,2641,98],{"class":53},[33,2643,2644],{"class":101}," \"number\"\n",[33,2646,2647,2650,2653,2656,2659,2662,2665],{"class":35,"line":171},[33,2648,2649],{"class":53},"                ?",[33,2651,2652],{"class":61}," { tag: ",[33,2654,2655],{"class":101},"\"TEXT\"",[33,2657,2658],{"class":61},", props: {}, text: ",[33,2660,2661],{"class":57},"String",[33,2663,2664],{"class":61},"(child) }  ",[33,2666,2667],{"class":39},"\u002F\u002F wrap text\n",[33,2669,2670,2673],{"class":35,"line":187},[33,2671,2672],{"class":53},"                :",[33,2674,2675],{"class":61}," child\n",[33,2677,2678],{"class":35,"line":192},[33,2679,2680],{"class":61},"        ),\n",[33,2682,2683],{"class":35,"line":210},[33,2684,741],{"class":61},[33,2686,2687],{"class":35,"line":215},[33,2688,528],{"class":61},[33,2690,2691],{"class":35,"line":230},[33,2692,47],{"emptyLinePlaceholder":46},[33,2694,2695],{"class":35,"line":274},[33,2696,2697],{"class":39},"\u002F\u002F ── Render a vnode to a real DOM node ──\n",[33,2699,2700,2702,2705,2707,2710],{"class":35,"line":293},[33,2701,54],{"class":53},[33,2703,2704],{"class":57}," render",[33,2706,62],{"class":61},[33,2708,2709],{"class":65},"vnode",[33,2711,80],{"class":61},[33,2713,2714,2716,2719,2721,2724,2726,2728,2731,2734],{"class":35,"line":299},[33,2715,86],{"class":53},[33,2717,2718],{"class":61}," (vnode.tag ",[33,2720,98],{"class":53},[33,2722,2723],{"class":101}," \"TEXT\"",[33,2725,570],{"class":61},[33,2727,204],{"class":53},[33,2729,2730],{"class":61}," document.",[33,2732,2733],{"class":57},"createTextNode",[33,2735,2736],{"class":61},"(vnode.text);\n",[33,2738,2739,2741,2744,2746,2748,2751],{"class":35,"line":321},[33,2740,152],{"class":53},[33,2742,2743],{"class":155}," el",[33,2745,159],{"class":53},[33,2747,2730],{"class":61},[33,2749,2750],{"class":57},"createElement",[33,2752,2753],{"class":61},"(vnode.tag);\n",[33,2755,2756,2759,2761,2763,2766,2769,2771,2774,2777,2780,2783,2786],{"class":35,"line":330},[33,2757,2758],{"class":53},"    for",[33,2760,89],{"class":61},[33,2762,763],{"class":53},[33,2764,2765],{"class":61}," [",[33,2767,2768],{"class":155},"key",[33,2770,69],{"class":61},[33,2772,2773],{"class":155},"value",[33,2775,2776],{"class":61},"] ",[33,2778,2779],{"class":53},"of",[33,2781,2782],{"class":61}," Object.",[33,2784,2785],{"class":57},"entries",[33,2787,2788],{"class":61},"(vnode.props)) {\n",[33,2790,2791,2793,2796,2799,2801,2804,2806,2809,2811,2814,2816,2818],{"class":35,"line":343},[33,2792,233],{"class":53},[33,2794,2795],{"class":61}," (key.",[33,2797,2798],{"class":57},"startsWith",[33,2800,62],{"class":61},[33,2802,2803],{"class":101},"\"on\"",[33,2805,570],{"class":61},[33,2807,2808],{"class":53},"&&",[33,2810,261],{"class":53},[33,2812,2813],{"class":61}," value ",[33,2815,98],{"class":53},[33,2817,102],{"class":101},[33,2819,80],{"class":61},[33,2821,2822,2825,2828,2831,2834,2836,2838,2841,2844,2847],{"class":35,"line":360},[33,2823,2824],{"class":61},"            el.",[33,2826,2827],{"class":57},"addEventListener",[33,2829,2830],{"class":61},"(key.",[33,2832,2833],{"class":57},"slice",[33,2835,62],{"class":61},[33,2837,1411],{"class":155},[33,2839,2840],{"class":61},").",[33,2842,2843],{"class":57},"toLowerCase",[33,2845,2846],{"class":61},"(), value);  ",[33,2848,2849],{"class":39},"\u002F\u002F event handler\n",[33,2851,2852,2854,2857,2860,2863,2865,2868,2870,2872,2874,2876,2878],{"class":35,"line":371},[33,2853,363],{"class":61},[33,2855,2856],{"class":53},"else",[33,2858,2859],{"class":53}," if",[33,2861,2862],{"class":61}," (key ",[33,2864,98],{"class":53},[33,2866,2867],{"class":101}," \"style\"",[33,2869,1706],{"class":53},[33,2871,261],{"class":53},[33,2873,2813],{"class":61},[33,2875,98],{"class":53},[33,2877,246],{"class":101},[33,2879,80],{"class":61},[33,2881,2882,2885,2888,2891],{"class":35,"line":382},[33,2883,2884],{"class":61},"            Object.",[33,2886,2887],{"class":57},"assign",[33,2889,2890],{"class":61},"(el.style, value);  ",[33,2892,2893],{"class":39},"\u002F\u002F style object\n",[33,2895,2896,2898,2900,2902,2904,2906,2909],{"class":35,"line":387},[33,2897,363],{"class":61},[33,2899,2856],{"class":53},[33,2901,2859],{"class":53},[33,2903,2862],{"class":61},[33,2905,98],{"class":53},[33,2907,2908],{"class":101}," \"className\"",[33,2910,80],{"class":61},[33,2912,2913,2915,2918,2920,2923],{"class":35,"line":413},[33,2914,2824],{"class":61},[33,2916,2917],{"class":57},"setAttribute",[33,2919,62],{"class":61},[33,2921,2922],{"class":101},"\"class\"",[33,2924,2925],{"class":61},", value);\n",[33,2927,2928,2930,2932],{"class":35,"line":421},[33,2929,363],{"class":61},[33,2931,2856],{"class":53},[33,2933,327],{"class":61},[33,2935,2936,2938,2940],{"class":35,"line":426},[33,2937,2824],{"class":61},[33,2939,2917],{"class":57},[33,2941,2942],{"class":61},"(key, value);\n",[33,2944,2945],{"class":35,"line":431},[33,2946,296],{"class":61},[33,2948,2949],{"class":35,"line":446},[33,2950,126],{"class":61},[33,2952,2953,2955,2957,2959,2962,2965,2968,2971,2973,2976],{"class":35,"line":457},[33,2954,2758],{"class":53},[33,2956,89],{"class":61},[33,2958,763],{"class":53},[33,2960,2961],{"class":155}," child",[33,2963,2964],{"class":53}," of",[33,2966,2967],{"class":61}," vnode.children) el.",[33,2969,2970],{"class":57},"appendChild",[33,2972,62],{"class":61},[33,2974,2975],{"class":57},"render",[33,2977,2978],{"class":61},"(child));\n",[33,2980,2981,2983],{"class":35,"line":480},[33,2982,519],{"class":53},[33,2984,2985],{"class":61}," el;\n",[33,2987,2988],{"class":35,"line":485},[33,2989,528],{"class":61},[33,2991,2992],{"class":35,"line":490},[33,2993,47],{"emptyLinePlaceholder":46},[33,2995,2996],{"class":35,"line":496},[33,2997,2998],{"class":39},"\u002F\u002F ── Diff two vnodes and patch the real DOM ──\n",[33,3000,3001,3003,3006,3008,3011,3013,3016,3018,3021,3023,3026,3028,3030],{"class":35,"line":511},[33,3002,54],{"class":53},[33,3004,3005],{"class":57}," patch",[33,3007,62],{"class":61},[33,3009,3010],{"class":65},"parent",[33,3012,69],{"class":61},[33,3014,3015],{"class":65},"oldVNode",[33,3017,69],{"class":61},[33,3019,3020],{"class":65},"newVNode",[33,3022,69],{"class":61},[33,3024,3025],{"class":65},"index",[33,3027,159],{"class":53},[33,3029,1560],{"class":155},[33,3031,80],{"class":61},[33,3033,3034,3036,3038,3040],{"class":35,"line":516},[33,3035,152],{"class":53},[33,3037,2743],{"class":155},[33,3039,159],{"class":53},[33,3041,3042],{"class":61}," parent.childNodes[index];\n",[33,3044,3045],{"class":35,"line":525},[33,3046,47],{"emptyLinePlaceholder":46},[33,3048,3049,3051,3053,3055],{"class":35,"line":531},[33,3050,86],{"class":53},[33,3052,89],{"class":61},[33,3054,1681],{"class":53},[33,3056,3057],{"class":61},"oldVNode) {\n",[33,3059,3060],{"class":35,"line":536},[33,3061,3062],{"class":39},"        \u002F\u002F New node — append\n",[33,3064,3065,3068,3070,3072,3074],{"class":35,"line":542},[33,3066,3067],{"class":61},"        parent.",[33,3069,2970],{"class":57},[33,3071,62],{"class":61},[33,3073,2975],{"class":57},[33,3075,3076],{"class":61},"(newVNode));\n",[33,3078,3079,3082,3084,3086,3088,3090],{"class":35,"line":560},[33,3080,3081],{"class":61},"    } ",[33,3083,2856],{"class":53},[33,3085,2859],{"class":53},[33,3087,89],{"class":61},[33,3089,1681],{"class":53},[33,3091,3092],{"class":61},"newVNode) {\n",[33,3094,3095],{"class":35,"line":589},[33,3096,3097],{"class":39},"        \u002F\u002F Removed — delete\n",[33,3099,3100,3102,3105],{"class":35,"line":605},[33,3101,3067],{"class":61},[33,3103,3104],{"class":57},"removeChild",[33,3106,3107],{"class":61},"(el);\n",[33,3109,3110,3112,3114,3116,3118,3121],{"class":35,"line":611},[33,3111,3081],{"class":61},[33,3113,2856],{"class":53},[33,3115,2859],{"class":53},[33,3117,89],{"class":61},[33,3119,3120],{"class":57},"isChanged",[33,3122,3123],{"class":61},"(oldVNode, newVNode)) {\n",[33,3125,3126],{"class":35,"line":623},[33,3127,3128],{"class":39},"        \u002F\u002F Changed — replace\n",[33,3130,3131,3133,3136,3138,3140],{"class":35,"line":629},[33,3132,3067],{"class":61},[33,3134,3135],{"class":57},"replaceChild",[33,3137,62],{"class":61},[33,3139,2975],{"class":57},[33,3141,3142],{"class":61},"(newVNode), el);\n",[33,3144,3145,3147,3149,3151,3154,3156],{"class":35,"line":650},[33,3146,3081],{"class":61},[33,3148,2856],{"class":53},[33,3150,2859],{"class":53},[33,3152,3153],{"class":61}," (oldVNode.tag ",[33,3155,98],{"class":53},[33,3157,3158],{"class":61}," newVNode.tag) {\n",[33,3160,3161],{"class":35,"line":656},[33,3162,3163],{"class":39},"        \u002F\u002F Same tag — diff props and children\n",[33,3165,3166,3169],{"class":35,"line":685},[33,3167,3168],{"class":57},"        patchProps",[33,3170,3171],{"class":61},"(el, oldVNode.props, newVNode.props);\n",[33,3173,3174,3176,3179,3181,3183,3186,3189,3192,3195,3197],{"class":35,"line":691},[33,3175,592],{"class":53},[33,3177,3178],{"class":155}," max",[33,3180,159],{"class":53},[33,3182,1949],{"class":61},[33,3184,3185],{"class":57},"max",[33,3187,3188],{"class":61},"(oldVNode.children.",[33,3190,3191],{"class":155},"length",[33,3193,3194],{"class":61},", newVNode.children.",[33,3196,3191],{"class":155},[33,3198,290],{"class":61},[33,3200,3201,3203,3205,3207,3210,3212,3214,3217,3219,3222,3224],{"class":35,"line":725},[33,3202,1547],{"class":53},[33,3204,89],{"class":61},[33,3206,1552],{"class":53},[33,3208,3209],{"class":61}," i ",[33,3211,143],{"class":53},[33,3213,1560],{"class":155},[33,3215,3216],{"class":61},"; i ",[33,3218,1711],{"class":53},[33,3220,3221],{"class":61}," max; i",[33,3223,1572],{"class":53},[33,3225,80],{"class":61},[33,3227,3228,3231],{"class":35,"line":738},[33,3229,3230],{"class":57},"            patch",[33,3232,3233],{"class":61},"(el, oldVNode.children[i], newVNode.children[i], i);\n",[33,3235,3236],{"class":35,"line":744},[33,3237,296],{"class":61},[33,3239,3240],{"class":35,"line":749},[33,3241,126],{"class":61},[33,3243,3244],{"class":35,"line":754},[33,3245,528],{"class":61},[33,3247,3248],{"class":35,"line":760},[33,3249,47],{"emptyLinePlaceholder":46},[33,3251,3252,3254,3257,3259,3262,3264,3267],{"class":35,"line":787},[33,3253,54],{"class":53},[33,3255,3256],{"class":57}," isChanged",[33,3258,62],{"class":61},[33,3260,3261],{"class":65},"oldV",[33,3263,69],{"class":61},[33,3265,3266],{"class":65},"newV",[33,3268,80],{"class":61},[33,3270,3271,3273,3276,3278,3281],{"class":35,"line":804},[33,3272,519],{"class":53},[33,3274,3275],{"class":61}," oldV.tag ",[33,3277,243],{"class":53},[33,3279,3280],{"class":61}," newV.tag ",[33,3282,3283],{"class":53},"||\n",[33,3285,3286,3289,3291,3293,3295,3298,3300],{"class":35,"line":822},[33,3287,3288],{"class":61},"        (oldV.tag ",[33,3290,98],{"class":53},[33,3292,2723],{"class":101},[33,3294,1706],{"class":53},[33,3296,3297],{"class":61}," oldV.text ",[33,3299,243],{"class":53},[33,3301,3302],{"class":61}," newV.text);\n",[33,3304,3305],{"class":35,"line":843},[33,3306,528],{"class":61},[33,3308,3309],{"class":35,"line":851},[33,3310,47],{"emptyLinePlaceholder":46},[33,3312,3313,3315,3318,3320,3323,3325,3328,3330,3333],{"class":35,"line":857},[33,3314,54],{"class":53},[33,3316,3317],{"class":57}," patchProps",[33,3319,62],{"class":61},[33,3321,3322],{"class":65},"el",[33,3324,69],{"class":61},[33,3326,3327],{"class":65},"oldProps",[33,3329,69],{"class":61},[33,3331,3332],{"class":65},"newProps",[33,3334,80],{"class":61},[33,3336,3337],{"class":35,"line":862},[33,3338,3339],{"class":39},"    \u002F\u002F Remove old props\n",[33,3341,3342,3344,3346,3348,3351,3353,3355,3358],{"class":35,"line":868},[33,3343,2758],{"class":53},[33,3345,89],{"class":61},[33,3347,763],{"class":53},[33,3349,3350],{"class":155}," key",[33,3352,2964],{"class":53},[33,3354,2782],{"class":61},[33,3356,3357],{"class":57},"keys",[33,3359,3360],{"class":61},"(oldProps)) {\n",[33,3362,3363,3365,3367,3369,3372,3375],{"class":35,"line":892},[33,3364,233],{"class":53},[33,3366,89],{"class":61},[33,3368,1681],{"class":53},[33,3370,3371],{"class":61},"(key ",[33,3373,3374],{"class":53},"in",[33,3376,3377],{"class":61}," newProps)) {\n",[33,3379,3380,3383,3385,3387,3389,3391,3394,3397,3399,3401,3403,3405,3407,3409],{"class":35,"line":909},[33,3381,3382],{"class":53},"            if",[33,3384,2795],{"class":61},[33,3386,2798],{"class":57},[33,3388,62],{"class":61},[33,3390,2803],{"class":101},[33,3392,3393],{"class":61},")) el.",[33,3395,3396],{"class":57},"removeEventListener",[33,3398,2830],{"class":61},[33,3400,2833],{"class":57},[33,3402,62],{"class":61},[33,3404,1411],{"class":155},[33,3406,2840],{"class":61},[33,3408,2843],{"class":57},[33,3410,3411],{"class":61},"(), oldProps[key]);\n",[33,3413,3414,3417,3420,3423],{"class":35,"line":922},[33,3415,3416],{"class":53},"            else",[33,3418,3419],{"class":61}," el.",[33,3421,3422],{"class":57},"removeAttribute",[33,3424,3425],{"class":61},"(key);\n",[33,3427,3428],{"class":35,"line":927},[33,3429,296],{"class":61},[33,3431,3432],{"class":35,"line":939},[33,3433,126],{"class":61},[33,3435,3436],{"class":35,"line":944},[33,3437,3438],{"class":39},"    \u002F\u002F Set new\u002Fchanged props\n",[33,3440,3441,3443,3445,3447,3449,3451,3453,3455,3457,3459,3461,3463],{"class":35,"line":949},[33,3442,2758],{"class":53},[33,3444,89],{"class":61},[33,3446,763],{"class":53},[33,3448,2765],{"class":61},[33,3450,2768],{"class":155},[33,3452,69],{"class":61},[33,3454,2773],{"class":155},[33,3456,2776],{"class":61},[33,3458,2779],{"class":53},[33,3460,2782],{"class":61},[33,3462,2785],{"class":57},[33,3464,3465],{"class":61},"(newProps)) {\n",[33,3467,3468,3470,3473,3475],{"class":35,"line":955},[33,3469,233],{"class":53},[33,3471,3472],{"class":61}," (oldProps[key] ",[33,3474,243],{"class":53},[33,3476,3477],{"class":61}," value) {\n",[33,3479,3480,3482,3484,3486,3488,3490,3492,3494,3496,3498,3500,3502],{"class":35,"line":983},[33,3481,3382],{"class":53},[33,3483,2795],{"class":61},[33,3485,2798],{"class":57},[33,3487,62],{"class":61},[33,3489,2803],{"class":101},[33,3491,570],{"class":61},[33,3493,2808],{"class":53},[33,3495,261],{"class":53},[33,3497,2813],{"class":61},[33,3499,98],{"class":53},[33,3501,102],{"class":101},[33,3503,80],{"class":61},[33,3505,3506,3508,3511,3513,3515,3517,3519,3521,3523,3525],{"class":35,"line":992},[33,3507,1676],{"class":53},[33,3509,3510],{"class":61}," (oldProps[key]) el.",[33,3512,3396],{"class":57},[33,3514,2830],{"class":61},[33,3516,2833],{"class":57},[33,3518,62],{"class":61},[33,3520,1411],{"class":155},[33,3522,2840],{"class":61},[33,3524,2843],{"class":57},[33,3526,3411],{"class":61},[33,3528,3529,3532,3534,3536,3538,3540,3542,3544,3546],{"class":35,"line":1028},[33,3530,3531],{"class":61},"                el.",[33,3533,2827],{"class":57},[33,3535,2830],{"class":61},[33,3537,2833],{"class":57},[33,3539,62],{"class":61},[33,3541,1411],{"class":155},[33,3543,2840],{"class":61},[33,3545,2843],{"class":57},[33,3547,3548],{"class":61},"(), value);\n",[33,3550,3551,3553,3555,3557,3559,3561,3563],{"class":35,"line":1057},[33,3552,1846],{"class":61},[33,3554,2856],{"class":53},[33,3556,2859],{"class":53},[33,3558,2862],{"class":61},[33,3560,98],{"class":53},[33,3562,2908],{"class":101},[33,3564,80],{"class":61},[33,3566,3567,3569,3571,3573,3575],{"class":35,"line":1082},[33,3568,3531],{"class":61},[33,3570,2917],{"class":57},[33,3572,62],{"class":61},[33,3574,2922],{"class":101},[33,3576,2925],{"class":61},[33,3578,3579,3581,3583],{"class":35,"line":1099},[33,3580,1846],{"class":61},[33,3582,2856],{"class":53},[33,3584,327],{"class":61},[33,3586,3587,3589,3591],{"class":35,"line":1104},[33,3588,3531],{"class":61},[33,3590,2917],{"class":57},[33,3592,2942],{"class":61},[33,3594,3595],{"class":35,"line":1109},[33,3596,1989],{"class":61},[33,3598,3599],{"class":35,"line":1114},[33,3600,296],{"class":61},[33,3602,3603],{"class":35,"line":1120},[33,3604,126],{"class":61},[33,3606,3607],{"class":35,"line":1145},[33,3608,528],{"class":61},[33,3610,3611],{"class":35,"line":1176},[33,3612,47],{"emptyLinePlaceholder":46},[33,3614,3615],{"class":35,"line":1195},[33,3616,1117],{"class":39},[33,3618,3619,3621,3624,3626,3628,3630,3633,3636,3639],{"class":35,"line":1217},[33,3620,763],{"class":53},[33,3622,3623],{"class":155}," oldTree",[33,3625,159],{"class":53},[33,3627,2557],{"class":57},[33,3629,62],{"class":61},[33,3631,3632],{"class":101},"\"div\"",[33,3634,3635],{"class":61},", { id: ",[33,3637,3638],{"class":101},"\"app\"",[33,3640,3641],{"class":61}," },\n",[33,3643,3644,3647,3649,3652,3655,3658],{"class":35,"line":1234},[33,3645,3646],{"class":57},"    h",[33,3648,62],{"class":61},[33,3650,3651],{"class":101},"\"h1\"",[33,3653,3654],{"class":61},", {}, ",[33,3656,3657],{"class":101},"\"Hello\"",[33,3659,3660],{"class":61},"),\n",[33,3662,3663,3665,3667,3670,3673,3676,3678,3681],{"class":35,"line":1239},[33,3664,3646],{"class":57},[33,3666,62],{"class":61},[33,3668,3669],{"class":101},"\"p\"",[33,3671,3672],{"class":61},", { className: ",[33,3674,3675],{"class":101},"\"desc\"",[33,3677,976],{"class":61},[33,3679,3680],{"class":101},"\"World\"",[33,3682,3660],{"class":61},[33,3684,3685],{"class":35,"line":1245},[33,3686,290],{"class":61},[33,3688,3689,3691,3694,3696,3698,3700,3702,3704,3706],{"class":35,"line":1269},[33,3690,763],{"class":53},[33,3692,3693],{"class":155}," newTree",[33,3695,159],{"class":53},[33,3697,2557],{"class":57},[33,3699,62],{"class":61},[33,3701,3632],{"class":101},[33,3703,3635],{"class":61},[33,3705,3638],{"class":101},[33,3707,3641],{"class":61},[33,3709,3710,3712,3714,3716,3718,3721],{"class":35,"line":1293},[33,3711,3646],{"class":57},[33,3713,62],{"class":61},[33,3715,3651],{"class":101},[33,3717,3654],{"class":61},[33,3719,3720],{"class":101},"\"Hello Updated\"",[33,3722,3660],{"class":61},[33,3724,3725,3727,3729,3731,3733,3735,3738,3741,3744,3746],{"class":35,"line":1298},[33,3726,3646],{"class":57},[33,3728,62],{"class":61},[33,3730,3669],{"class":101},[33,3732,3672],{"class":61},[33,3734,3675],{"class":101},[33,3736,3737],{"class":61},", style: { color: ",[33,3739,3740],{"class":101},"\"red\"",[33,3742,3743],{"class":61}," } }, ",[33,3745,3680],{"class":101},[33,3747,3660],{"class":61},[33,3749,3751,3753,3755,3758,3761,3764,3767,3769,3772,3774,3777,3780,3783],{"class":35,"line":3750},94,[33,3752,3646],{"class":57},[33,3754,62],{"class":61},[33,3756,3757],{"class":101},"\"button\"",[33,3759,3760],{"class":61},", { ",[33,3762,3763],{"class":57},"onClick",[33,3765,3766],{"class":61},": () ",[33,3768,465],{"class":53},[33,3770,3771],{"class":57}," alert",[33,3773,62],{"class":61},[33,3775,3776],{"class":101},"\"clicked\"",[33,3778,3779],{"class":61},") }, ",[33,3781,3782],{"class":101},"\"Click\"",[33,3784,3660],{"class":61},[33,3786,3788],{"class":35,"line":3787},95,[33,3789,290],{"class":61},[33,3791,3793,3795,3798,3800,3802,3805,3807,3810],{"class":35,"line":3792},96,[33,3794,763],{"class":53},[33,3796,3797],{"class":155}," container",[33,3799,159],{"class":53},[33,3801,2730],{"class":61},[33,3803,3804],{"class":57},"querySelector",[33,3806,62],{"class":61},[33,3808,3809],{"class":101},"\"#root\"",[33,3811,290],{"class":61},[33,3813,3815,3818,3820,3822,3824,3827],{"class":35,"line":3814},97,[33,3816,3817],{"class":61},"container.",[33,3819,2970],{"class":57},[33,3821,62],{"class":61},[33,3823,2975],{"class":57},[33,3825,3826],{"class":61},"(oldTree));  ",[33,3828,3829],{"class":39},"\u002F\u002F initial render\n",[33,3831,3833,3836,3839],{"class":35,"line":3832},98,[33,3834,3835],{"class":57},"patch",[33,3837,3838],{"class":61},"(container, oldTree, newTree);  ",[33,3840,3841],{"class":39},"\u002F\u002F diff and patch (minimal DOM updates)\n",[15,3843,3845],{"id":3844},"project-4-websocket-real-time-chat","Project 4: WebSocket Real-Time Chat",[20,3847,3848],{"language":22},[24,3849,3851],{"className":26,"code":3850,"language":22,"meta":28,"style":28},"\u002F\u002F ── WebSocket client with reconnection, heartbeat, and message queuing ──\n\nclass ChatClient {\n    constructor(url, options = {}) {\n        this.url = url;\n        this.options = {\n            heartbeatInterval: 30000,\n            reconnectDelay: 1000,\n            maxReconnectDelay: 30000,\n            ...options,\n        };\n        this.ws = null;\n        this.reconnectAttempts = 0;\n        this.messageQueue = [];  \u002F\u002F messages sent while disconnected\n        this.handlers = new Map();  \u002F\u002F event handlers\n        this.heartbeatId = null;\n        this.shouldReconnect = true;\n    }\n\n    connect() {\n        this.ws = new WebSocket(this.url);\n        this.ws.onopen = () => {\n            this.reconnectAttempts = 0;\n            this.flushQueue();  \u002F\u002F send queued messages\n            this.startHeartbeat();\n            this.emit(\"connected\");\n        };\n        this.ws.onmessage = (event) => {\n            const message = JSON.parse(event.data);\n            this.emit(message.type, message.data);  \u002F\u002F dispatch to handlers\n        };\n        this.ws.onclose = () => {\n            this.stopHeartbeat();\n            if (this.shouldReconnect) this.scheduleReconnect();\n            this.emit(\"disconnected\");\n        };\n        this.ws.onerror = (error) => this.emit(\"error\", error);\n    }\n\n    send(type, data) {\n        const message = JSON.stringify({ type, data, timestamp: Date.now() });\n        if (this.ws?.readyState === WebSocket.OPEN) {\n            this.ws.send(message);\n        } else {\n            this.messageQueue.push(message);  \u002F\u002F queue if disconnected\n        }\n    }\n\n    on(event, handler) {\n        if (!this.handlers.has(event)) this.handlers.set(event, new Set());\n        this.handlers.get(event).add(handler);\n        return () => this.handlers.get(event)?.delete(handler);  \u002F\u002F unsubscribe\n    }\n\n    emit(event, data) { this.handlers.get(event)?.forEach(fn => fn(data)); }\n\n    flushQueue() {\n        while (this.messageQueue.length) this.ws.send(this.messageQueue.shift());\n    }\n\n    startHeartbeat() {\n        this.heartbeatId = setInterval(() => this.send(\"ping\", {}), this.options.heartbeatInterval);\n    }\n\n    stopHeartbeat() { if (this.heartbeatId) clearInterval(this.heartbeatId); }\n\n    scheduleReconnect() {\n        const delay = Math.min(\n            this.options.reconnectDelay * Math.pow(2, this.reconnectAttempts),\n            this.options.maxReconnectDelay,\n        );\n        this.reconnectAttempts++;\n        setTimeout(() => this.connect(), delay);\n    }\n\n    disconnect() {\n        this.shouldReconnect = false;\n        this.stopHeartbeat();\n        this.ws?.close(1000, \"client disconnect\");\n    }\n}\n\n\u002F\u002F ── Usage ──\nconst chat = new ChatClient(\"wss:\u002F\u002Fchat.example.com\u002Fws\");\nchat.on(\"connected\", () => console.log(\"connected to chat\"));\nchat.on(\"message\", (data) => console.log(`${data.user}: ${data.text}`));\nchat.on(\"disconnected\", () => console.log(\"disconnected — reconnecting...\"));\nchat.connect();\nchat.send(\"message\", { user: \"Alice\", text: \"Hello, world!\" });  \u002F\u002F queues if not connected\n",[30,3852,3853,3858,3862,3871,3888,3900,3911,3921,3930,3939,3946,3950,3963,3976,3991,4010,4023,4036,4040,4044,4052,4072,4090,4103,4117,4128,4144,4148,4170,4191,4205,4209,4226,4237,4257,4272,4276,4309,4313,4317,4334,4357,4378,4389,4397,4413,4417,4421,4425,4441,4477,4493,4518,4522,4526,4562,4566,4573,4605,4609,4613,4620,4654,4658,4662,4689,4693,4700,4716,4740,4747,4752,4763,4782,4786,4790,4797,4809,4819,4840,4844,4848,4852,4856,4876,4905,4953,4978,4986],{"__ignoreMap":28},[33,3854,3855],{"class":35,"line":36},[33,3856,3857],{"class":39},"\u002F\u002F ── WebSocket client with reconnection, heartbeat, and message queuing ──\n",[33,3859,3860],{"class":35,"line":43},[33,3861,47],{"emptyLinePlaceholder":46},[33,3863,3864,3866,3869],{"class":35,"line":50},[33,3865,1333],{"class":53},[33,3867,3868],{"class":57}," ChatClient",[33,3870,327],{"class":61},[33,3872,3873,3875,3877,3880,3882,3884,3886],{"class":35,"line":83},[33,3874,1343],{"class":53},[33,3876,62],{"class":61},[33,3878,3879],{"class":65},"url",[33,3881,69],{"class":61},[33,3883,1461],{"class":65},[33,3885,159],{"class":53},[33,3887,1358],{"class":61},[33,3889,3890,3892,3895,3897],{"class":35,"line":111},[33,3891,1363],{"class":155},[33,3893,3894],{"class":61},".url ",[33,3896,143],{"class":53},[33,3898,3899],{"class":61}," url;\n",[33,3901,3902,3904,3907,3909],{"class":35,"line":123},[33,3903,1363],{"class":155},[33,3905,3906],{"class":61},".options ",[33,3908,143],{"class":53},[33,3910,327],{"class":61},[33,3912,3913,3916,3919],{"class":35,"line":129},[33,3914,3915],{"class":61},"            heartbeatInterval: ",[33,3917,3918],{"class":155},"30000",[33,3920,1393],{"class":61},[33,3922,3923,3926,3928],{"class":35,"line":134},[33,3924,3925],{"class":61},"            reconnectDelay: ",[33,3927,1288],{"class":155},[33,3929,1393],{"class":61},[33,3931,3932,3935,3937],{"class":35,"line":149},[33,3933,3934],{"class":61},"            maxReconnectDelay: ",[33,3936,3918],{"class":155},[33,3938,1393],{"class":61},[33,3940,3941,3943],{"class":35,"line":171},[33,3942,1428],{"class":53},[33,3944,3945],{"class":61},"options,\n",[33,3947,3948],{"class":35,"line":187},[33,3949,653],{"class":61},[33,3951,3952,3954,3957,3959,3961],{"class":35,"line":192},[33,3953,1363],{"class":155},[33,3955,3956],{"class":61},".ws ",[33,3958,143],{"class":53},[33,3960,256],{"class":155},[33,3962,184],{"class":61},[33,3964,3965,3967,3970,3972,3974],{"class":35,"line":210},[33,3966,1363],{"class":155},[33,3968,3969],{"class":61},".reconnectAttempts ",[33,3971,143],{"class":53},[33,3973,1560],{"class":155},[33,3975,184],{"class":61},[33,3977,3978,3980,3983,3985,3988],{"class":35,"line":215},[33,3979,1363],{"class":155},[33,3981,3982],{"class":61},".messageQueue ",[33,3984,143],{"class":53},[33,3986,3987],{"class":61}," [];  ",[33,3989,3990],{"class":39},"\u002F\u002F messages sent while disconnected\n",[33,3992,3993,3995,3998,4000,4002,4005,4007],{"class":35,"line":230},[33,3994,1363],{"class":155},[33,3996,3997],{"class":61},".handlers ",[33,3999,143],{"class":53},[33,4001,162],{"class":53},[33,4003,4004],{"class":57}," Map",[33,4006,2522],{"class":61},[33,4008,4009],{"class":39},"\u002F\u002F event handlers\n",[33,4011,4012,4014,4017,4019,4021],{"class":35,"line":274},[33,4013,1363],{"class":155},[33,4015,4016],{"class":61},".heartbeatId ",[33,4018,143],{"class":53},[33,4020,256],{"class":155},[33,4022,184],{"class":61},[33,4024,4025,4027,4030,4032,4034],{"class":35,"line":293},[33,4026,1363],{"class":155},[33,4028,4029],{"class":61},".shouldReconnect ",[33,4031,143],{"class":53},[33,4033,338],{"class":155},[33,4035,184],{"class":61},[33,4037,4038],{"class":35,"line":299},[33,4039,126],{"class":61},[33,4041,4042],{"class":35,"line":321},[33,4043,47],{"emptyLinePlaceholder":46},[33,4045,4046,4049],{"class":35,"line":330},[33,4047,4048],{"class":57},"    connect",[33,4050,4051],{"class":61},"() {\n",[33,4053,4054,4056,4058,4060,4062,4065,4067,4069],{"class":35,"line":343},[33,4055,1363],{"class":155},[33,4057,3956],{"class":61},[33,4059,143],{"class":53},[33,4061,162],{"class":53},[33,4063,4064],{"class":57}," WebSocket",[33,4066,62],{"class":61},[33,4068,1481],{"class":155},[33,4070,4071],{"class":61},".url);\n",[33,4073,4074,4076,4079,4082,4084,4086,4088],{"class":35,"line":360},[33,4075,1363],{"class":155},[33,4077,4078],{"class":61},".ws.",[33,4080,4081],{"class":57},"onopen",[33,4083,159],{"class":53},[33,4085,462],{"class":61},[33,4087,465],{"class":53},[33,4089,327],{"class":61},[33,4091,4092,4095,4097,4099,4101],{"class":35,"line":371},[33,4093,4094],{"class":155},"            this",[33,4096,3969],{"class":61},[33,4098,143],{"class":53},[33,4100,1560],{"class":155},[33,4102,184],{"class":61},[33,4104,4105,4107,4109,4112,4114],{"class":35,"line":382},[33,4106,4094],{"class":155},[33,4108,1607],{"class":61},[33,4110,4111],{"class":57},"flushQueue",[33,4113,2522],{"class":61},[33,4115,4116],{"class":39},"\u002F\u002F send queued messages\n",[33,4118,4119,4121,4123,4126],{"class":35,"line":387},[33,4120,4094],{"class":155},[33,4122,1607],{"class":61},[33,4124,4125],{"class":57},"startHeartbeat",[33,4127,168],{"class":61},[33,4129,4130,4132,4134,4137,4139,4142],{"class":35,"line":413},[33,4131,4094],{"class":155},[33,4133,1607],{"class":61},[33,4135,4136],{"class":57},"emit",[33,4138,62],{"class":61},[33,4140,4141],{"class":101},"\"connected\"",[33,4143,290],{"class":61},[33,4145,4146],{"class":35,"line":421},[33,4147,653],{"class":61},[33,4149,4150,4152,4154,4157,4159,4161,4164,4166,4168],{"class":35,"line":426},[33,4151,1363],{"class":155},[33,4153,4078],{"class":61},[33,4155,4156],{"class":57},"onmessage",[33,4158,159],{"class":53},[33,4160,89],{"class":61},[33,4162,4163],{"class":65},"event",[33,4165,570],{"class":61},[33,4167,465],{"class":53},[33,4169,327],{"class":61},[33,4171,4172,4175,4178,4180,4183,4185,4188],{"class":35,"line":431},[33,4173,4174],{"class":53},"            const",[33,4176,4177],{"class":155}," message",[33,4179,159],{"class":53},[33,4181,4182],{"class":155}," JSON",[33,4184,1607],{"class":61},[33,4186,4187],{"class":57},"parse",[33,4189,4190],{"class":61},"(event.data);\n",[33,4192,4193,4195,4197,4199,4202],{"class":35,"line":446},[33,4194,4094],{"class":155},[33,4196,1607],{"class":61},[33,4198,4136],{"class":57},[33,4200,4201],{"class":61},"(message.type, message.data);  ",[33,4203,4204],{"class":39},"\u002F\u002F dispatch to handlers\n",[33,4206,4207],{"class":35,"line":457},[33,4208,653],{"class":61},[33,4210,4211,4213,4215,4218,4220,4222,4224],{"class":35,"line":480},[33,4212,1363],{"class":155},[33,4214,4078],{"class":61},[33,4216,4217],{"class":57},"onclose",[33,4219,159],{"class":53},[33,4221,462],{"class":61},[33,4223,465],{"class":53},[33,4225,327],{"class":61},[33,4227,4228,4230,4232,4235],{"class":35,"line":485},[33,4229,4094],{"class":155},[33,4231,1607],{"class":61},[33,4233,4234],{"class":57},"stopHeartbeat",[33,4236,168],{"class":61},[33,4238,4239,4241,4243,4245,4248,4250,4252,4255],{"class":35,"line":490},[33,4240,3382],{"class":53},[33,4242,89],{"class":61},[33,4244,1481],{"class":155},[33,4246,4247],{"class":61},".shouldReconnect) ",[33,4249,1481],{"class":155},[33,4251,1607],{"class":61},[33,4253,4254],{"class":57},"scheduleReconnect",[33,4256,168],{"class":61},[33,4258,4259,4261,4263,4265,4267,4270],{"class":35,"line":496},[33,4260,4094],{"class":155},[33,4262,1607],{"class":61},[33,4264,4136],{"class":57},[33,4266,62],{"class":61},[33,4268,4269],{"class":101},"\"disconnected\"",[33,4271,290],{"class":61},[33,4273,4274],{"class":35,"line":511},[33,4275,653],{"class":61},[33,4277,4278,4280,4282,4285,4287,4289,4291,4293,4295,4297,4299,4301,4303,4306],{"class":35,"line":516},[33,4279,1363],{"class":155},[33,4281,4078],{"class":61},[33,4283,4284],{"class":57},"onerror",[33,4286,159],{"class":53},[33,4288,89],{"class":61},[33,4290,2402],{"class":65},[33,4292,570],{"class":61},[33,4294,465],{"class":53},[33,4296,2033],{"class":155},[33,4298,1607],{"class":61},[33,4300,4136],{"class":57},[33,4302,62],{"class":61},[33,4304,4305],{"class":101},"\"error\"",[33,4307,4308],{"class":61},", error);\n",[33,4310,4311],{"class":35,"line":525},[33,4312,126],{"class":61},[33,4314,4315],{"class":35,"line":531},[33,4316,47],{"emptyLinePlaceholder":46},[33,4318,4319,4322,4324,4327,4329,4332],{"class":35,"line":536},[33,4320,4321],{"class":57},"    send",[33,4323,62],{"class":61},[33,4325,4326],{"class":65},"type",[33,4328,69],{"class":61},[33,4330,4331],{"class":65},"data",[33,4333,80],{"class":61},[33,4335,4336,4338,4340,4342,4344,4346,4348,4351,4354],{"class":35,"line":542},[33,4337,592],{"class":53},[33,4339,4177],{"class":155},[33,4341,159],{"class":53},[33,4343,4182],{"class":155},[33,4345,1607],{"class":61},[33,4347,2101],{"class":57},[33,4349,4350],{"class":61},"({ type, data, timestamp: Date.",[33,4352,4353],{"class":57},"now",[33,4355,4356],{"class":61},"() });\n",[33,4358,4359,4361,4363,4365,4368,4370,4373,4376],{"class":35,"line":560},[33,4360,233],{"class":53},[33,4362,89],{"class":61},[33,4364,1481],{"class":155},[33,4366,4367],{"class":61},".ws?.readyState ",[33,4369,98],{"class":53},[33,4371,4372],{"class":61}," WebSocket.",[33,4374,4375],{"class":155},"OPEN",[33,4377,80],{"class":61},[33,4379,4380,4382,4384,4387],{"class":35,"line":589},[33,4381,4094],{"class":155},[33,4383,4078],{"class":61},[33,4385,4386],{"class":57},"send",[33,4388,2233],{"class":61},[33,4390,4391,4393,4395],{"class":35,"line":605},[33,4392,363],{"class":61},[33,4394,2856],{"class":53},[33,4396,327],{"class":61},[33,4398,4399,4401,4404,4407,4410],{"class":35,"line":611},[33,4400,4094],{"class":155},[33,4402,4403],{"class":61},".messageQueue.",[33,4405,4406],{"class":57},"push",[33,4408,4409],{"class":61},"(message);  ",[33,4411,4412],{"class":39},"\u002F\u002F queue if disconnected\n",[33,4414,4415],{"class":35,"line":623},[33,4416,296],{"class":61},[33,4418,4419],{"class":35,"line":629},[33,4420,126],{"class":61},[33,4422,4423],{"class":35,"line":650},[33,4424,47],{"emptyLinePlaceholder":46},[33,4426,4427,4430,4432,4434,4436,4439],{"class":35,"line":656},[33,4428,4429],{"class":57},"    on",[33,4431,62],{"class":61},[33,4433,4163],{"class":65},[33,4435,69],{"class":61},[33,4437,4438],{"class":65},"handler",[33,4440,80],{"class":61},[33,4442,4443,4445,4447,4449,4451,4454,4457,4460,4462,4464,4467,4470,4473,4475],{"class":35,"line":685},[33,4444,233],{"class":53},[33,4446,89],{"class":61},[33,4448,1681],{"class":53},[33,4450,1481],{"class":155},[33,4452,4453],{"class":61},".handlers.",[33,4455,4456],{"class":57},"has",[33,4458,4459],{"class":61},"(event)) ",[33,4461,1481],{"class":155},[33,4463,4453],{"class":61},[33,4465,4466],{"class":57},"set",[33,4468,4469],{"class":61},"(event, ",[33,4471,4472],{"class":53},"new",[33,4474,165],{"class":57},[33,4476,840],{"class":61},[33,4478,4479,4481,4483,4485,4488,4490],{"class":35,"line":691},[33,4480,1363],{"class":155},[33,4482,4453],{"class":61},[33,4484,2341],{"class":57},[33,4486,4487],{"class":61},"(event).",[33,4489,451],{"class":57},[33,4491,4492],{"class":61},"(handler);\n",[33,4494,4495,4497,4499,4501,4503,4505,4507,4510,4512,4515],{"class":35,"line":725},[33,4496,114],{"class":53},[33,4498,462],{"class":61},[33,4500,465],{"class":53},[33,4502,2033],{"class":155},[33,4504,4453],{"class":61},[33,4506,2341],{"class":57},[33,4508,4509],{"class":61},"(event)?.",[33,4511,471],{"class":57},[33,4513,4514],{"class":61},"(handler);  ",[33,4516,4517],{"class":39},"\u002F\u002F unsubscribe\n",[33,4519,4520],{"class":35,"line":738},[33,4521,126],{"class":61},[33,4523,4524],{"class":35,"line":744},[33,4525,47],{"emptyLinePlaceholder":46},[33,4527,4528,4531,4533,4535,4537,4539,4541,4543,4545,4547,4549,4551,4553,4555,4557,4559],{"class":35,"line":749},[33,4529,4530],{"class":57},"    emit",[33,4532,62],{"class":61},[33,4534,4163],{"class":65},[33,4536,69],{"class":61},[33,4538,4331],{"class":65},[33,4540,2166],{"class":61},[33,4542,1481],{"class":155},[33,4544,4453],{"class":61},[33,4546,2341],{"class":57},[33,4548,4509],{"class":61},[33,4550,393],{"class":57},[33,4552,62],{"class":61},[33,4554,398],{"class":65},[33,4556,401],{"class":53},[33,4558,404],{"class":57},[33,4560,4561],{"class":61},"(data)); }\n",[33,4563,4564],{"class":35,"line":754},[33,4565,47],{"emptyLinePlaceholder":46},[33,4567,4568,4571],{"class":35,"line":760},[33,4569,4570],{"class":57},"    flushQueue",[33,4572,4051],{"class":61},[33,4574,4575,4578,4580,4582,4584,4586,4588,4590,4592,4594,4596,4598,4600,4603],{"class":35,"line":787},[33,4576,4577],{"class":53},"        while",[33,4579,89],{"class":61},[33,4581,1481],{"class":155},[33,4583,4403],{"class":61},[33,4585,3191],{"class":155},[33,4587,570],{"class":61},[33,4589,1481],{"class":155},[33,4591,4078],{"class":61},[33,4593,4386],{"class":57},[33,4595,62],{"class":61},[33,4597,1481],{"class":155},[33,4599,4403],{"class":61},[33,4601,4602],{"class":57},"shift",[33,4604,840],{"class":61},[33,4606,4607],{"class":35,"line":804},[33,4608,126],{"class":61},[33,4610,4611],{"class":35,"line":822},[33,4612,47],{"emptyLinePlaceholder":46},[33,4614,4615,4618],{"class":35,"line":843},[33,4616,4617],{"class":57},"    startHeartbeat",[33,4619,4051],{"class":61},[33,4621,4622,4624,4626,4628,4631,4633,4635,4637,4639,4641,4643,4646,4649,4651],{"class":35,"line":851},[33,4623,1363],{"class":155},[33,4625,4016],{"class":61},[33,4627,143],{"class":53},[33,4629,4630],{"class":57}," setInterval",[33,4632,1154],{"class":61},[33,4634,465],{"class":53},[33,4636,2033],{"class":155},[33,4638,1607],{"class":61},[33,4640,4386],{"class":57},[33,4642,62],{"class":61},[33,4644,4645],{"class":101},"\"ping\"",[33,4647,4648],{"class":61},", {}), ",[33,4650,1481],{"class":155},[33,4652,4653],{"class":61},".options.heartbeatInterval);\n",[33,4655,4656],{"class":35,"line":857},[33,4657,126],{"class":61},[33,4659,4660],{"class":35,"line":862},[33,4661,47],{"emptyLinePlaceholder":46},[33,4663,4664,4667,4669,4672,4674,4676,4679,4682,4684,4686],{"class":35,"line":868},[33,4665,4666],{"class":57},"    stopHeartbeat",[33,4668,201],{"class":61},[33,4670,4671],{"class":53},"if",[33,4673,89],{"class":61},[33,4675,1481],{"class":155},[33,4677,4678],{"class":61},".heartbeatId) ",[33,4680,4681],{"class":57},"clearInterval",[33,4683,62],{"class":61},[33,4685,1481],{"class":155},[33,4687,4688],{"class":61},".heartbeatId); }\n",[33,4690,4691],{"class":35,"line":892},[33,4692,47],{"emptyLinePlaceholder":46},[33,4694,4695,4698],{"class":35,"line":909},[33,4696,4697],{"class":57},"    scheduleReconnect",[33,4699,4051],{"class":61},[33,4701,4702,4704,4706,4708,4710,4713],{"class":35,"line":922},[33,4703,592],{"class":53},[33,4705,1938],{"class":155},[33,4707,159],{"class":53},[33,4709,1949],{"class":61},[33,4711,4712],{"class":57},"min",[33,4714,4715],{"class":61},"(\n",[33,4717,4718,4720,4723,4725,4727,4729,4731,4733,4735,4737],{"class":35,"line":927},[33,4719,4094],{"class":155},[33,4721,4722],{"class":61},".options.reconnectDelay ",[33,4724,1946],{"class":53},[33,4726,1949],{"class":61},[33,4728,1952],{"class":57},[33,4730,62],{"class":61},[33,4732,1411],{"class":155},[33,4734,69],{"class":61},[33,4736,1481],{"class":155},[33,4738,4739],{"class":61},".reconnectAttempts),\n",[33,4741,4742,4744],{"class":35,"line":939},[33,4743,4094],{"class":155},[33,4745,4746],{"class":61},".options.maxReconnectDelay,\n",[33,4748,4749],{"class":35,"line":944},[33,4750,4751],{"class":61},"        );\n",[33,4753,4754,4756,4759,4761],{"class":35,"line":949},[33,4755,1363],{"class":155},[33,4757,4758],{"class":61},".reconnectAttempts",[33,4760,1572],{"class":53},[33,4762,184],{"class":61},[33,4764,4765,4768,4770,4772,4774,4776,4779],{"class":35,"line":955},[33,4766,4767],{"class":57},"        setTimeout",[33,4769,1154],{"class":61},[33,4771,465],{"class":53},[33,4773,2033],{"class":155},[33,4775,1607],{"class":61},[33,4777,4778],{"class":57},"connect",[33,4780,4781],{"class":61},"(), delay);\n",[33,4783,4784],{"class":35,"line":983},[33,4785,126],{"class":61},[33,4787,4788],{"class":35,"line":992},[33,4789,47],{"emptyLinePlaceholder":46},[33,4791,4792,4795],{"class":35,"line":1028},[33,4793,4794],{"class":57},"    disconnect",[33,4796,4051],{"class":61},[33,4798,4799,4801,4803,4805,4807],{"class":35,"line":1057},[33,4800,1363],{"class":155},[33,4802,4029],{"class":61},[33,4804,143],{"class":53},[33,4806,181],{"class":155},[33,4808,184],{"class":61},[33,4810,4811,4813,4815,4817],{"class":35,"line":1082},[33,4812,1363],{"class":155},[33,4814,1607],{"class":61},[33,4816,4234],{"class":57},[33,4818,168],{"class":61},[33,4820,4821,4823,4826,4829,4831,4833,4835,4838],{"class":35,"line":1099},[33,4822,1363],{"class":155},[33,4824,4825],{"class":61},".ws?.",[33,4827,4828],{"class":57},"close",[33,4830,62],{"class":61},[33,4832,1288],{"class":155},[33,4834,69],{"class":61},[33,4836,4837],{"class":101},"\"client disconnect\"",[33,4839,290],{"class":61},[33,4841,4842],{"class":35,"line":1104},[33,4843,126],{"class":61},[33,4845,4846],{"class":35,"line":1109},[33,4847,528],{"class":61},[33,4849,4850],{"class":35,"line":1114},[33,4851,47],{"emptyLinePlaceholder":46},[33,4853,4854],{"class":35,"line":1120},[33,4855,1117],{"class":39},[33,4857,4858,4860,4863,4865,4867,4869,4871,4874],{"class":35,"line":1145},[33,4859,763],{"class":53},[33,4861,4862],{"class":155}," chat",[33,4864,159],{"class":53},[33,4866,162],{"class":53},[33,4868,3868],{"class":57},[33,4870,62],{"class":61},[33,4872,4873],{"class":101},"\"wss:\u002F\u002Fchat.example.com\u002Fws\"",[33,4875,290],{"class":61},[33,4877,4878,4881,4884,4886,4888,4891,4893,4895,4897,4899,4902],{"class":35,"line":1176},[33,4879,4880],{"class":61},"chat.",[33,4882,4883],{"class":57},"on",[33,4885,62],{"class":61},[33,4887,4141],{"class":101},[33,4889,4890],{"class":61},", () ",[33,4892,465],{"class":53},[33,4894,1159],{"class":61},[33,4896,793],{"class":57},[33,4898,62],{"class":61},[33,4900,4901],{"class":101},"\"connected to chat\"",[33,4903,4904],{"class":61},"));\n",[33,4906,4907,4909,4911,4913,4916,4919,4921,4923,4925,4927,4929,4931,4933,4935,4937,4940,4942,4944,4946,4949,4951],{"class":35,"line":1195},[33,4908,4880],{"class":61},[33,4910,4883],{"class":57},[33,4912,62],{"class":61},[33,4914,4915],{"class":101},"\"message\"",[33,4917,4918],{"class":61},", (",[33,4920,4331],{"class":65},[33,4922,570],{"class":61},[33,4924,465],{"class":53},[33,4926,1159],{"class":61},[33,4928,793],{"class":57},[33,4930,62],{"class":61},[33,4932,1602],{"class":101},[33,4934,4331],{"class":61},[33,4936,1607],{"class":101},[33,4938,4939],{"class":61},"user",[33,4941,2416],{"class":101},[33,4943,4331],{"class":61},[33,4945,1607],{"class":101},[33,4947,4948],{"class":61},"text",[33,4950,1617],{"class":101},[33,4952,4904],{"class":61},[33,4954,4955,4957,4959,4961,4963,4965,4967,4969,4971,4973,4976],{"class":35,"line":1217},[33,4956,4880],{"class":61},[33,4958,4883],{"class":57},[33,4960,62],{"class":61},[33,4962,4269],{"class":101},[33,4964,4890],{"class":61},[33,4966,465],{"class":53},[33,4968,1159],{"class":61},[33,4970,793],{"class":57},[33,4972,62],{"class":61},[33,4974,4975],{"class":101},"\"disconnected — reconnecting...\"",[33,4977,4904],{"class":61},[33,4979,4980,4982,4984],{"class":35,"line":1234},[33,4981,4880],{"class":61},[33,4983,4778],{"class":57},[33,4985,168],{"class":61},[33,4987,4988,4990,4992,4994,4996,4999,5001,5004,5007,5009],{"class":35,"line":1239},[33,4989,4880],{"class":61},[33,4991,4386],{"class":57},[33,4993,62],{"class":61},[33,4995,4915],{"class":101},[33,4997,4998],{"class":61},", { user: ",[33,5000,2375],{"class":101},[33,5002,5003],{"class":61},", text: ",[33,5005,5006],{"class":101},"\"Hello, world!\"",[33,5008,1189],{"class":61},[33,5010,5011],{"class":39},"\u002F\u002F queues if not connected\n",[15,5013,5015],{"id":5014},"project-5-debounce-throttle-and-memoize-utilities","Project 5: Debounce, Throttle, and Memoize Utilities",[20,5017,5018],{"language":22},[24,5019,5021],{"className":26,"code":5020,"language":22,"meta":28,"style":28},"\u002F\u002F ── Production-grade utility functions with edge-case handling ──\n\n\u002F\u002F ── Debounce: delay execution until calls stop for `wait` ms ──\nfunction debounce(fn, wait, { leading = false, trailing = true } = {}) {\n    let timerId = null;\n    let lastArgs = null;\n    function debounced(...args) {\n        lastArgs = args;\n        if (timerId) clearTimeout(timerId);\n        if (leading && !timerId) fn.apply(this, args);  \u002F\u002F call immediately on first\n        timerId = setTimeout(() => {\n            timerId = null;\n            if (trailing && lastArgs) fn.apply(this, lastArgs);  \u002F\u002F call on trailing\n        }, wait);\n    }\n    debounced.cancel = () => { clearTimeout(timerId); timerId = null; };\n    debounced.flush = () => { if (timerId) { clearTimeout(timerId); timerId = null; fn.apply(this, lastArgs); } };\n    return debounced;\n}\n\n\u002F\u002F ── Throttle: execute at most once per `wait` ms ──\nfunction throttle(fn, wait, { leading = true, trailing = true } = {}) {\n    let lastCall = 0;\n    let timerId = null;\n    let lastArgs = null;\n    function throttled(...args) {\n        const now = Date.now();\n        lastArgs = args;\n        const remaining = wait - (now - lastCall);\n        if (remaining \u003C= 0 || remaining > wait) {\n            if (timerId) { clearTimeout(timerId); timerId = null; }\n            lastCall = now;\n            fn.apply(this, args);\n        } else if (trailing && !timerId) {\n            timerId = setTimeout(() => {\n                lastCall = Date.now();\n                timerId = null;\n                fn.apply(this, lastArgs);\n            }, remaining);\n        }\n    }\n    throttled.cancel = () => { clearTimeout(timerId); timerId = null; lastCall = 0; };\n    return throttled;\n}\n\n\u002F\u002F ── Memoize: cache results by argument identity (WeakMap for objects) ──\nfunction memoize(fn) {\n    const primitiveCache = new Map();   \u002F\u002F for primitive args\n    const objectCache = new WeakMap();  \u002F\u002F for object args (GC'd when key is GC'd)\n    return function memoized(...args) {\n        if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\n            if (objectCache.has(args[0])) return objectCache.get(args[0]);\n            const result = fn.apply(this, args);\n            objectCache.set(args[0], result);\n            return result;\n        }\n        const key = JSON.stringify(args);\n        if (primitiveCache.has(key)) return primitiveCache.get(key);\n        const result = fn.apply(this, args);\n        primitiveCache.set(key, result);\n        return result;\n    };\n}\n\n\u002F\u002F ── Usage ──\nconst debouncedSearch = debounce((query) => fetchResults(query), 300);\ninput.addEventListener(\"input\", (e) => debouncedSearch(e.target.value));\n\nconst throttledScroll = throttle(() => updateProgress(), 100);\nwindow.addEventListener(\"scroll\", throttledScroll);\n\nconst expensiveCalc = memoize((n) => {\n    console.log(\"computing...\");\n    return n * 2;\n});\n",[30,5022,5023,5028,5032,5037,5078,5091,5104,5120,5130,5143,5171,5186,5197,5221,5226,5230,5258,5298,5305,5309,5313,5318,5355,5368,5380,5392,5407,5423,5431,5453,5475,5492,5502,5516,5533,5547,5560,5571,5585,5590,5594,5598,5630,5637,5641,5645,5650,5663,5682,5701,5719,5762,5793,5812,5826,5833,5837,5854,5875,5893,5903,5909,5913,5917,5921,5925,5956,5982,5986,6011,6026,6030,6052,6065,6079],{"__ignoreMap":28},[33,5024,5025],{"class":35,"line":36},[33,5026,5027],{"class":39},"\u002F\u002F ── Production-grade utility functions with edge-case handling ──\n",[33,5029,5030],{"class":35,"line":43},[33,5031,47],{"emptyLinePlaceholder":46},[33,5033,5034],{"class":35,"line":50},[33,5035,5036],{"class":39},"\u002F\u002F ── Debounce: delay execution until calls stop for `wait` ms ──\n",[33,5038,5039,5041,5044,5046,5048,5050,5053,5055,5058,5060,5062,5064,5067,5069,5071,5074,5076],{"class":35,"line":83},[33,5040,54],{"class":53},[33,5042,5043],{"class":57}," debounce",[33,5045,62],{"class":61},[33,5047,398],{"class":65},[33,5049,69],{"class":61},[33,5051,5052],{"class":65},"wait",[33,5054,3760],{"class":61},[33,5056,5057],{"class":65},"leading",[33,5059,159],{"class":53},[33,5061,181],{"class":155},[33,5063,69],{"class":61},[33,5065,5066],{"class":65},"trailing",[33,5068,159],{"class":53},[33,5070,338],{"class":155},[33,5072,5073],{"class":61}," } ",[33,5075,143],{"class":53},[33,5077,1358],{"class":61},[33,5079,5080,5082,5085,5087,5089],{"class":35,"line":111},[33,5081,137],{"class":53},[33,5083,5084],{"class":61}," timerId ",[33,5086,143],{"class":53},[33,5088,256],{"class":155},[33,5090,184],{"class":61},[33,5092,5093,5095,5098,5100,5102],{"class":35,"line":123},[33,5094,137],{"class":53},[33,5096,5097],{"class":61}," lastArgs ",[33,5099,143],{"class":53},[33,5101,256],{"class":155},[33,5103,184],{"class":61},[33,5105,5106,5108,5111,5113,5115,5118],{"class":35,"line":129},[33,5107,195],{"class":53},[33,5109,5110],{"class":57}," debounced",[33,5112,62],{"class":61},[33,5114,552],{"class":53},[33,5116,5117],{"class":65},"args",[33,5119,80],{"class":61},[33,5121,5122,5125,5127],{"class":35,"line":134},[33,5123,5124],{"class":61},"        lastArgs ",[33,5126,143],{"class":53},[33,5128,5129],{"class":61}," args;\n",[33,5131,5132,5134,5137,5140],{"class":35,"line":149},[33,5133,233],{"class":53},[33,5135,5136],{"class":61}," (timerId) ",[33,5138,5139],{"class":57},"clearTimeout",[33,5141,5142],{"class":61},"(timerId);\n",[33,5144,5145,5147,5150,5152,5155,5158,5161,5163,5165,5168],{"class":35,"line":171},[33,5146,233],{"class":53},[33,5148,5149],{"class":61}," (leading ",[33,5151,2808],{"class":53},[33,5153,5154],{"class":53}," !",[33,5156,5157],{"class":61},"timerId) fn.",[33,5159,5160],{"class":57},"apply",[33,5162,62],{"class":61},[33,5164,1481],{"class":155},[33,5166,5167],{"class":61},", args);  ",[33,5169,5170],{"class":39},"\u002F\u002F call immediately on first\n",[33,5172,5173,5176,5178,5180,5182,5184],{"class":35,"line":187},[33,5174,5175],{"class":61},"        timerId ",[33,5177,143],{"class":53},[33,5179,1517],{"class":57},[33,5181,1154],{"class":61},[33,5183,465],{"class":53},[33,5185,327],{"class":61},[33,5187,5188,5191,5193,5195],{"class":35,"line":192},[33,5189,5190],{"class":61},"            timerId ",[33,5192,143],{"class":53},[33,5194,256],{"class":155},[33,5196,184],{"class":61},[33,5198,5199,5201,5204,5206,5209,5211,5213,5215,5218],{"class":35,"line":210},[33,5200,3382],{"class":53},[33,5202,5203],{"class":61}," (trailing ",[33,5205,2808],{"class":53},[33,5207,5208],{"class":61}," lastArgs) fn.",[33,5210,5160],{"class":57},[33,5212,62],{"class":61},[33,5214,1481],{"class":155},[33,5216,5217],{"class":61},", lastArgs);  ",[33,5219,5220],{"class":39},"\u002F\u002F call on trailing\n",[33,5222,5223],{"class":35,"line":215},[33,5224,5225],{"class":61},"        }, wait);\n",[33,5227,5228],{"class":35,"line":230},[33,5229,126],{"class":61},[33,5231,5232,5235,5238,5240,5242,5244,5246,5248,5251,5253,5255],{"class":35,"line":274},[33,5233,5234],{"class":61},"    debounced.",[33,5236,5237],{"class":57},"cancel",[33,5239,159],{"class":53},[33,5241,462],{"class":61},[33,5243,465],{"class":53},[33,5245,730],{"class":61},[33,5247,5139],{"class":57},[33,5249,5250],{"class":61},"(timerId); timerId ",[33,5252,143],{"class":53},[33,5254,256],{"class":155},[33,5256,5257],{"class":61},"; };\n",[33,5259,5260,5262,5265,5267,5269,5271,5273,5275,5278,5280,5282,5284,5286,5289,5291,5293,5295],{"class":35,"line":293},[33,5261,5234],{"class":61},[33,5263,5264],{"class":57},"flush",[33,5266,159],{"class":53},[33,5268,462],{"class":61},[33,5270,465],{"class":53},[33,5272,730],{"class":61},[33,5274,4671],{"class":53},[33,5276,5277],{"class":61}," (timerId) { ",[33,5279,5139],{"class":57},[33,5281,5250],{"class":61},[33,5283,143],{"class":53},[33,5285,256],{"class":155},[33,5287,5288],{"class":61},"; fn.",[33,5290,5160],{"class":57},[33,5292,62],{"class":61},[33,5294,1481],{"class":155},[33,5296,5297],{"class":61},", lastArgs); } };\n",[33,5299,5300,5302],{"class":35,"line":299},[33,5301,519],{"class":53},[33,5303,5304],{"class":61}," debounced;\n",[33,5306,5307],{"class":35,"line":321},[33,5308,528],{"class":61},[33,5310,5311],{"class":35,"line":330},[33,5312,47],{"emptyLinePlaceholder":46},[33,5314,5315],{"class":35,"line":343},[33,5316,5317],{"class":39},"\u002F\u002F ── Throttle: execute at most once per `wait` ms ──\n",[33,5319,5320,5322,5325,5327,5329,5331,5333,5335,5337,5339,5341,5343,5345,5347,5349,5351,5353],{"class":35,"line":360},[33,5321,54],{"class":53},[33,5323,5324],{"class":57}," throttle",[33,5326,62],{"class":61},[33,5328,398],{"class":65},[33,5330,69],{"class":61},[33,5332,5052],{"class":65},[33,5334,3760],{"class":61},[33,5336,5057],{"class":65},[33,5338,159],{"class":53},[33,5340,338],{"class":155},[33,5342,69],{"class":61},[33,5344,5066],{"class":65},[33,5346,159],{"class":53},[33,5348,338],{"class":155},[33,5350,5073],{"class":61},[33,5352,143],{"class":53},[33,5354,1358],{"class":61},[33,5356,5357,5359,5362,5364,5366],{"class":35,"line":371},[33,5358,137],{"class":53},[33,5360,5361],{"class":61}," lastCall ",[33,5363,143],{"class":53},[33,5365,1560],{"class":155},[33,5367,184],{"class":61},[33,5369,5370,5372,5374,5376,5378],{"class":35,"line":382},[33,5371,137],{"class":53},[33,5373,5084],{"class":61},[33,5375,143],{"class":53},[33,5377,256],{"class":155},[33,5379,184],{"class":61},[33,5381,5382,5384,5386,5388,5390],{"class":35,"line":387},[33,5383,137],{"class":53},[33,5385,5097],{"class":61},[33,5387,143],{"class":53},[33,5389,256],{"class":155},[33,5391,184],{"class":61},[33,5393,5394,5396,5399,5401,5403,5405],{"class":35,"line":413},[33,5395,195],{"class":53},[33,5397,5398],{"class":57}," throttled",[33,5400,62],{"class":61},[33,5402,552],{"class":53},[33,5404,5117],{"class":65},[33,5406,80],{"class":61},[33,5408,5409,5411,5414,5416,5419,5421],{"class":35,"line":421},[33,5410,592],{"class":53},[33,5412,5413],{"class":155}," now",[33,5415,159],{"class":53},[33,5417,5418],{"class":61}," Date.",[33,5420,4353],{"class":57},[33,5422,168],{"class":61},[33,5424,5425,5427,5429],{"class":35,"line":426},[33,5426,5124],{"class":61},[33,5428,143],{"class":53},[33,5430,5129],{"class":61},[33,5432,5433,5435,5438,5440,5443,5445,5448,5450],{"class":35,"line":431},[33,5434,592],{"class":53},[33,5436,5437],{"class":155}," remaining",[33,5439,159],{"class":53},[33,5441,5442],{"class":61}," wait ",[33,5444,1046],{"class":53},[33,5446,5447],{"class":61}," (now ",[33,5449,1046],{"class":53},[33,5451,5452],{"class":61}," lastCall);\n",[33,5454,5455,5457,5460,5462,5464,5466,5469,5472],{"class":35,"line":446},[33,5456,233],{"class":53},[33,5458,5459],{"class":61}," (remaining ",[33,5461,1566],{"class":53},[33,5463,1560],{"class":155},[33,5465,249],{"class":53},[33,5467,5468],{"class":61}," remaining ",[33,5470,5471],{"class":53},">",[33,5473,5474],{"class":61}," wait) {\n",[33,5476,5477,5479,5481,5483,5485,5487,5489],{"class":35,"line":457},[33,5478,3382],{"class":53},[33,5480,5277],{"class":61},[33,5482,5139],{"class":57},[33,5484,5250],{"class":61},[33,5486,143],{"class":53},[33,5488,256],{"class":155},[33,5490,5491],{"class":61},"; }\n",[33,5493,5494,5497,5499],{"class":35,"line":480},[33,5495,5496],{"class":61},"            lastCall ",[33,5498,143],{"class":53},[33,5500,5501],{"class":61}," now;\n",[33,5503,5504,5507,5509,5511,5513],{"class":35,"line":485},[33,5505,5506],{"class":61},"            fn.",[33,5508,5160],{"class":57},[33,5510,62],{"class":61},[33,5512,1481],{"class":155},[33,5514,5515],{"class":61},", args);\n",[33,5517,5518,5520,5522,5524,5526,5528,5530],{"class":35,"line":490},[33,5519,363],{"class":61},[33,5521,2856],{"class":53},[33,5523,2859],{"class":53},[33,5525,5203],{"class":61},[33,5527,2808],{"class":53},[33,5529,5154],{"class":53},[33,5531,5532],{"class":61},"timerId) {\n",[33,5534,5535,5537,5539,5541,5543,5545],{"class":35,"line":496},[33,5536,5190],{"class":61},[33,5538,143],{"class":53},[33,5540,1517],{"class":57},[33,5542,1154],{"class":61},[33,5544,465],{"class":53},[33,5546,327],{"class":61},[33,5548,5549,5552,5554,5556,5558],{"class":35,"line":511},[33,5550,5551],{"class":61},"                lastCall ",[33,5553,143],{"class":53},[33,5555,5418],{"class":61},[33,5557,4353],{"class":57},[33,5559,168],{"class":61},[33,5561,5562,5565,5567,5569],{"class":35,"line":516},[33,5563,5564],{"class":61},"                timerId ",[33,5566,143],{"class":53},[33,5568,256],{"class":155},[33,5570,184],{"class":61},[33,5572,5573,5576,5578,5580,5582],{"class":35,"line":525},[33,5574,5575],{"class":61},"                fn.",[33,5577,5160],{"class":57},[33,5579,62],{"class":61},[33,5581,1481],{"class":155},[33,5583,5584],{"class":61},", lastArgs);\n",[33,5586,5587],{"class":35,"line":531},[33,5588,5589],{"class":61},"            }, remaining);\n",[33,5591,5592],{"class":35,"line":536},[33,5593,296],{"class":61},[33,5595,5596],{"class":35,"line":542},[33,5597,126],{"class":61},[33,5599,5600,5603,5605,5607,5609,5611,5613,5615,5617,5619,5621,5624,5626,5628],{"class":35,"line":560},[33,5601,5602],{"class":61},"    throttled.",[33,5604,5237],{"class":57},[33,5606,159],{"class":53},[33,5608,462],{"class":61},[33,5610,465],{"class":53},[33,5612,730],{"class":61},[33,5614,5139],{"class":57},[33,5616,5250],{"class":61},[33,5618,143],{"class":53},[33,5620,256],{"class":155},[33,5622,5623],{"class":61},"; lastCall ",[33,5625,143],{"class":53},[33,5627,1560],{"class":155},[33,5629,5257],{"class":61},[33,5631,5632,5634],{"class":35,"line":589},[33,5633,519],{"class":53},[33,5635,5636],{"class":61}," throttled;\n",[33,5638,5639],{"class":35,"line":605},[33,5640,528],{"class":61},[33,5642,5643],{"class":35,"line":611},[33,5644,47],{"emptyLinePlaceholder":46},[33,5646,5647],{"class":35,"line":623},[33,5648,5649],{"class":39},"\u002F\u002F ── Memoize: cache results by argument identity (WeakMap for objects) ──\n",[33,5651,5652,5654,5657,5659,5661],{"class":35,"line":629},[33,5653,54],{"class":53},[33,5655,5656],{"class":57}," memoize",[33,5658,62],{"class":61},[33,5660,398],{"class":65},[33,5662,80],{"class":61},[33,5664,5665,5667,5670,5672,5674,5676,5679],{"class":35,"line":650},[33,5666,152],{"class":53},[33,5668,5669],{"class":155}," primitiveCache",[33,5671,159],{"class":53},[33,5673,162],{"class":53},[33,5675,4004],{"class":57},[33,5677,5678],{"class":61},"();   ",[33,5680,5681],{"class":39},"\u002F\u002F for primitive args\n",[33,5683,5684,5686,5689,5691,5693,5696,5698],{"class":35,"line":656},[33,5685,152],{"class":53},[33,5687,5688],{"class":155}," objectCache",[33,5690,159],{"class":53},[33,5692,162],{"class":53},[33,5694,5695],{"class":57}," WeakMap",[33,5697,2522],{"class":61},[33,5699,5700],{"class":39},"\u002F\u002F for object args (GC'd when key is GC'd)\n",[33,5702,5703,5705,5708,5711,5713,5715,5717],{"class":35,"line":685},[33,5704,519],{"class":53},[33,5706,5707],{"class":53}," function",[33,5709,5710],{"class":57}," memoized",[33,5712,62],{"class":61},[33,5714,552],{"class":53},[33,5716,5117],{"class":65},[33,5718,80],{"class":61},[33,5720,5721,5723,5726,5728,5731,5733,5735,5737,5740,5742,5744,5746,5748,5750,5752,5754,5756,5758,5760],{"class":35,"line":691},[33,5722,233],{"class":53},[33,5724,5725],{"class":61}," (args.",[33,5727,3191],{"class":155},[33,5729,5730],{"class":53}," ===",[33,5732,1022],{"class":155},[33,5734,1706],{"class":53},[33,5736,261],{"class":53},[33,5738,5739],{"class":61}," args[",[33,5741,973],{"class":155},[33,5743,2776],{"class":61},[33,5745,98],{"class":53},[33,5747,246],{"class":101},[33,5749,1706],{"class":53},[33,5751,5739],{"class":61},[33,5753,973],{"class":155},[33,5755,2776],{"class":61},[33,5757,243],{"class":53},[33,5759,256],{"class":155},[33,5761,80],{"class":61},[33,5763,5764,5766,5769,5771,5774,5776,5779,5781,5784,5786,5788,5790],{"class":35,"line":725},[33,5765,3382],{"class":53},[33,5767,5768],{"class":61}," (objectCache.",[33,5770,4456],{"class":57},[33,5772,5773],{"class":61},"(args[",[33,5775,973],{"class":155},[33,5777,5778],{"class":61},"])) ",[33,5780,204],{"class":53},[33,5782,5783],{"class":61}," objectCache.",[33,5785,2341],{"class":57},[33,5787,5773],{"class":61},[33,5789,973],{"class":155},[33,5791,5792],{"class":61},"]);\n",[33,5794,5795,5797,5799,5801,5804,5806,5808,5810],{"class":35,"line":738},[33,5796,4174],{"class":53},[33,5798,809],{"class":155},[33,5800,159],{"class":53},[33,5802,5803],{"class":61}," fn.",[33,5805,5160],{"class":57},[33,5807,62],{"class":61},[33,5809,1481],{"class":155},[33,5811,5515],{"class":61},[33,5813,5814,5817,5819,5821,5823],{"class":35,"line":744},[33,5815,5816],{"class":61},"            objectCache.",[33,5818,4466],{"class":57},[33,5820,5773],{"class":61},[33,5822,973],{"class":155},[33,5824,5825],{"class":61},"], result);\n",[33,5827,5828,5831],{"class":35,"line":749},[33,5829,5830],{"class":53},"            return",[33,5832,848],{"class":61},[33,5834,5835],{"class":35,"line":754},[33,5836,296],{"class":61},[33,5838,5839,5841,5843,5845,5847,5849,5851],{"class":35,"line":760},[33,5840,592],{"class":53},[33,5842,3350],{"class":155},[33,5844,159],{"class":53},[33,5846,4182],{"class":155},[33,5848,1607],{"class":61},[33,5850,2101],{"class":57},[33,5852,5853],{"class":61},"(args);\n",[33,5855,5856,5858,5861,5863,5866,5868,5871,5873],{"class":35,"line":787},[33,5857,233],{"class":53},[33,5859,5860],{"class":61}," (primitiveCache.",[33,5862,4456],{"class":57},[33,5864,5865],{"class":61},"(key)) ",[33,5867,204],{"class":53},[33,5869,5870],{"class":61}," primitiveCache.",[33,5872,2341],{"class":57},[33,5874,3425],{"class":61},[33,5876,5877,5879,5881,5883,5885,5887,5889,5891],{"class":35,"line":804},[33,5878,592],{"class":53},[33,5880,809],{"class":155},[33,5882,159],{"class":53},[33,5884,5803],{"class":61},[33,5886,5160],{"class":57},[33,5888,62],{"class":61},[33,5890,1481],{"class":155},[33,5892,5515],{"class":61},[33,5894,5895,5898,5900],{"class":35,"line":822},[33,5896,5897],{"class":61},"        primitiveCache.",[33,5899,4466],{"class":57},[33,5901,5902],{"class":61},"(key, result);\n",[33,5904,5905,5907],{"class":35,"line":843},[33,5906,114],{"class":53},[33,5908,848],{"class":61},[33,5910,5911],{"class":35,"line":851},[33,5912,741],{"class":61},[33,5914,5915],{"class":35,"line":857},[33,5916,528],{"class":61},[33,5918,5919],{"class":35,"line":862},[33,5920,47],{"emptyLinePlaceholder":46},[33,5922,5923],{"class":35,"line":868},[33,5924,1117],{"class":39},[33,5926,5927,5929,5932,5934,5936,5938,5941,5943,5945,5948,5951,5954],{"class":35,"line":892},[33,5928,763],{"class":53},[33,5930,5931],{"class":155}," debouncedSearch",[33,5933,159],{"class":53},[33,5935,5043],{"class":57},[33,5937,706],{"class":61},[33,5939,5940],{"class":65},"query",[33,5942,570],{"class":61},[33,5944,465],{"class":53},[33,5946,5947],{"class":57}," fetchResults",[33,5949,5950],{"class":61},"(query), ",[33,5952,5953],{"class":155},"300",[33,5955,290],{"class":61},[33,5957,5958,5961,5963,5965,5968,5970,5973,5975,5977,5979],{"class":35,"line":909},[33,5959,5960],{"class":61},"input.",[33,5962,2827],{"class":57},[33,5964,62],{"class":61},[33,5966,5967],{"class":101},"\"input\"",[33,5969,4918],{"class":61},[33,5971,5972],{"class":65},"e",[33,5974,570],{"class":61},[33,5976,465],{"class":53},[33,5978,5931],{"class":57},[33,5980,5981],{"class":61},"(e.target.value));\n",[33,5983,5984],{"class":35,"line":922},[33,5985,47],{"emptyLinePlaceholder":46},[33,5987,5988,5990,5993,5995,5997,5999,6001,6004,6007,6009],{"class":35,"line":927},[33,5989,763],{"class":53},[33,5991,5992],{"class":155}," throttledScroll",[33,5994,159],{"class":53},[33,5996,5324],{"class":57},[33,5998,1154],{"class":61},[33,6000,465],{"class":53},[33,6002,6003],{"class":57}," updateProgress",[33,6005,6006],{"class":61},"(), ",[33,6008,1421],{"class":155},[33,6010,290],{"class":61},[33,6012,6013,6016,6018,6020,6023],{"class":35,"line":939},[33,6014,6015],{"class":61},"window.",[33,6017,2827],{"class":57},[33,6019,62],{"class":61},[33,6021,6022],{"class":101},"\"scroll\"",[33,6024,6025],{"class":61},", throttledScroll);\n",[33,6027,6028],{"class":35,"line":944},[33,6029,47],{"emptyLinePlaceholder":46},[33,6031,6032,6034,6037,6039,6041,6043,6046,6048,6050],{"class":35,"line":949},[33,6033,763],{"class":53},[33,6035,6036],{"class":155}," expensiveCalc",[33,6038,159],{"class":53},[33,6040,5656],{"class":57},[33,6042,706],{"class":61},[33,6044,6045],{"class":65},"n",[33,6047,570],{"class":61},[33,6049,465],{"class":53},[33,6051,327],{"class":61},[33,6053,6054,6056,6058,6060,6063],{"class":35,"line":955},[33,6055,790],{"class":61},[33,6057,793],{"class":57},[33,6059,62],{"class":61},[33,6061,6062],{"class":101},"\"computing...\"",[33,6064,290],{"class":61},[33,6066,6067,6069,6072,6074,6077],{"class":35,"line":983},[33,6068,519],{"class":53},[33,6070,6071],{"class":61}," n ",[33,6073,1946],{"class":53},[33,6075,6076],{"class":155}," 2",[33,6078,184],{"class":61},[33,6080,6081],{"class":35,"line":992},[33,6082,6083],{"class":61},"});\n",[15,6085,6087],{"id":6086},"spot-the-bug-memory-leak-in-event-listener","Spot the Bug: Memory Leak in Event Listener",[20,6089,6090],{"language":22},[24,6091,6093],{"className":26,"code":6092,"language":22,"meta":28,"style":28},"function setupWidget() {\n    const button = document.querySelector(\"#btn\");\n    const heavyData = new Array(1_000_000).fill(0);\n    button.addEventListener(\"click\", function onClick() {\n        process(heavyData);  \u002F\u002F closure captures heavyData\n    });\n    return () => button.removeEventListener(\"click\", onClick);\n}\nconst cleanup = setupWidget();\n\u002F\u002F cleanup() is never called → heavyData stays in memory forever\n",[30,6094,6095,6104,6124,6154,6175,6186,6191,6211,6215,6228],{"__ignoreMap":28},[33,6096,6097,6099,6102],{"class":35,"line":36},[33,6098,54],{"class":53},[33,6100,6101],{"class":57}," setupWidget",[33,6103,4051],{"class":61},[33,6105,6106,6108,6111,6113,6115,6117,6119,6122],{"class":35,"line":43},[33,6107,152],{"class":53},[33,6109,6110],{"class":155}," button",[33,6112,159],{"class":53},[33,6114,2730],{"class":61},[33,6116,3804],{"class":57},[33,6118,62],{"class":61},[33,6120,6121],{"class":101},"\"#btn\"",[33,6123,290],{"class":61},[33,6125,6126,6128,6131,6133,6135,6138,6140,6143,6145,6148,6150,6152],{"class":35,"line":50},[33,6127,152],{"class":53},[33,6129,6130],{"class":155}," heavyData",[33,6132,159],{"class":53},[33,6134,162],{"class":53},[33,6136,6137],{"class":57}," Array",[33,6139,62],{"class":61},[33,6141,6142],{"class":155},"1_000_000",[33,6144,2840],{"class":61},[33,6146,6147],{"class":57},"fill",[33,6149,62],{"class":61},[33,6151,973],{"class":155},[33,6153,290],{"class":61},[33,6155,6156,6159,6161,6163,6166,6168,6170,6173],{"class":35,"line":83},[33,6157,6158],{"class":61},"    button.",[33,6160,2827],{"class":57},[33,6162,62],{"class":61},[33,6164,6165],{"class":101},"\"click\"",[33,6167,69],{"class":61},[33,6169,54],{"class":53},[33,6171,6172],{"class":57}," onClick",[33,6174,4051],{"class":61},[33,6176,6177,6180,6183],{"class":35,"line":111},[33,6178,6179],{"class":57},"        process",[33,6181,6182],{"class":61},"(heavyData);  ",[33,6184,6185],{"class":39},"\u002F\u002F closure captures heavyData\n",[33,6187,6188],{"class":35,"line":123},[33,6189,6190],{"class":61},"    });\n",[33,6192,6193,6195,6197,6199,6202,6204,6206,6208],{"class":35,"line":129},[33,6194,519],{"class":53},[33,6196,462],{"class":61},[33,6198,465],{"class":53},[33,6200,6201],{"class":61}," button.",[33,6203,3396],{"class":57},[33,6205,62],{"class":61},[33,6207,6165],{"class":101},[33,6209,6210],{"class":61},", onClick);\n",[33,6212,6213],{"class":35,"line":134},[33,6214,528],{"class":61},[33,6216,6217,6219,6222,6224,6226],{"class":35,"line":149},[33,6218,763],{"class":53},[33,6220,6221],{"class":155}," cleanup",[33,6223,159],{"class":53},[33,6225,6101],{"class":57},[33,6227,168],{"class":61},[33,6229,6230],{"class":35,"line":171},[33,6231,6232],{"class":39},"\u002F\u002F cleanup() is never called → heavyData stays in memory forever\n",[6234,6235,6236,6240,6265,6280,6289,6429],"details",{},[6237,6238,6239],"summary",{},"Answer",[6241,6242,6243,6244,6246,6247,6250,6251,6253,6254,6256,6257,6261,6262,6264],"p",{},"The ",[30,6245,3763],{}," function is defined as a function expression assigned to a variable inside ",[30,6248,6249],{},"setupWidget",". But the ",[30,6252,3396],{}," call in the cleanup function references ",[30,6255,3763],{}," — which is a ",[6258,6259,6260],"strong",{},"named function expression",". The name ",[30,6263,3763],{}," is only available inside the function body, not in the outer scope.",[6241,6266,6267,6268,6271,6272,6275,6276,6279],{},"So ",[30,6269,6270],{},"removeEventListener(\"click\", onClick)"," will throw a ",[30,6273,6274],{},"ReferenceError: onClick is not defined"," — the listener is never removed, ",[30,6277,6278],{},"heavyData"," (8MB) stays in memory, and the button keeps triggering the handler even after the widget is \"destroyed.\"",[6241,6281,6282,6285,6286,6288],{},[6258,6283,6284],{},"Fix",": declare the handler in the outer scope so ",[30,6287,3396],{}," can reference it:",[24,6290,6292],{"className":26,"code":6291,"language":22,"meta":28,"style":28},"function setupWidget() {\n    const button = document.querySelector(\"#btn\");\n    const heavyData = new Array(1_000_000).fill(0);\n    function onClick() {       \u002F\u002F ✅ declared in setupWidget's scope\n        process(heavyData);\n    }\n    button.addEventListener(\"click\", onClick);\n    return () => button.removeEventListener(\"click\", onClick);  \u002F\u002F ✅ can reference onClick\n}\nconst cleanup = setupWidget();\ncleanup();  \u002F\u002F ✅ listener removed, heavyData can be GC'd\n",[30,6293,6294,6302,6320,6346,6358,6365,6369,6381,6403,6407,6419],{"__ignoreMap":28},[33,6295,6296,6298,6300],{"class":35,"line":36},[33,6297,54],{"class":53},[33,6299,6101],{"class":57},[33,6301,4051],{"class":61},[33,6303,6304,6306,6308,6310,6312,6314,6316,6318],{"class":35,"line":43},[33,6305,152],{"class":53},[33,6307,6110],{"class":155},[33,6309,159],{"class":53},[33,6311,2730],{"class":61},[33,6313,3804],{"class":57},[33,6315,62],{"class":61},[33,6317,6121],{"class":101},[33,6319,290],{"class":61},[33,6321,6322,6324,6326,6328,6330,6332,6334,6336,6338,6340,6342,6344],{"class":35,"line":50},[33,6323,152],{"class":53},[33,6325,6130],{"class":155},[33,6327,159],{"class":53},[33,6329,162],{"class":53},[33,6331,6137],{"class":57},[33,6333,62],{"class":61},[33,6335,6142],{"class":155},[33,6337,2840],{"class":61},[33,6339,6147],{"class":57},[33,6341,62],{"class":61},[33,6343,973],{"class":155},[33,6345,290],{"class":61},[33,6347,6348,6350,6352,6355],{"class":35,"line":83},[33,6349,195],{"class":53},[33,6351,6172],{"class":57},[33,6353,6354],{"class":61},"() {       ",[33,6356,6357],{"class":39},"\u002F\u002F ✅ declared in setupWidget's scope\n",[33,6359,6360,6362],{"class":35,"line":111},[33,6361,6179],{"class":57},[33,6363,6364],{"class":61},"(heavyData);\n",[33,6366,6367],{"class":35,"line":123},[33,6368,126],{"class":61},[33,6370,6371,6373,6375,6377,6379],{"class":35,"line":129},[33,6372,6158],{"class":61},[33,6374,2827],{"class":57},[33,6376,62],{"class":61},[33,6378,6165],{"class":101},[33,6380,6210],{"class":61},[33,6382,6383,6385,6387,6389,6391,6393,6395,6397,6400],{"class":35,"line":134},[33,6384,519],{"class":53},[33,6386,462],{"class":61},[33,6388,465],{"class":53},[33,6390,6201],{"class":61},[33,6392,3396],{"class":57},[33,6394,62],{"class":61},[33,6396,6165],{"class":101},[33,6398,6399],{"class":61},", onClick);  ",[33,6401,6402],{"class":39},"\u002F\u002F ✅ can reference onClick\n",[33,6404,6405],{"class":35,"line":149},[33,6406,528],{"class":61},[33,6408,6409,6411,6413,6415,6417],{"class":35,"line":171},[33,6410,763],{"class":53},[33,6412,6221],{"class":155},[33,6414,159],{"class":53},[33,6416,6101],{"class":57},[33,6418,168],{"class":61},[33,6420,6421,6424,6426],{"class":35,"line":187},[33,6422,6423],{"class":57},"cleanup",[33,6425,2522],{"class":61},[33,6427,6428],{"class":39},"\u002F\u002F ✅ listener removed, heavyData can be GC'd\n",[6241,6430,6431,1001,6434,6436,6437,6440,6441,6443,6444,6447],{},[6258,6432,6433],{},"The lesson",[30,6435,3396],{}," requires a reference to the ",[6258,6438,6439],{},"exact same function object"," that was passed to ",[30,6442,2827],{},". Named function expressions (",[30,6445,6446],{},"const fn = function name() {}",") have the name available only inside the function — not in the enclosing scope. Always store the handler reference where the cleanup function can access it.",[6449,6450,6451],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":28,"searchDepth":43,"depth":43,"links":6453},[6454,6455,6456,6457,6458,6459],{"id":17,"depth":43,"text":18},{"id":1311,"depth":43,"text":1312},{"id":2528,"depth":43,"text":2529},{"id":3844,"depth":43,"text":3845},{"id":5014,"depth":43,"text":5015},{"id":6086,"depth":43,"text":6087},"Capstone projects for senior JavaScript engineers: build a reactive state store, a promise-based API client with retry, a virtual DOM diffing engine, a WebSocket real-time chat, and a full testing suite. Code-first reference with production-grade implementations.","md",{},"\u002Fjs\u002F30-exercises-and-projects",{"title":5,"description":6460},"js\u002F30-exercises-and-projects","qEJ-2uj3nteTSJj8knttRvUEu07gr_z3NP-312dJMaM",1789924649600]