@jaredwray/mockhttp 1.9.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -8
- package/dist/index.d.mts +90 -11
- package/dist/index.mjs +203 -240
- package/package.json +17 -13
- package/site/dist/api/index.html +17983 -0
- package/site/dist/api/swagger.json +5487 -0
- package/site/dist/changelog/index.html +784 -0
- package/site/dist/changelog/page/2/index.html +577 -0
- package/site/dist/changelog/v0-2-0/index.html +437 -0
- package/site/dist/changelog/v0-2-1/index.html +438 -0
- package/site/dist/changelog/v0-2-2/index.html +436 -0
- package/site/dist/changelog/v0-2-3/index.html +440 -0
- package/site/dist/changelog/v0-3-0/index.html +443 -0
- package/site/dist/changelog/v0-4-0/index.html +437 -0
- package/site/dist/changelog/v0-4-1/index.html +441 -0
- package/site/dist/changelog/v0-4-2/index.html +446 -0
- package/site/dist/changelog/v0-4-3/index.html +437 -0
- package/site/dist/changelog/v0-5-0/index.html +443 -0
- package/site/dist/changelog/v0-6-0/index.html +444 -0
- package/site/dist/changelog/v0-6-1/index.html +441 -0
- package/site/dist/changelog/v0-7-0/index.html +445 -0
- package/site/dist/changelog/v0-8-0/index.html +440 -0
- package/site/dist/changelog/v0-9-0/index.html +437 -0
- package/site/dist/changelog/v1-0-0/index.html +446 -0
- package/site/dist/changelog/v1-0-1/index.html +437 -0
- package/site/dist/changelog/v1-1-0/index.html +445 -0
- package/site/dist/changelog/v1-2-0/index.html +443 -0
- package/site/dist/changelog/v1-3-0/index.html +448 -0
- package/site/dist/changelog/v1-4-0/index.html +445 -0
- package/site/dist/changelog/v1-5-0/index.html +439 -0
- package/site/dist/changelog/v1-5-1/index.html +443 -0
- package/site/dist/changelog/v1-6-0/index.html +467 -0
- package/site/dist/changelog/v1-7-0/index.html +462 -0
- package/site/dist/changelog/v1-8-0/index.html +481 -0
- package/site/dist/changelog/v1-8-1/index.html +457 -0
- package/site/dist/changelog/v1-9-0/index.html +495 -0
- package/site/dist/changelog/v2-0-0/index.html +500 -0
- package/site/dist/changelog/v2-1-0/index.html +476 -0
- package/site/dist/changelog-latest.json +269 -0
- package/site/dist/changelog.json +399 -0
- package/site/dist/css/api.css +1113 -0
- package/site/dist/css/highlight/highlight.min.js +1433 -0
- package/site/dist/css/highlight/styles/base16/docula.css +123 -0
- package/site/dist/css/home.css +133 -0
- package/site/dist/css/styles.css +1227 -0
- package/site/dist/css/variables.css +32 -0
- package/site/dist/docs/bins/index.html +980 -0
- package/site/dist/docs/configuration/index.html +809 -0
- package/site/dist/docs/hosted/index.html +583 -0
- package/site/dist/docs/how-to-deploy/cloudflare/index.html +655 -0
- package/site/dist/docs/how-to-deploy/docker/index.html +664 -0
- package/site/dist/docs/how-to-deploy/wasmer/index.html +625 -0
- package/site/dist/docs/https/index.html +796 -0
- package/site/dist/docs/index.html +640 -0
- package/site/dist/docs/library/index.html +818 -0
- package/site/dist/docs/taps/index.html +746 -0
- package/site/dist/favicon.svg +14 -0
- package/site/dist/feed.xml +1 -0
- package/site/dist/index.html +640 -0
- package/site/dist/js/api.js +363 -0
- package/site/dist/js/search.js +436 -0
- package/site/dist/llms-full.txt +7640 -0
- package/site/dist/llms.txt +43 -0
- package/site/dist/logo.svg +21 -0
- package/site/dist/robots.txt +2 -0
- package/site/dist/search-index.json +1 -0
- package/site/dist/sitemap.xml +1 -0
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<meta name="description" content="Deploy MockHTTP as a Cloudflare Worker. Workers Assets serve the docs site, and the Worker handles mock API routes.">
|
|
8
|
+
<link rel="stylesheet" href="/css/variables.css">
|
|
9
|
+
<link rel="stylesheet" href="/css/styles.css">
|
|
10
|
+
<link rel="stylesheet" href="/css/highlight/styles/base16/docula.css">
|
|
11
|
+
<link rel="icon" href="/logo.svg">
|
|
12
|
+
<script>
|
|
13
|
+
(function(){
|
|
14
|
+
window.__doculaThemeKey = 'docula:theme:' + ('https://mockhttp.org').replace(/^https?:\/\//, '');
|
|
15
|
+
var mode = localStorage.getItem(window.__doculaThemeKey) || 'light';
|
|
16
|
+
var resolved = mode === 'system'
|
|
17
|
+
? (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark')
|
|
18
|
+
: mode;
|
|
19
|
+
if (resolved === 'light') document.documentElement.setAttribute('data-theme', 'light');
|
|
20
|
+
try {
|
|
21
|
+
var authState = JSON.parse(localStorage.getItem('docula-auth-state'));
|
|
22
|
+
if (authState) {
|
|
23
|
+
window.__doculaAuth = authState;
|
|
24
|
+
if (authState.loggedIn) {
|
|
25
|
+
document.documentElement.classList.add('docula-auth-logged-in');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {
|
|
29
|
+
// Ignore localStorage parsing errors
|
|
30
|
+
}
|
|
31
|
+
if (!window.__doculaAuth) {
|
|
32
|
+
window.__doculaAuth = { loggedIn: false, displayName: null };
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
</script>
|
|
36
|
+
<title>MockHTTP - Cloudflare</title>
|
|
37
|
+
<script type="application/ld+json">
|
|
38
|
+
{"@context":"https://schema.org","@type":"TechArticle","headline":"Cloudflare","description":"Deploy MockHTTP as a Cloudflare Worker. Workers Assets serve the docs site, and the Worker handles mock API routes.","url":"https://mockhttp.org/docs/how-to-deploy/cloudflare/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
|
|
39
|
+
</script>
|
|
40
|
+
</head>
|
|
41
|
+
|
|
42
|
+
<body>
|
|
43
|
+
<header class="header">
|
|
44
|
+
<div class="header-top">
|
|
45
|
+
<div class="header-top__content">
|
|
46
|
+
<button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle navigation menu">
|
|
47
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>
|
|
48
|
+
</button>
|
|
49
|
+
<a href="/" class="logo-link">
|
|
50
|
+
<img alt="MockHTTP" class="logo__img" src="/logo.svg">
|
|
51
|
+
</a>
|
|
52
|
+
<nav class="header-bottom__nav">
|
|
53
|
+
<a class="header-bottom__item" href="/docs/" id="nav-docs">
|
|
54
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
|
55
|
+
<span>Documentation</span>
|
|
56
|
+
</a>
|
|
57
|
+
<a class="header-bottom__item" href="/api" id="nav-api">
|
|
58
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>
|
|
59
|
+
<span>API Reference</span>
|
|
60
|
+
</a>
|
|
61
|
+
<a class="header-bottom__item" href="/changelog/" id="nav-changelog">
|
|
62
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"/><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"/><path d="M8 6v8"/></svg>
|
|
63
|
+
<span>Changelog</span>
|
|
64
|
+
</a>
|
|
65
|
+
</nav>
|
|
66
|
+
<div class="header-actions">
|
|
67
|
+
<button class="search-button" id="search-button" type="button" aria-label="Search" aria-keyshortcuts="Meta+K Control+K">
|
|
68
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
69
|
+
<span class="search-button__text">Search</span>
|
|
70
|
+
<span class="search-button__shortcut">⌘K</span>
|
|
71
|
+
</button>
|
|
72
|
+
<button class="theme-button" id="theme-toggle" aria-label="Theme: system (following system)">
|
|
73
|
+
<svg class="theme-button__icon theme-button__icon--system" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/></svg>
|
|
74
|
+
<svg class="theme-button__icon theme-button__icon--sun theme-button__icon--hidden" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
|
|
75
|
+
<svg class="theme-button__icon theme-button__icon--moon theme-button__icon--hidden" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</header>
|
|
81
|
+
<aside class="mobile-sidebar" id="mobile-sidebar">
|
|
82
|
+
<nav class="mobile-nav">
|
|
83
|
+
<a class="mobile-nav__item" href="/docs/">
|
|
84
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
|
85
|
+
<span>Documentation</span>
|
|
86
|
+
</a>
|
|
87
|
+
<a class="mobile-nav__item" href="/api">
|
|
88
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>
|
|
89
|
+
<span>API Reference</span>
|
|
90
|
+
</a>
|
|
91
|
+
<a class="mobile-nav__item" href="/changelog/">
|
|
92
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"/><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"/><path d="M8 6v8"/></svg>
|
|
93
|
+
<span>Changelog</span>
|
|
94
|
+
</a>
|
|
95
|
+
</nav>
|
|
96
|
+
<div class="mobile-sidebar__docs" id="mobile-sidebar-docs"></div>
|
|
97
|
+
</aside>
|
|
98
|
+
<div class="sidebar-backdrop" id="sidebar-backdrop"></div>
|
|
99
|
+
<div class="search-modal" id="search-modal" hidden>
|
|
100
|
+
<div class="search-modal__backdrop" data-search-close></div>
|
|
101
|
+
<div class="search-modal__panel" role="dialog" aria-modal="true" aria-label="Search">
|
|
102
|
+
<div class="search-modal__header">
|
|
103
|
+
<svg class="search-modal__icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
104
|
+
<input
|
|
105
|
+
type="search"
|
|
106
|
+
id="search-input"
|
|
107
|
+
class="search-modal__input"
|
|
108
|
+
placeholder="Search"
|
|
109
|
+
autocomplete="off"
|
|
110
|
+
autocorrect="off"
|
|
111
|
+
autocapitalize="off"
|
|
112
|
+
spellcheck="false"
|
|
113
|
+
aria-label="Search"
|
|
114
|
+
aria-controls="search-results"
|
|
115
|
+
aria-activedescendant=""
|
|
116
|
+
role="combobox"
|
|
117
|
+
aria-expanded="true"
|
|
118
|
+
/>
|
|
119
|
+
<button class="search-modal__clear" id="search-clear" type="button" aria-label="Clear search" hidden>
|
|
120
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="search-modal__body">
|
|
124
|
+
<ul class="search-results" id="search-results" role="listbox" aria-label="Search results"></ul>
|
|
125
|
+
<div class="search-modal__empty" id="search-empty" hidden>
|
|
126
|
+
No results for "<span class="search-modal__empty-query" id="search-empty-query"></span>"
|
|
127
|
+
</div>
|
|
128
|
+
<div class="search-modal__initial" id="search-initial">
|
|
129
|
+
Type to search the documentation.
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="search-modal__footer">
|
|
133
|
+
<span class="search-modal__hint"><kbd class="search-modal__kbd">↑</kbd><kbd class="search-modal__kbd">↓</kbd> to navigate</span>
|
|
134
|
+
<span class="search-modal__hint"><kbd class="search-modal__kbd">↵</kbd> to select</span>
|
|
135
|
+
<span class="search-modal__hint"><kbd class="search-modal__kbd">esc</kbd> to close</span>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<script>
|
|
140
|
+
window.__doculaSearch = { indexUrl: "/search-index.json" };
|
|
141
|
+
</script>
|
|
142
|
+
<script src="/js/search.js" defer></script>
|
|
143
|
+
<div class="page-dropdown" id="page-dropdown">
|
|
144
|
+
<button class="page-dropdown__toggle" id="page-dropdown-toggle">
|
|
145
|
+
<span class="page-dropdown__title">Cloudflare</span>
|
|
146
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
|
|
147
|
+
</button>
|
|
148
|
+
<div class="page-dropdown__list" id="page-dropdown-list">
|
|
149
|
+
<section class="nav-sidebar__section">
|
|
150
|
+
<ul class="nav-sidebar__list">
|
|
151
|
+
<li>
|
|
152
|
+
<a href="/docs/" class="nav-sidebar__item">Getting Started</a>
|
|
153
|
+
</li>
|
|
154
|
+
</ul>
|
|
155
|
+
</section>
|
|
156
|
+
<section class="nav-sidebar__section">
|
|
157
|
+
<ul class="nav-sidebar__list">
|
|
158
|
+
<li>
|
|
159
|
+
<a href="/docs/https/" class="nav-sidebar__item">HTTPS and HTTP/2</a>
|
|
160
|
+
</li>
|
|
161
|
+
</ul>
|
|
162
|
+
</section>
|
|
163
|
+
<section class="nav-sidebar__section">
|
|
164
|
+
<ul class="nav-sidebar__list">
|
|
165
|
+
<li>
|
|
166
|
+
<a href="/docs/taps/" class="nav-sidebar__item">Taps</a>
|
|
167
|
+
</li>
|
|
168
|
+
</ul>
|
|
169
|
+
</section>
|
|
170
|
+
<section class="nav-sidebar__section">
|
|
171
|
+
<ul class="nav-sidebar__list">
|
|
172
|
+
<li>
|
|
173
|
+
<a href="/docs/bins/" class="nav-sidebar__item">Bins</a>
|
|
174
|
+
</li>
|
|
175
|
+
</ul>
|
|
176
|
+
</section>
|
|
177
|
+
<section class="nav-sidebar__section">
|
|
178
|
+
<ul class="nav-sidebar__list">
|
|
179
|
+
<li>
|
|
180
|
+
<a href="/docs/configuration/" class="nav-sidebar__item">Configuration</a>
|
|
181
|
+
</li>
|
|
182
|
+
</ul>
|
|
183
|
+
</section>
|
|
184
|
+
<section class="nav-sidebar__section">
|
|
185
|
+
<ul class="nav-sidebar__list">
|
|
186
|
+
<li>
|
|
187
|
+
<a href="/docs/library/" class="nav-sidebar__item">Library API</a>
|
|
188
|
+
</li>
|
|
189
|
+
</ul>
|
|
190
|
+
</section>
|
|
191
|
+
<section class="nav-sidebar__section">
|
|
192
|
+
<ul class="nav-sidebar__list">
|
|
193
|
+
<li>
|
|
194
|
+
<a href="/docs/hosted/" class="nav-sidebar__item">MockHttp.org Live</a>
|
|
195
|
+
</li>
|
|
196
|
+
</ul>
|
|
197
|
+
</section>
|
|
198
|
+
<section class="nav-sidebar__section nav-sidebar__section--collapsible">
|
|
199
|
+
<h2 class="nav-sidebar__title">
|
|
200
|
+
<button type="button" class="nav-sidebar__toggle" aria-expanded="true">
|
|
201
|
+
<span class="nav-sidebar__toggle-label">How to Deploy</span>
|
|
202
|
+
<svg class="nav-sidebar__chevron" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
|
|
203
|
+
</button>
|
|
204
|
+
</h2>
|
|
205
|
+
<ul class="nav-sidebar__list">
|
|
206
|
+
<li>
|
|
207
|
+
<a href="/docs/how-to-deploy/cloudflare/" class="nav-sidebar__item">Cloudflare</a>
|
|
208
|
+
</li>
|
|
209
|
+
<li>
|
|
210
|
+
<a href="/docs/how-to-deploy/docker/" class="nav-sidebar__item">Docker</a>
|
|
211
|
+
</li>
|
|
212
|
+
<li>
|
|
213
|
+
<a href="/docs/how-to-deploy/wasmer/" class="nav-sidebar__item">Wasmer</a>
|
|
214
|
+
</li>
|
|
215
|
+
</ul>
|
|
216
|
+
</section>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="content">
|
|
220
|
+
<aside class="nav-sidebar" id="nav-sidebar">
|
|
221
|
+
<section class="nav-sidebar__section">
|
|
222
|
+
<ul class="nav-sidebar__list">
|
|
223
|
+
<li>
|
|
224
|
+
<a href="/docs/" class="nav-sidebar__item">Getting Started</a>
|
|
225
|
+
</li>
|
|
226
|
+
</ul>
|
|
227
|
+
</section>
|
|
228
|
+
<section class="nav-sidebar__section">
|
|
229
|
+
<ul class="nav-sidebar__list">
|
|
230
|
+
<li>
|
|
231
|
+
<a href="/docs/https/" class="nav-sidebar__item">HTTPS and HTTP/2</a>
|
|
232
|
+
</li>
|
|
233
|
+
</ul>
|
|
234
|
+
</section>
|
|
235
|
+
<section class="nav-sidebar__section">
|
|
236
|
+
<ul class="nav-sidebar__list">
|
|
237
|
+
<li>
|
|
238
|
+
<a href="/docs/taps/" class="nav-sidebar__item">Taps</a>
|
|
239
|
+
</li>
|
|
240
|
+
</ul>
|
|
241
|
+
</section>
|
|
242
|
+
<section class="nav-sidebar__section">
|
|
243
|
+
<ul class="nav-sidebar__list">
|
|
244
|
+
<li>
|
|
245
|
+
<a href="/docs/bins/" class="nav-sidebar__item">Bins</a>
|
|
246
|
+
</li>
|
|
247
|
+
</ul>
|
|
248
|
+
</section>
|
|
249
|
+
<section class="nav-sidebar__section">
|
|
250
|
+
<ul class="nav-sidebar__list">
|
|
251
|
+
<li>
|
|
252
|
+
<a href="/docs/configuration/" class="nav-sidebar__item">Configuration</a>
|
|
253
|
+
</li>
|
|
254
|
+
</ul>
|
|
255
|
+
</section>
|
|
256
|
+
<section class="nav-sidebar__section">
|
|
257
|
+
<ul class="nav-sidebar__list">
|
|
258
|
+
<li>
|
|
259
|
+
<a href="/docs/library/" class="nav-sidebar__item">Library API</a>
|
|
260
|
+
</li>
|
|
261
|
+
</ul>
|
|
262
|
+
</section>
|
|
263
|
+
<section class="nav-sidebar__section">
|
|
264
|
+
<ul class="nav-sidebar__list">
|
|
265
|
+
<li>
|
|
266
|
+
<a href="/docs/hosted/" class="nav-sidebar__item">MockHttp.org Live</a>
|
|
267
|
+
</li>
|
|
268
|
+
</ul>
|
|
269
|
+
</section>
|
|
270
|
+
<section class="nav-sidebar__section nav-sidebar__section--collapsible">
|
|
271
|
+
<h2 class="nav-sidebar__title">
|
|
272
|
+
<button type="button" class="nav-sidebar__toggle" aria-expanded="true">
|
|
273
|
+
<span class="nav-sidebar__toggle-label">How to Deploy</span>
|
|
274
|
+
<svg class="nav-sidebar__chevron" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
|
|
275
|
+
</button>
|
|
276
|
+
</h2>
|
|
277
|
+
<ul class="nav-sidebar__list">
|
|
278
|
+
<li>
|
|
279
|
+
<a href="/docs/how-to-deploy/cloudflare/" class="nav-sidebar__item">Cloudflare</a>
|
|
280
|
+
</li>
|
|
281
|
+
<li>
|
|
282
|
+
<a href="/docs/how-to-deploy/docker/" class="nav-sidebar__item">Docker</a>
|
|
283
|
+
</li>
|
|
284
|
+
<li>
|
|
285
|
+
<a href="/docs/how-to-deploy/wasmer/" class="nav-sidebar__item">Wasmer</a>
|
|
286
|
+
</li>
|
|
287
|
+
</ul>
|
|
288
|
+
</section>
|
|
289
|
+
</aside>
|
|
290
|
+
<article class="article">
|
|
291
|
+
<header class="article__header">
|
|
292
|
+
<h1 class="article__title">Cloudflare</h1>
|
|
293
|
+
<p class="article__description">Deploy MockHTTP as a Cloudflare Worker. Workers Assets serve the docs site, and the Worker handles mock API routes.</p>
|
|
294
|
+
</header>
|
|
295
|
+
<div class="article__body">
|
|
296
|
+
<main class="article__main">
|
|
297
|
+
<p>MockHTTP runs on Cloudflare as a Worker, not a container. <a href="https://mockhttp.org">mockhttp.org</a> is this deployment: <a href="https://developers.cloudflare.com/workers/static-assets/">Workers Assets</a> serve the documentation site, and the Worker handles mock APIs such as <code>/get</code> and <code>/post</code>.</p>
|
|
298
|
+
<p>The Worker entry is <a href="https://github.com/jaredwray/mockhttp/blob/main/worker/index.ts"><code>worker/index.ts</code></a>. It listens with the Node.js compatibility layer and forwards each request into the Fastify app. Configuration lives in <a href="https://github.com/jaredwray/mockhttp/blob/main/wrangler.jsonc"><code>wrangler.jsonc</code></a>.</p>
|
|
299
|
+
<h2 id="table-of-contents">Table of Contents</h2>
|
|
300
|
+
<ul>
|
|
301
|
+
<li><a href="#what-the-worker-does-differently">What the Worker does differently</a></li>
|
|
302
|
+
<li><a href="#deploy-your-own-worker">Deploy your own Worker</a></li>
|
|
303
|
+
<li><a href="#deploy-from-github-actions">Deploy from GitHub Actions</a></li>
|
|
304
|
+
</ul>
|
|
305
|
+
<h2 id="what-the-worker-does-differently">What the Worker does differently</h2>
|
|
306
|
+
<p>The Worker starts the same MockHTTP app as Node.js, with a few options set for the Workers runtime:</p>
|
|
307
|
+
<table>
|
|
308
|
+
<thead>
|
|
309
|
+
<tr>
|
|
310
|
+
<th>Option</th>
|
|
311
|
+
<th>Worker value</th>
|
|
312
|
+
<th>Why</th>
|
|
313
|
+
</tr>
|
|
314
|
+
</thead>
|
|
315
|
+
<tbody>
|
|
316
|
+
<tr>
|
|
317
|
+
<td><code>rateLimit</code></td>
|
|
318
|
+
<td><code>false</code></td>
|
|
319
|
+
<td><a href="https://github.com/fastify/fastify-rate-limit"><code>@fastify/rate-limit</code></a> is off. A Cloudflare rate limiting binding allows 1000 requests per 60 seconds per IP.</td>
|
|
320
|
+
</tr>
|
|
321
|
+
<tr>
|
|
322
|
+
<td><code>siteDistPath</code></td>
|
|
323
|
+
<td>unused path</td>
|
|
324
|
+
<td>Fastify does not serve the docs. Workers Assets serve <code>site/dist</code>.</td>
|
|
325
|
+
</tr>
|
|
326
|
+
<tr>
|
|
327
|
+
<td><code>staticFiles</code></td>
|
|
328
|
+
<td><code>false</code></td>
|
|
329
|
+
<td>Logos and other <code>public/</code> files are copied into <code>site/dist</code> and read through the <code>ASSETS</code> binding.</td>
|
|
330
|
+
</tr>
|
|
331
|
+
<tr>
|
|
332
|
+
<td><code>startBins</code></td>
|
|
333
|
+
<td><code>false</code></td>
|
|
334
|
+
<td>The bin cleanup timer uses <code>setInterval</code>, which can hang a Worker isolate during startup. Bin routes still register. Expired bins are removed when that bin is read, not by a sweep of every bin.</td>
|
|
335
|
+
</tr>
|
|
336
|
+
<tr>
|
|
337
|
+
<td><code>pluginTimeout</code></td>
|
|
338
|
+
<td><code>0</code></td>
|
|
339
|
+
<td>Disables Fastify's plugin boot timeout. Those timers are unreliable on Workers.</td>
|
|
340
|
+
</tr>
|
|
341
|
+
<tr>
|
|
342
|
+
<td><code>logging</code></td>
|
|
343
|
+
<td><code>false</code></td>
|
|
344
|
+
<td>Request logging is off.</td>
|
|
345
|
+
</tr>
|
|
346
|
+
<tr>
|
|
347
|
+
<td><code>autoDetectPort</code></td>
|
|
348
|
+
<td><code>false</code></td>
|
|
349
|
+
<td>The app always listens on port 3000 inside the isolate.</td>
|
|
350
|
+
</tr>
|
|
351
|
+
</tbody>
|
|
352
|
+
</table>
|
|
353
|
+
<p>The rate limiter key is the <code>cf-connecting-ip</code> header, then the first address in <code>x-forwarded-for</code>, then <code>unknown</code>. A limited request returns <code>429</code> with <code>retry-after: 60</code>. That limiter is shared across isolates. Bins are not. The default store is in memory inside the isolate that handled the request, so a bin created on one request can be missing when another isolate or location handles the next one.</p>
|
|
354
|
+
<p>Taps are a library API (<code>mock.taps.inject</code>). They have no HTTP routes, so a deployed Worker has no way to inject one.</p>
|
|
355
|
+
<p>Assets are checked before the Worker. A file under <code>site/dist</code> (the docs site, favicon, logos) is served as a static asset. A path that is not a file, such as <code>/get</code> or <code>/post</code>, is handled by the Worker.</p>
|
|
356
|
+
<h2 id="deploy-your-own-worker">Deploy your own Worker</h2>
|
|
357
|
+
<p>You need a Cloudflare account and permission to deploy Workers. This repository pins Wrangler to <code>4.127.0</code>.</p>
|
|
358
|
+
<p>Change <code>name</code> and <code>routes</code> in <code>wrangler.jsonc</code> before you deploy a fork. The route in this repo attaches the custom domain <code>mockhttp.org</code>. <code>workers_dev</code> is enabled, so a <code>*.workers.dev</code> hostname is available as well. Adjust the <code>RATE_LIMITER</code> binding if you want a different limit than 1000 requests per 60 seconds.</p>
|
|
359
|
+
<p>Also change <code>siteUrl</code> in <code>site/docula.config.ts</code>. The sitemap is a static file, and this repo sets it to <code>https://mockhttp.org</code>. The <code>migrations</code> block records a container class that this Worker no longer uses. A brand-new Worker can omit that block.</p>
|
|
360
|
+
<p>Wrangler bundles <code>worker/index.ts</code> from source, so you do not need <code>pnpm build</code> for this deploy. <code>pnpm website:build</code> produces <code>site/dist</code>.</p>
|
|
361
|
+
<pre><code class="hljs language-bash">pnpm install
|
|
362
|
+
pnpm website:build
|
|
363
|
+
pnpm prepare:worker-assets
|
|
364
|
+
npx wrangler@4.127.0 deploy
|
|
365
|
+
</code></pre>
|
|
366
|
+
<p><code>pnpm prepare:worker-assets</code> copies <code>public/</code> into <code>site/dist</code> so favicon and logo files ship with the docs site. Wrangler reads <code>CLOUDFLARE_API_TOKEN</code> and <code>CLOUDFLARE_ACCOUNT_ID</code> from the environment when you are not logged in locally.</p>
|
|
367
|
+
<p>After deploy, open the workers.dev hostname or your custom domain and request <code>/get</code>. The docs are at <code>/docs</code>, and the OpenAPI reference is at <code>/api</code>.</p>
|
|
368
|
+
<h2 id="deploy-from-github-actions">Deploy from GitHub Actions</h2>
|
|
369
|
+
<p><a href="https://github.com/jaredwray/mockhttp/blob/main/.github/workflows/deploy-site.yaml"><code>.github/workflows/deploy-site.yaml</code></a> deploys this Worker on the <code>released</code> event (a published release, not a prerelease) and when the workflow is started by hand. The <code>production</code> environment provides <code>CLOUDFLARE_API_TOKEN</code> and <code>CLOUDFLARE_ACCOUNT_ID</code>. The deploy job installs production dependencies, downloads the built site, copies <code>public/</code> into <code>site/dist</code>, installs Wrangler <code>4.127.0</code>, and runs <code>wrangler deploy</code>.</p>
|
|
370
|
+
<p>That workflow does not deploy the <a href="/docs/how-to-deploy/wasmer/">Wasmer</a> app. Docker image publishing is a separate workflow, documented in <a href="/docs/how-to-deploy/docker/">Docker</a>.</p>
|
|
371
|
+
</main>
|
|
372
|
+
<aside class="content-aside">
|
|
373
|
+
<h2 class="content-aside__title">TABLE OF CONTENTS</h2>
|
|
374
|
+
<ul class="content-aside__list" id="toc-list">
|
|
375
|
+
</ul>
|
|
376
|
+
</aside>
|
|
377
|
+
</div>
|
|
378
|
+
<a class="article__edit-link" href="https://github.com/jaredwray/mockhttp/edit/main/site/docs/how-to-deploy/cloudflare.md" target="_blank" rel="noopener noreferrer">
|
|
379
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
|
380
|
+
Edit this page
|
|
381
|
+
</a>
|
|
382
|
+
</article>
|
|
383
|
+
</div>
|
|
384
|
+
<footer>
|
|
385
|
+
<a href="/llms.txt" class="footer__link">llms.txt</a>
|
|
386
|
+
<a href="/feed.xml" class="footer__link" aria-label="RSS Feed">
|
|
387
|
+
<svg class="footer__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><circle cx="6.18" cy="17.82" r="2.18"/><path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/></svg>
|
|
388
|
+
</a>
|
|
389
|
+
|
|
390
|
+
<a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
|
|
391
|
+
<svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
|
|
392
|
+
</a>
|
|
393
|
+
<span class="footer__updated">updated: 2026-10-03</span>
|
|
394
|
+
</footer>
|
|
395
|
+
<script src="/css/highlight/highlight.min.js"></script>
|
|
396
|
+
<script>
|
|
397
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
398
|
+
document.querySelectorAll('pre code').forEach(el => { el.textContent = el.textContent.trimEnd(); });
|
|
399
|
+
hljs.highlightAll();
|
|
400
|
+
var kbd = document.querySelector('.search-button__shortcut');
|
|
401
|
+
if (kbd) kbd.textContent = navigator.platform.indexOf('Mac') > -1 ? '⌘K' : 'Ctrl K';
|
|
402
|
+
|
|
403
|
+
// Copy code buttons
|
|
404
|
+
const copyIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
|
|
405
|
+
const checkIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';
|
|
406
|
+
document.querySelectorAll('pre').forEach(function(pre) {
|
|
407
|
+
const btn = document.createElement('button');
|
|
408
|
+
btn.className = 'copy-code-btn';
|
|
409
|
+
btn.innerHTML = copyIcon;
|
|
410
|
+
btn.setAttribute('aria-label', 'Copy code');
|
|
411
|
+
btn.addEventListener('click', function() {
|
|
412
|
+
const code = pre.querySelector('code');
|
|
413
|
+
const text = code ? code.textContent : pre.textContent;
|
|
414
|
+
navigator.clipboard.writeText(text || '').then(function() {
|
|
415
|
+
btn.innerHTML = checkIcon;
|
|
416
|
+
setTimeout(function() { btn.innerHTML = copyIcon; }, 2000);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
pre.appendChild(btn);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
</script>
|
|
423
|
+
<script>
|
|
424
|
+
(function() {
|
|
425
|
+
var themeKey = window.__doculaThemeKey;
|
|
426
|
+
function getMode() {
|
|
427
|
+
return localStorage.getItem(themeKey) || 'light';
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function resolveTheme(mode) {
|
|
431
|
+
if (mode === 'system') {
|
|
432
|
+
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
|
433
|
+
}
|
|
434
|
+
return mode;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
var cachedEls = null;
|
|
438
|
+
function getThemeEls() {
|
|
439
|
+
if (!cachedEls) {
|
|
440
|
+
cachedEls = {
|
|
441
|
+
systemIcon: document.querySelector('.theme-button__icon--system'),
|
|
442
|
+
sunIcon: document.querySelector('.theme-button__icon--sun'),
|
|
443
|
+
moonIcon: document.querySelector('.theme-button__icon--moon'),
|
|
444
|
+
toggle: document.getElementById('theme-toggle')
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
return cachedEls;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function applyTheme(mode) {
|
|
451
|
+
var resolved = resolveTheme(mode);
|
|
452
|
+
if (resolved === 'light') {
|
|
453
|
+
document.documentElement.setAttribute('data-theme', 'light');
|
|
454
|
+
} else {
|
|
455
|
+
document.documentElement.removeAttribute('data-theme');
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
var els = getThemeEls();
|
|
459
|
+
if (els.systemIcon && els.sunIcon && els.moonIcon) {
|
|
460
|
+
els.systemIcon.classList.add('theme-button__icon--hidden');
|
|
461
|
+
els.sunIcon.classList.add('theme-button__icon--hidden');
|
|
462
|
+
els.moonIcon.classList.add('theme-button__icon--hidden');
|
|
463
|
+
if (mode === 'system') {
|
|
464
|
+
els.systemIcon.classList.remove('theme-button__icon--hidden');
|
|
465
|
+
} else if (mode === 'light') {
|
|
466
|
+
els.sunIcon.classList.remove('theme-button__icon--hidden');
|
|
467
|
+
} else {
|
|
468
|
+
els.moonIcon.classList.remove('theme-button__icon--hidden');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (els.toggle) {
|
|
473
|
+
var labels = { system: 'Theme: system (following system)', light: 'Theme: light', dark: 'Theme: dark' };
|
|
474
|
+
els.toggle.setAttribute('aria-label', labels[mode] || 'Toggle theme');
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function nextMode(current) {
|
|
479
|
+
var cycle = { system: 'light', light: 'dark', dark: 'system' };
|
|
480
|
+
return cycle[current] || 'system';
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
var currentMode = getMode();
|
|
484
|
+
|
|
485
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
486
|
+
applyTheme(currentMode);
|
|
487
|
+
|
|
488
|
+
var els = getThemeEls();
|
|
489
|
+
if (els.toggle) {
|
|
490
|
+
els.toggle.addEventListener('click', function() {
|
|
491
|
+
currentMode = nextMode(currentMode);
|
|
492
|
+
localStorage.setItem(themeKey, currentMode);
|
|
493
|
+
applyTheme(currentMode);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', function() {
|
|
499
|
+
if (getMode() === 'system') {
|
|
500
|
+
applyTheme('system');
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
})();
|
|
504
|
+
</script>
|
|
505
|
+
<script defer>
|
|
506
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
507
|
+
const menuToggle = document.getElementById('mobile-menu-toggle');
|
|
508
|
+
const mobileSidebar = document.getElementById('mobile-sidebar');
|
|
509
|
+
const backdrop = document.getElementById('sidebar-backdrop');
|
|
510
|
+
if (menuToggle && mobileSidebar) {
|
|
511
|
+
menuToggle.addEventListener('click', () => {
|
|
512
|
+
mobileSidebar.classList.toggle('mobile-sidebar--open');
|
|
513
|
+
if (backdrop) backdrop.classList.toggle('sidebar-backdrop--visible');
|
|
514
|
+
});
|
|
515
|
+
if (backdrop) {
|
|
516
|
+
backdrop.addEventListener('click', () => {
|
|
517
|
+
mobileSidebar.classList.remove('mobile-sidebar--open');
|
|
518
|
+
backdrop.classList.remove('sidebar-backdrop--visible');
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Page dropdown toggle (docs pages)
|
|
524
|
+
const dropdownToggle = document.getElementById('page-dropdown-toggle');
|
|
525
|
+
const dropdownList = document.getElementById('page-dropdown-list');
|
|
526
|
+
if (dropdownToggle && dropdownList) {
|
|
527
|
+
dropdownToggle.addEventListener('click', () => {
|
|
528
|
+
dropdownList.classList.toggle('page-dropdown__list--open');
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Active sidebar link highlighting
|
|
533
|
+
const sidebarLinks = document.querySelectorAll('.nav-sidebar__item');
|
|
534
|
+
const currentPath = window.location.pathname;
|
|
535
|
+
sidebarLinks.forEach((link) => {
|
|
536
|
+
if (link.getAttribute('href') === currentPath || link.getAttribute('href') === currentPath.replace(/\/$/, '')) {
|
|
537
|
+
link.classList.add('nav-sidebar__item--active');
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
// Sidebar section collapse/expand
|
|
542
|
+
const SIDEBAR_STORAGE_KEY = 'docula:sidebar-sections';
|
|
543
|
+
const collapsibleSections = document.querySelectorAll('.nav-sidebar__section--collapsible');
|
|
544
|
+
if (collapsibleSections.length > 0) {
|
|
545
|
+
let storedSectionState = {};
|
|
546
|
+
try {
|
|
547
|
+
storedSectionState = JSON.parse(localStorage.getItem(SIDEBAR_STORAGE_KEY) || '{}');
|
|
548
|
+
} catch (e) { storedSectionState = {}; }
|
|
549
|
+
|
|
550
|
+
const setSectionOpen = (section, toggle, open) => {
|
|
551
|
+
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
552
|
+
section.classList.toggle('nav-sidebar__section--collapsed', !open);
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
collapsibleSections.forEach((section, idx) => {
|
|
556
|
+
const toggle = section.querySelector('.nav-sidebar__toggle');
|
|
557
|
+
const list = section.querySelector('.nav-sidebar__list');
|
|
558
|
+
if (!toggle || !list) return;
|
|
559
|
+
const listId = 'nav-sidebar-section-' + idx;
|
|
560
|
+
list.id = listId;
|
|
561
|
+
toggle.setAttribute('aria-controls', listId);
|
|
562
|
+
|
|
563
|
+
const key = 'section-' + idx;
|
|
564
|
+
const hasStored = Object.prototype.hasOwnProperty.call(storedSectionState, key);
|
|
565
|
+
const isOpen = hasStored ? !!storedSectionState[key] : true;
|
|
566
|
+
setSectionOpen(section, toggle, isOpen);
|
|
567
|
+
|
|
568
|
+
toggle.addEventListener('click', () => {
|
|
569
|
+
const next = toggle.getAttribute('aria-expanded') !== 'true';
|
|
570
|
+
setSectionOpen(section, toggle, next);
|
|
571
|
+
storedSectionState[key] = next;
|
|
572
|
+
try {
|
|
573
|
+
localStorage.setItem(SIDEBAR_STORAGE_KEY, JSON.stringify(storedSectionState));
|
|
574
|
+
} catch (e) { /* storage unavailable */ }
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Active nav link highlighting (header + mobile). Highlight only the
|
|
580
|
+
// single most-specific link: the one whose path is the longest prefix of
|
|
581
|
+
// the current path. A plain startsWith() check lights up every link whose
|
|
582
|
+
// path merely prefixes the current one, so when the site is embedded under
|
|
583
|
+
// a base path (e.g. served at /developer with docs at that root) the
|
|
584
|
+
// Documentation link — whose path is a prefix of every page — would stay
|
|
585
|
+
// highlighted everywhere. Trailing slashes are normalized so matches stop
|
|
586
|
+
// at a path boundary. We read the anchor's resolved origin/pathname so
|
|
587
|
+
// absolute, relative, and same-origin hrefs all work, while cross-origin
|
|
588
|
+
// links and non-anchor items (e.g. the mobile logout button) are skipped.
|
|
589
|
+
const withTrailingSlash = (p) => (p.endsWith('/') ? p : p + '/');
|
|
590
|
+
const here = withTrailingSlash(currentPath);
|
|
591
|
+
const markActiveNav = (selector, activeClass) => {
|
|
592
|
+
let bestLink = null;
|
|
593
|
+
let bestLength = -1;
|
|
594
|
+
document.querySelectorAll(selector).forEach((link) => {
|
|
595
|
+
// Only same-origin anchors can match the current pathname.
|
|
596
|
+
if (!link.href || link.origin !== window.location.origin) return;
|
|
597
|
+
const base = withTrailingSlash(link.pathname);
|
|
598
|
+
if (here.startsWith(base) && base.length > bestLength) {
|
|
599
|
+
bestLink = link;
|
|
600
|
+
bestLength = base.length;
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
if (bestLink) bestLink.classList.add(activeClass);
|
|
604
|
+
};
|
|
605
|
+
markActiveNav('.header-bottom__item', 'header-bottom__item--active');
|
|
606
|
+
markActiveNav('.mobile-nav__item', 'mobile-nav__item--active');
|
|
607
|
+
|
|
608
|
+
// TOC extraction: move the inline TOC into the right sidebar and hide the original
|
|
609
|
+
const tocHeading = document.getElementById('table-of-contents');
|
|
610
|
+
if (tocHeading) {
|
|
611
|
+
const tocList = tocHeading.nextElementSibling;
|
|
612
|
+
const tocTarget = document.getElementById('toc-list');
|
|
613
|
+
if (tocList && tocList.tagName === 'UL' && tocTarget) {
|
|
614
|
+
tocTarget.innerHTML = tocList.innerHTML;
|
|
615
|
+
tocHeading.style.display = 'none';
|
|
616
|
+
tocList.style.display = 'none';
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Hide the TOC aside if it has no entries
|
|
621
|
+
const tocSidebar = document.getElementById('toc-list');
|
|
622
|
+
if (tocSidebar && tocSidebar.children.length === 0) {
|
|
623
|
+
const aside = tocSidebar.closest('.content-aside');
|
|
624
|
+
if (aside) { aside.style.display = 'none'; }
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Image lightbox
|
|
628
|
+
const lightboxOverlay = document.createElement('div');
|
|
629
|
+
lightboxOverlay.className = 'lightbox-overlay';
|
|
630
|
+
lightboxOverlay.addEventListener('click', function(e) {
|
|
631
|
+
if (e.target !== lightboxImg) lightboxOverlay.classList.remove('lightbox-overlay--visible');
|
|
632
|
+
});
|
|
633
|
+
const lightboxClose = document.createElement('button');
|
|
634
|
+
lightboxClose.className = 'lightbox-close';
|
|
635
|
+
lightboxClose.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
|
|
636
|
+
lightboxClose.addEventListener('click', function() {
|
|
637
|
+
lightboxOverlay.classList.remove('lightbox-overlay--visible');
|
|
638
|
+
});
|
|
639
|
+
const lightboxImg = document.createElement('img');
|
|
640
|
+
lightboxOverlay.appendChild(lightboxClose);
|
|
641
|
+
lightboxOverlay.appendChild(lightboxImg);
|
|
642
|
+
document.body.appendChild(lightboxOverlay);
|
|
643
|
+
|
|
644
|
+
document.querySelectorAll('.article__main img, .changelog-entry-body img').forEach(function(img) {
|
|
645
|
+
if (img.closest('a')) return;
|
|
646
|
+
img.addEventListener('click', function() {
|
|
647
|
+
lightboxImg.src = img.src;
|
|
648
|
+
lightboxOverlay.classList.add('lightbox-overlay--visible');
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
</script>
|
|
653
|
+
</body>
|
|
654
|
+
|
|
655
|
+
</html>
|