[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"playground-using-explicit-resource-management":32},[4,20],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[13,15,29],"es",{"courses":17,"paths":17,"articles":18,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":17},2,{"slug":33,"title":34,"summary":35,"html":36,"css":37,"js":38,"config":39,"doc":41},"using-explicit-resource-management","using \u002F explicit resource management — interactive playground",null,"\u003Cdiv class=\"pg\">\n    \u003Ch1 class=\"pg-title\">🎮 using \u002F explicit resource management\u003C\u002Fh1>\n    \u003Cp class=\"pg-sub\">\n      Three resources — a \u003Ccode>Lock\u003C\u002Fcode>, a \u003Ccode>DB Connection\u003C\u002Fcode>, and a \u003Ccode>File Handle\u003C\u002Fcode> —\n      need to be released in reverse order, even when the work between them throws. Pick a cleanup\n      style, then run the happy path or the throwing path and watch what actually gets disposed.\n    \u003C\u002Fp>\n\n    \u003Cdiv class=\"banner info show\">\n      \u003Cstrong>Simulated, not native.\u003C\u002Fstrong> \u003Ccode>using\u003C\u002Fcode> is TC39 Stage 3 — it shipped in\n      TypeScript 5.2 and runs natively in recent Chrome, Firefox, Safari and Node, but it is not yet\n      Baseline across all browsers. This demo scripts the disposal order the spec defines, so it\n      behaves correctly no matter what's rendering this page.\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Cdiv class=\"panel-label\">\u003Cspan class=\"n\">1\u003C\u002Fspan> Controls\u003C\u002Fdiv>\n\n      \u003Cdiv class=\"field\" style=\"margin-bottom: 14px;\">\n        \u003Cspan class=\"label\">Cleanup style\u003C\u002Fspan>\n        \u003Cdiv class=\"seg\" id=\"modeSeg\" role=\"group\" aria-label=\"Cleanup style\">\n          \u003Cbutton type=\"button\" data-mode=\"using\" aria-pressed=\"true\">using declaration\u003C\u002Fbutton>\n          \u003Cbutton type=\"button\" data-mode=\"manual\" aria-pressed=\"false\">manual try\u002Ffinally\u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n\n      \u003Cdiv class=\"btn-row\">\n        \u003Cbutton class=\"btn primary\" id=\"runHappy\">▶️ Run happy path\u003C\u002Fbutton>\n        \u003Cbutton class=\"btn bad\" id=\"runThrow\">💥 Run throwing path\u003C\u002Fbutton>\n        \u003Cbutton class=\"btn ghost\" id=\"resetBtn\">↺ Reset\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n\n      \u003Cdiv class=\"presets\" style=\"margin-top: 14px\">\n        \u003Cbutton class=\"btn sm\" data-preset=\"using-happy\">using · happy\u003C\u002Fbutton>\n        \u003Cbutton class=\"btn sm\" data-preset=\"using-throw\">using · throw (still safe)\u003C\u002Fbutton>\n        \u003Cbutton class=\"btn sm\" data-preset=\"manual-happy\">manual · happy (partial leak)\u003C\u002Fbutton>\n        \u003Cbutton class=\"btn sm\" data-preset=\"manual-throw\">manual · throw (leaks!)\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Cdiv class=\"panel-label\">\u003Cspan class=\"n\">2\u003C\u002Fspan> The code being simulated\u003C\u002Fdiv>\n      \u003Cdiv class=\"code-wrap\">\n        \u003Cpre class=\"code code-mode active\" id=\"codeUsing\">function process() {\n  \u003Cspan class=\"tok-kw\">using\u003C\u002Fspan> lock = acquireLock();\n  \u003Cspan class=\"tok-kw\">using\u003C\u002Fspan> db   = openConnection();\n  \u003Cspan class=\"tok-kw\">using\u003C\u002Fspan> file = openFile();\n\n  doWork(); \u003Cspan class=\"tok-cmt\">\u002F\u002F may throw\u003C\u002Fspan>\n}\n\u003Cspan class=\"tok-cmt\">\u002F\u002F scope exits — normally OR via throw — and\u003C\u002Fspan>\n\u003Cspan class=\"tok-cmt\">\u002F\u002F file, then db, then lock dispose automatically, in reverse order.\u003C\u002Fspan>\u003C\u002Fpre>\n        \u003Cpre class=\"code code-mode\" id=\"codeManual\">\u003Cspan class=\"tok-kw\">function\u003C\u002Fspan> process() {\n  \u003Cspan class=\"tok-kw\">const\u003C\u002Fspan> lock = acquireLock();\n  \u003Cspan class=\"tok-kw\">const\u003C\u002Fspan> db   = openConnection();\n  \u003Cspan class=\"tok-kw\">const\u003C\u002Fspan> file = openFile();\n\n  \u003Cspan class=\"tok-kw\">try\u003C\u002Fspan> {\n    doWork();        \u003Cspan class=\"tok-cmt\">\u002F\u002F may throw\u003C\u002Fspan>\n    file.dispose();  \u003Cspan class=\"tok-cmt\">\u002F\u002F only reached if doWork() succeeds\u003C\u002Fspan>\n    db.dispose();    \u003Cspan class=\"tok-cmt\">\u002F\u002F only reached if doWork() succeeds\u003C\u002Fspan>\n  } \u003Cspan class=\"tok-kw\">finally\u003C\u002Fspan> {\n    \u003Cspan class=\"tok-cmt\">\u002F\u002F lock.dispose();  &lt;- forgotten entirely\u003C\u002Fspan>\n  }\n}\u003C\u002Fpre>\n      \u003C\u002Fdiv>\n      \u003Cp class=\"hint\">\n        Acquisition order:\n        \u003Cspan class=\"order-line mono\">\u003Cstrong>Lock\u003C\u002Fstrong> \u003Cspan class=\"arrow\">→\u003C\u002Fspan> \u003Cstrong>DB Connection\u003C\u002Fstrong> \u003Cspan class=\"arrow\">→\u003C\u002Fspan> \u003Cstrong>File Handle\u003C\u002Fstrong>\u003C\u002Fspan>.\n        Correct disposal always reverses that:\n        \u003Cspan class=\"order-line mono\">\u003Cstrong>File Handle\u003C\u002Fstrong> \u003Cspan class=\"arrow\">→\u003C\u002Fspan> \u003Cstrong>DB Connection\u003C\u002Fstrong> \u003Cspan class=\"arrow\">→\u003C\u002Fspan> \u003Cstrong>Lock\u003C\u002Fstrong>\u003C\u002Fspan>.\n      \u003C\u002Fp>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Cdiv class=\"panel-label\">\u003Cspan class=\"n\">3\u003C\u002Fspan> Resources &amp; log\u003C\u002Fdiv>\n\n      \u003Cdiv class=\"resources\" id=\"resources\">\n        \u003Cdiv class=\"resource\" data-res=\"lock\">\n          \u003Cspan class=\"icon\">🔒\u003C\u002Fspan>\u003Cspan class=\"name\">Lock\u003C\u002Fspan>\n          \u003Cspan class=\"pill\" id=\"pill-lock\">idle\u003C\u002Fspan>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"resource\" data-res=\"db\">\n          \u003Cspan class=\"icon\">🗄️\u003C\u002Fspan>\u003Cspan class=\"name\">DB Connection\u003C\u002Fspan>\n          \u003Cspan class=\"pill\" id=\"pill-db\">idle\u003C\u002Fspan>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"resource\" data-res=\"file\">\n          \u003Cspan class=\"icon\">📄\u003C\u002Fspan>\u003Cspan class=\"name\">File Handle\u003C\u002Fspan>\n          \u003Cspan class=\"pill\" id=\"pill-file\">idle\u003C\u002Fspan>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n\n      \u003Cdiv class=\"log\" id=\"log\" role=\"log\" aria-label=\"Execution log\">Click a run button to start…\u003C\u002Fdiv>\n\n      \u003Cdiv class=\"status\" id=\"status\" style=\"margin-top: 12px\">\n        \u003Cspan class=\"dot info\" id=\"dot\">\u003C\u002Fspan>\n        \u003Cspan id=\"statusText\" aria-live=\"polite\">Ready — pick a cleanup style, then run a path.\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n      \u003Cp class=\"hint\" id=\"hint\">\n        Every resource opens in the same order either way. Watch which ones a thrown error skips,\n        and which one manual code simply never remembers to close.\n      \u003C\u002Fp>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>","\u002F*\n * Shared playground design system — daily-post\n * ============================================\n * Link it from any post's playground\u002Findex.html (no per-post boilerplate):\n *\n *   \u003Clink rel=\"stylesheet\" href=\"..\u002F..\u002F..\u002Fplayground\u002Fassets\u002Fbase.css\" \u002F>\n *\n * The relative path resolves the same locally (posts\u002F\u003Cbase>\u002Fplayground\u002F) and on\n * the deployed host (public\u002Fposts\u002F\u003Cbase>\u002Fplayground\u002F), because build-site.js\n * mirrors this file to public\u002Fplayground\u002Fassets\u002Fbase.css. For a CodeSandbox\n * --embed, playground.js inlines this file so the single-file sandbox still styles.\n *\n * Then write ONLY the demo-specific CSS in your inline \u003Cstyle>. Everything below\n * — tokens, layout, panels, buttons, toggles, sliders, code blocks, status\u002Flog,\n * banners, presets — comes for free and looks consistent across every post.\n *\n * Theme-aware: dark by default, light via prefers-color-scheme AND a\n * [data-theme] override on \u003Chtml> (so a toggle can force either). Accessible:\n * real focus rings, adequate contrast, reduced-motion honored.\n *\u002F\n\n\u002F* ---------- design tokens ---------- *\u002F\n:root {\n  --bg: #0d1117;\n  --bg-grid: #ffffff0a;\n  --panel: #161b22;\n  --panel-2: #0b0e14;\n  --card: #0b0e14;\n  --ink: #e8ebf2;\n  --muted: #9aa4b2;\n  --faint: #6b7688;\n  --line: #232a35;\n  --line-soft: #ffffff12;\n  --accent: #7c9cff;\n  --accent-ink: #0b0e14;\n  --good: #3fb950;\n  --warn: #f0883e;\n  --bad: #f85149;\n  --info: #58a6ff;\n  --radius: 14px;\n  --radius-sm: 9px;\n  --shadow: 0 8px 30px #0006;\n  --shadow-sm: 0 4px 14px #0005;\n  --mono: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n  --sans: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif;\n  color-scheme: dark;\n}\n\n\u002F* Light theme — system preference, overridable by [data-theme] on \u003Chtml>. *\u002F\n@media (prefers-color-scheme: light) {\n  :root:not([data-theme=\"dark\"]) {\n    --bg: #f4f6fb;\n    --bg-grid: #00000008;\n    --panel: #ffffff;\n    --panel-2: #f0f3f9;\n    --card: #f7f9fc;\n    --ink: #131722;\n    --muted: #5b6472;\n    --faint: #8a93a3;\n    --line: #e2e7f0;\n    --line-soft: #00000010;\n    --accent: #2f6df0;\n    --accent-ink: #ffffff;\n    --good: #1a7f37;\n    --warn: #bc4c00;\n    --bad: #cf222e;\n    --info: #0969da;\n    --shadow: 0 8px 30px #0000001f;\n    --shadow-sm: 0 4px 14px #00000014;\n    color-scheme: light;\n  }\n}\n:root[data-theme=\"light\"] {\n  --bg: #f4f6fb;\n  --bg-grid: #00000008;\n  --panel: #ffffff;\n  --panel-2: #f0f3f9;\n  --card: #f7f9fc;\n  --ink: #131722;\n  --muted: #5b6472;\n  --faint: #8a93a3;\n  --line: #e2e7f0;\n  --line-soft: #00000010;\n  --accent: #2f6df0;\n  --accent-ink: #ffffff;\n  --good: #1a7f37;\n  --warn: #bc4c00;\n  --bad: #cf222e;\n  --info: #0969da;\n  --shadow: 0 8px 30px #0000001f;\n  --shadow-sm: 0 4px 14px #00000014;\n  color-scheme: light;\n}\n\n\u002F* ---------- reset & base ---------- *\u002F\n*, *::before, *::after { box-sizing: border-box; }\n\nbody {\n  margin: 0;\n  min-height: 100vh;\n  font-family: var(--sans);\n  color: var(--ink);\n  background:\n    radial-gradient(circle at 1px 1px, var(--bg-grid) 1px, transparent 0) 0 0 \u002F 24px 24px,\n    var(--bg);\n  display: flex;\n  justify-content: center;\n  align-items: flex-start;\n  padding: clamp(14px, 3vw, 26px);\n  line-height: 1.55;\n  -webkit-font-smoothing: antialiased;\n  text-rendering: optimizeLegibility;\n}\n\n\u002F* The single content column. *\u002F\n.pg {\n  width: 100%;\n  max-width: 860px;\n}\n\n\u002F* ---------- header ---------- *\u002F\n.pg-title {\n  font-size: clamp(1.15rem, 1rem + 1vw, 1.4rem);\n  font-weight: 800;\n  letter-spacing: -0.02em;\n  margin: 0 0 4px;\n  display: flex;\n  align-items: center;\n  gap: 9px;\n}\n.pg-sub {\n  color: var(--muted);\n  font-size: 0.92rem;\n  line-height: 1.5;\n  margin: 0 0 16px;\n  max-width: 66ch;\n}\n.pg-sub strong, .pg-sub b { color: var(--ink); font-weight: 700; }\n.pg-sub code { font-family: var(--mono); background: var(--line-soft); color: var(--accent);\n  padding: 1px 6px; border-radius: 6px; font-size: 0.85em; }\n\n\u002F* ---------- panels ---------- *\u002F\n.panel {\n  background: var(--panel);\n  border: 1px solid var(--line);\n  border-radius: var(--radius);\n  padding: 16px 18px;\n  margin-bottom: 14px;\n  box-shadow: var(--shadow-sm);\n}\n.panel-label {\n  font-size: 0.7rem;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: var(--muted);\n  font-weight: 800;\n  margin: 0 0 13px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n.panel-label .n {\n  display: inline-grid; place-items: center;\n  width: 18px; height: 18px; border-radius: 50%;\n  background: var(--accent); color: var(--accent-ink);\n  font-size: 0.66rem; font-weight: 800;\n}\n\n\u002F* ---------- layout helpers ---------- *\u002F\n.row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }\n.row.between { justify-content: space-between; }\n.col { display: flex; flex-direction: column; gap: 10px; }\n.grow { flex: 1 1 0; min-width: 0; }\n.wrap-controls { display: flex; flex-wrap: wrap; gap: 16px 24px; align-items: flex-end; }\n.field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }\n.field > .label, .ctl-label {\n  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;\n  color: var(--muted); font-weight: 800;\n}\n\n\u002F* ---------- buttons ---------- *\u002F\n.btn {\n  font: inherit; font-weight: 650; font-size: 0.88rem;\n  cursor: pointer; user-select: none;\n  border: 1px solid var(--line);\n  background: var(--card); color: var(--ink);\n  padding: 9px 15px; border-radius: var(--radius-sm);\n  display: inline-flex; align-items: center; gap: 8px;\n  transition: background .15s ease, border-color .15s ease, transform .05s ease, color .15s ease;\n}\n.btn:hover { background: var(--line-soft); border-color: var(--muted); }\n.btn:active { transform: translateY(1px); }\n.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }\n.btn[disabled] { opacity: 0.45; cursor: not-allowed; }\n.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }\n.btn.primary:hover { filter: brightness(1.08); background: var(--accent); }\n.btn.good { background: var(--good); border-color: var(--good); color: #04140a; }\n.btn.bad  { background: var(--bad);  border-color: var(--bad);  color: #fff; }\n.btn.ghost { background: transparent; }\n.btn.sm { padding: 6px 11px; font-size: 0.82rem; }\n\n.btn-row { display: flex; flex-wrap: wrap; gap: 9px; }\n\n\u002F* Segmented control — a row of buttons where one is active (aria-pressed). *\u002F\n.seg { display: inline-flex; background: var(--card); border: 1px solid var(--line);\n  border-radius: var(--radius-sm); padding: 3px; gap: 3px; }\n.seg button {\n  font: inherit; font-weight: 650; font-size: 0.84rem; cursor: pointer;\n  border: none; background: transparent; color: var(--muted);\n  padding: 7px 13px; border-radius: 7px;\n  transition: background .15s ease, color .15s ease;\n}\n.seg button:hover { color: var(--ink); }\n.seg button[aria-pressed=\"true\"] { background: var(--accent); color: var(--accent-ink); }\n.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }\n\n\u002F* Preset chips — worked-example shortcuts. *\u002F\n.presets { display: flex; flex-wrap: wrap; gap: 8px; }\n.presets .btn { background: var(--panel-2); }\n\n\u002F* ---------- toggle switch ---------- *\u002F\n.toggle { display: inline-flex; align-items: center; gap: 10px; cursor: pointer;\n  font-size: 0.88rem; font-weight: 600; user-select: none; }\n.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }\n.switch { position: relative; width: 44px; height: 25px; border-radius: 999px; flex: none;\n  background: var(--line); border: 1px solid var(--line); transition: background .18s ease; }\n.switch::after { content: \"\"; position: absolute; top: 2px; left: 2px; width: 19px; height: 19px;\n  border-radius: 50%; background: #cfd6e4; transition: transform .18s ease; box-shadow: 0 1px 3px #0006; }\n.toggle input:checked + .switch { background: var(--good); border-color: var(--good); }\n.toggle input:checked + .switch::after { transform: translateX(19px); background: #fff; }\n.toggle input:focus-visible + .switch { outline: 2px solid var(--accent); outline-offset: 2px; }\n\n\u002F* ---------- range slider ---------- *\u002F\ninput[type=\"range\"] {\n  -webkit-appearance: none; appearance: none; width: 100%; height: 6px;\n  background: var(--line); border-radius: 999px; outline: none; cursor: pointer; margin: 8px 0;\n}\ninput[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none; appearance: none; width: 19px; height: 19px; border-radius: 50%;\n  background: var(--accent); border: 2px solid var(--panel); box-shadow: var(--shadow-sm); cursor: pointer;\n}\ninput[type=\"range\"]::-moz-range-thumb {\n  width: 19px; height: 19px; border-radius: 50%; background: var(--accent);\n  border: 2px solid var(--panel); box-shadow: var(--shadow-sm); cursor: pointer;\n}\ninput[type=\"range\"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }\n\n\u002F* ---------- text inputs \u002F selects ---------- *\u002F\ninput[type=\"text\"], input[type=\"number\"], input[type=\"search\"], select, textarea {\n  font: inherit; color: var(--ink); background: var(--card);\n  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 8px 11px; width: 100%;\n}\ntextarea { font-family: var(--mono); font-size: 0.85rem; line-height: 1.6; resize: vertical; }\ninput:focus-visible, select:focus-visible, textarea:focus-visible {\n  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);\n}\n\n\u002F* ---------- status \u002F output \u002F log ---------- *\u002F\n.status {\n  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;\n  font-size: 0.9rem; font-weight: 600;\n  padding: 11px 14px; border-radius: var(--radius-sm);\n  background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);\n}\n.status.good { border-color: color-mix(in srgb, var(--good) 55%, var(--line)); }\n.status.warn { border-color: color-mix(in srgb, var(--warn) 55%, var(--line)); }\n.status.bad  { border-color: color-mix(in srgb, var(--bad) 55%, var(--line)); }\n.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); flex: none; }\n.dot.good { background: var(--good); } .dot.warn { background: var(--warn); }\n.dot.bad { background: var(--bad); } .dot.info { background: var(--info); }\n\n.output { background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm);\n  padding: 13px 15px; }\n.log { font-family: var(--mono); font-size: 0.8rem; line-height: 1.7; background: var(--card);\n  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 11px 14px;\n  max-height: 220px; overflow-y: auto; white-space: pre-wrap; }\n\n\u002F* ---------- code block + syntax tokens ---------- *\u002F\n.code {\n  font-family: var(--mono); font-size: 0.82rem; line-height: 1.7;\n  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm);\n  padding: 13px 15px; overflow-x: auto; white-space: pre; tab-size: 2;\n}\n.code .tok-sel  { color: var(--accent); }\n.code .tok-prop { color: #86e29b; }\n.code .tok-kw   { color: #ff9bcb; }\n.code .tok-str  { color: #f0c674; }\n.code .tok-num  { color: #f0883e; }\n.code .tok-cmt  { color: var(--faint); font-style: italic; }\n.code .hl { background: color-mix(in srgb, var(--good) 22%, transparent); border-radius: 4px; padding: 0 3px; }\n\n\u002F* ---------- hint \u002F caption ---------- *\u002F\n.hint { font-size: 0.83rem; color: var(--muted); line-height: 1.55; margin: 10px 0 0; }\n.hint code { font-family: var(--mono); color: var(--ink); background: var(--line-soft);\n  padding: 1px 5px; border-radius: 5px; font-size: 0.86em; }\n.hint strong { color: var(--ink); }\n\n\u002F* ---------- banner (feature-support \u002F notices) ---------- *\u002F\n.banner {\n  display: none; margin-bottom: 14px; padding: 11px 14px; border-radius: 11px;\n  font-size: 0.85rem; line-height: 1.5;\n  color: var(--ink); background: color-mix(in srgb, var(--warn) 12%, var(--panel));\n  border: 1px solid color-mix(in srgb, var(--warn) 45%, var(--line));\n}\n.banner.show { display: block; }\n.banner.info { background: color-mix(in srgb, var(--info) 12%, var(--panel));\n  border-color: color-mix(in srgb, var(--info) 45%, var(--line)); }\n.banner code { font-family: var(--mono); background: var(--line-soft); padding: 1px 5px; border-radius: 5px; }\n\n\u002F* ---------- misc atoms ---------- *\u002F\n.pill { display: inline-flex; align-items: center; gap: 6px; font-size: 0.75rem; font-weight: 700;\n  padding: 3px 9px; border-radius: 999px; background: var(--line-soft); color: var(--muted); }\n.pill.good { color: var(--good); background: color-mix(in srgb, var(--good) 16%, transparent); }\n.pill.bad  { color: var(--bad);  background: color-mix(in srgb, var(--bad) 16%, transparent); }\n.mono { font-family: var(--mono); }\n.muted { color: var(--muted); }\n.big-num { font-family: var(--mono); font-size: 1.6rem; font-weight: 800; letter-spacing: -0.02em; }\n\n\u002F* Optional theme toggle button (top-right). Wire it in JS if you want it. *\u002F\n.theme-btn { position: fixed; top: 12px; right: 12px; z-index: 20; }\n\n\u002F* ---------- responsive ---------- *\u002F\n@media (max-width: 560px) {\n  .panel { padding: 14px; }\n  .wrap-controls { gap: 14px; }\n  .btn { padding: 9px 13px; }\n}\n\n\u002F* ---------- reduced motion ---------- *\u002F\n@media (prefers-reduced-motion: reduce) {\n  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }\n}\n\n\n\u002F* Demo-specific CSS ONLY. base.css already provides tokens, layout, panels,\n     buttons, toggles, code blocks, status\u002Flog, banners, presets, pills. *\u002F\n\n  .resources { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }\n  .resource {\n    display: flex; align-items: center; gap: 9px;\n    background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm);\n    padding: 9px 13px; flex: 1 1 170px;\n  }\n  .resource .icon { font-size: 1.15rem; line-height: 1; }\n  .resource .name { font-weight: 700; font-size: 0.84rem; flex: 1; }\n\n  .code-wrap { position: relative; }\n  .code-mode { display: none; margin: 0; }\n  .code-mode.active { display: block; }\n\n  .order-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n  .order-line .arrow { color: var(--faint); }","\"use strict\";\nconst $ = (id) => document.getElementById(id);\n\nconst META = {\n  lock: { label: \"Lock\" },\n  db:   { label: \"DB Connection\" },\n  file: { label: \"File Handle\" },\n};\nconst ACQUIRE_ORDER = [\"lock\", \"db\", \"file\"];\nconst STEP_DELAY_MS = 420;\n\nlet mode = \"using\";\nlet busy = false;\nlet timerId = null;\n\nfunction setPill(res, state) {\n  const el = $(\"pill-\" + res);\n  if (!el) return;\n  if (state === \"idle\") { el.className = \"pill\"; el.textContent = \"idle\"; }\n  else if (state === \"open\") { el.className = \"pill good\"; el.textContent = \"● open\"; }\n  else if (state === \"disposed\") { el.className = \"pill\"; el.textContent = \"✓ disposed\"; }\n  else if (state === \"leaked\") { el.className = \"pill bad\"; el.textContent = \"⚠ leaked\"; }\n}\n\nfunction resetResources() {\n  ACQUIRE_ORDER.forEach((res) => setPill(res, \"idle\"));\n}\n\nfunction clearLogView() {\n  $(\"log\").textContent = \"\";\n}\n\nfunction appendLog(line) {\n  const log = $(\"log\");\n  log.textContent += (log.textContent ? \"\\n\" : \"\") + line;\n  log.scrollTop = log.scrollHeight;\n}\n\nfunction setStatus(text, kind) {\n  $(\"statusText\").textContent = text;\n  $(\"dot\").className = \"dot \" + (kind || \"info\");\n}\n\nfunction setBusy(v) {\n  busy = v;\n  $(\"runHappy\").disabled = v;\n  $(\"runThrow\").disabled = v;\n  document.querySelectorAll(\"#modeSeg button\").forEach((b) => (b.disabled = v));\n  document.querySelectorAll(\"[data-preset]\").forEach((b) => (b.disabled = v));\n}\n\nfunction applyMode(next) {\n  mode = next;\n  document.querySelectorAll(\"#modeSeg button\").forEach((b) => {\n    b.setAttribute(\"aria-pressed\", b.dataset.mode === mode ? \"true\" : \"false\");\n  });\n  $(\"codeUsing\").classList.toggle(\"active\", mode === \"using\");\n  $(\"codeManual\").classList.toggle(\"active\", mode === \"manual\");\n}\n\n\u002F\u002F Builds the ordered list of log lines + pill transitions for one run,\n\u002F\u002F following the disposal semantics the Explicit Resource Management proposal\n\u002F\u002F defines: `using` disposes in reverse declaration order on every scope exit\n\u002F\u002F (normal, return, or throw); hand-written cleanup only runs the lines a\n\u002F\u002F human actually wrote, wherever they were written.\nfunction buildScenario(currentMode, willThrow) {\n  const events = [];\n  let n = 0;\n  const line = (text, pill, kind) => {\n    n++;\n    events.push({ text: n + \". \" + text, pill, kind });\n  };\n\n  ACQUIRE_ORDER.forEach((res) => {\n    line(\"open    \" + META[res].label, [res, \"open\"]);\n  });\n\n  line(\"…doWork() running\");\n  if (willThrow) {\n    line('💥 doWork() threw \"disk full\"', null, \"bad\");\n  } else {\n    line(\"doWork() returned normally\");\n  }\n\n  let leaked = [];\n  if (currentMode === \"using\") {\n    [\"file\", \"db\", \"lock\"].forEach((res) => {\n      line(\"dispose \" + META[res].label, [res, \"disposed\"]);\n    });\n    if (willThrow) line(\"⬆ error re-thrown to the caller, after cleanup ran\", null, \"warn\");\n  } else if (!willThrow) {\n    line(\"dispose \" + META.file.label, [\"file\", \"disposed\"]);\n    line(\"dispose \" + META.db.label, [\"db\", \"disposed\"]);\n    line(\"⚠ \" + META.lock.label + \" never disposed — no cleanup was ever written for it\", [\"lock\", \"leaked\"], \"bad\");\n    leaked = [\"lock\"];\n  } else {\n    line(\"⚠ \" + META.file.label + \" leaked — the throw skipped its dispose() call\", [\"file\", \"leaked\"], \"bad\");\n    line(\"⚠ \" + META.db.label + \" leaked — the throw skipped its dispose() call\", [\"db\", \"leaked\"], \"bad\");\n    line(\"⚠ \" + META.lock.label + \" never disposed — no cleanup was ever written for it\", [\"lock\", \"leaked\"], \"bad\");\n    line(\"⬆ error re-thrown to the caller\", null, \"warn\");\n    leaked = [\"file\", \"db\", \"lock\"];\n  }\n\n  return { events, leaked };\n}\n\nfunction playScenario(currentMode, willThrow) {\n  if (busy) return;\n  setBusy(true);\n  clearLogView();\n  resetResources();\n\n  const { events, leaked } = buildScenario(currentMode, willThrow);\n  setStatus(\"Running…\", \"info\");\n\n  let i = 0;\n  function step() {\n    if (i >= events.length) {\n      finish(currentMode, willThrow, leaked);\n      return;\n    }\n    const ev = events[i];\n    appendLog(ev.text);\n    if (ev.pill) setPill(ev.pill[0], ev.pill[1]);\n    i++;\n    timerId = window.setTimeout(step, STEP_DELAY_MS);\n  }\n  step();\n}\n\nfunction finish(currentMode, willThrow, leaked) {\n  setBusy(false);\n  const modeLabel = currentMode === \"using\" ? \"using\" : \"manual try\u002Ffinally\";\n  const pathLabel = willThrow ? \"the throwing path\" : \"the happy path\";\n  if (leaked.length === 0) {\n    setStatus(\"No leaks — \" + modeLabel + \" disposed everything on \" + pathLabel + \".\", \"good\");\n    $(\"hint\").textContent = willThrow\n      ? \"Even though doWork() threw, every using declaration still disposed — in reverse order — before the error kept propagating.\"\n      : \"Every resource opened is a resource closed. That's the guarantee using makes on every exit path.\";\n  } else {\n    const names = leaked.map((r) => META[r].label).join(\", \");\n    setStatus(leaked.length + \" resource(s) leaked on \" + pathLabel + \": \" + names, \"bad\");\n    $(\"hint\").textContent =\n      \"Manual cleanup only runs the lines a human remembered to write, in the order they happened to write them. Nothing enforces reverse order or a forgotten line.\";\n  }\n}\n\n$(\"modeSeg\").addEventListener(\"click\", (e) => {\n  if (busy) return;\n  const btn = e.target.closest(\"button[data-mode]\");\n  if (!btn) return;\n  applyMode(btn.dataset.mode);\n});\n\n$(\"runHappy\").addEventListener(\"click\", () => playScenario(mode, false));\n$(\"runThrow\").addEventListener(\"click\", () => playScenario(mode, true));\n\n$(\"resetBtn\").addEventListener(\"click\", () => {\n  if (timerId) window.clearTimeout(timerId);\n  setBusy(false);\n  clearLogView();\n  $(\"log\").textContent = \"Click a run button to start…\";\n  resetResources();\n  setStatus(\"Ready — pick a cleanup style, then run a path.\", \"info\");\n  $(\"hint\").textContent =\n    \"Every resource opens in the same order either way. Watch which ones a thrown error skips, and which one manual code simply never remembers to close.\";\n});\n\ndocument.querySelectorAll(\"[data-preset]\").forEach((btn) => {\n  btn.addEventListener(\"click\", () => {\n    if (busy) return;\n    const [presetMode, path] = btn.dataset.preset.split(\"-\");\n    applyMode(presetMode);\n    playScenario(presetMode, path === \"throw\");\n  });\n});\n\n\u002F\u002F initial state\napplyMode(\"using\");\nresetResources();",{"height":40,"autorun":17},520,{"slug":33,"title":42,"excerpt":43,"coverUrl":44},"You still write `finally { resource.close() }`. The `using` keyword does it automatically.","The Explicit Resource Management proposal adds a `using` declaration that calls cleanup the moment a scope exits — normal return, early return, or throw. No more try\u002Ffinally boilerplate for timers, event listeners, connections, or any other resource you need to release.","\u002Fmedia\u002Fcovers\u002Fusing-explicit-resource-management.png"]