PageSourceSearch

https://ashler.com/deployment-aware-fetch.js

js ashler.com collected 2026-10-09 22:56:45 UTC 10,382 bytes, 313 lines download raw bytes

1(function() {
2  if (typeof window === 'undefined' || typeof window.fetch !== 'function') return;
3  if (window.__ashlerDeploymentAwareFetchInstalled) return;
4
5  var deploymentId = resolveDeploymentId();
6  window.__ashlerDeploymentAwareFetchInstalled = true;
7
8  var originalFetch = window.fetch.bind(window);
9  var deploymentHeaderName = 'x-ashler-deployment-id';
10  var nextDeploymentHeaderName = 'x-deployment-id';
11  var activeDeploymentHeaderName = 'x-ashler-active-deployment-id';
12  var beforeHardRefreshEventName = 'ashler:before-deployment-hard-refresh';
13  var hardRefreshReadyEventName = 'ashler:deployment-hard-refresh-ready';
14  var hardRefreshFallbackParamName = '__ashler_hard_refresh';
15  var stableDeploymentId = 'stable';
16  var reloadReasonStorageKey = 'ashler:page-reload';
17  var resumeDeploymentCheckUrl = '/api/readyz';
18  var resumeDeploymentCheckInFlight = false;
19  var resumeDeploymentCheckMinIntervalMs = 60000;
20  var lastResumeDeploymentCheckAt = 0;
21  var refreshScheduled = false;
22  var pendingRefresh = null;
23
24  function deploymentIdFromStaticAsset(rawUrl) {
25    if (!rawUrl) return '';
26    try {
27      var target = new URL(String(rawUrl), window.location.href);
28      if (target.origin !== window.location.origin) return '';
29      if (target.pathname.indexOf('/_next/static/') !== 0) return '';
30      return target.searchParams.get('dpl') || '';
31    } catch (_) {
32      return '';
33    }
34  }
35
36  function resolveDeploymentId() {
37    var documentDeploymentId =
38      (document.documentElement && document.documentElement.getAttribute('data-dpl-id')) || '';
39    if (documentDeploymentId) return documentDeploymentId;
40
41    var scripts = typeof document.querySelectorAll === 'function'
42      ? document.querySelectorAll('script[src]')
43      : [];
44    for (var index = 0; index < scripts.length; index += 1) {
45      var scriptDeploymentId = deploymentIdFromStaticAsset(scripts[index].src);
46      if (scriptDeploymentId) return scriptDeploymentId;
47    }
48    return '';
49  }
50
51  function isSameOriginRequest(rawUrl) {
52    if (!rawUrl) return false;
53    try {
54      var target = new URL(String(rawUrl), window.location.href);
55      return target.origin === window.location.origin;
56    } catch (_) {
57      return false;
58    }
59  }
60
61  function isSameOriginApiRequest(rawUrl) {
62    if (!rawUrl) return false;
63    try {
64      var target = new URL(String(rawUrl), window.location.href);
65      return target.origin === window.location.origin
66        && (target.pathname === '/api' || target.pathname.indexOf('/api/') === 0);
67    } catch (_) {
68      return false;
69    }
70  }
71
72  function requestUrl(input) {
73    if (typeof input === 'string') return input;
74    if (typeof URL !== 'undefined' && input instanceof URL) return input.href;
75    if (input && typeof input === 'object' && 'url' in input) return input.url;
76    return '';
77  }
78
79  function requestMethod(input, init) {
80    if (init && init.method) return String(init.method).toUpperCase();
81    if (typeof Request !== 'undefined' && input instanceof Request && input.method) {
82      return String(input.method).toUpperCase();
83    }
84    return 'GET';
85  }
86
87  function isSafeMethod(method) {
88    return method === 'GET' || method === 'HEAD' || method === 'OPTIONS';
89  }
90
91  function collectHeaders(input, init) {
92    var isRequest = typeof Request !== 'undefined' && input instanceof Request;
93    var headers = new Headers(isRequest ? input.headers : undefined);
94
95    if (init && init.headers) {
96      new Headers(init.headers).forEach(function(value, key) {
97        headers.set(key, value);
98      });
99    }
100
101    return headers;
102  }
103
104  function hasDeploymentPin(headers) {
105    return headers.has(nextDeploymentHeaderName) || headers.has(deploymentHeaderName);
106  }
107
108  function mergeHeaders(input, init) {
109    var headers = collectHeaders(input, init);
110
111    if (!hasDeploymentPin(headers)) {
112      headers.set(deploymentHeaderName, deploymentId);
113    }
114
115    return headers;
116  }
117
118  function rememberPendingRefresh(request) {
119    if (!pendingRefresh || request.reason === 'pinned-503') {
120      pendingRefresh = request;
121    }
122  }
123
124  function hasHardRefreshFallbackMarker(marker) {
125    try {
126      var currentUrl = new URL(window.location.href);
127      return currentUrl.searchParams.get(hardRefreshFallbackParamName) === marker;
128    } catch (_) {
129      return false;
130    }
131  }
132
133  function hardRefreshFallbackUrl(marker) {
134    try {
135      var refreshUrl = new URL(window.location.href);
136      refreshUrl.searchParams.set(hardRefreshFallbackParamName, marker);
137      return refreshUrl.href;
138    } catch (_) {
139      return '';
140    }
141  }
142
143  function rememberReloadReason(reason) {
144    try {
145      window.sessionStorage.setItem(reloadReasonStorageKey, JSON.stringify({ reason: reason, at: Date.now() }));
146    } catch (_) {
147      // Storage may be disabled; never block the refresh.
148    }
149  }
150
151  function hardRefreshForDeploymentSkew(reason, activeDeploymentId) {
152    if (refreshScheduled) return;
153
154    var refreshRequest = {
155      reason: reason,
156      deploymentId: deploymentId,
157      activeDeploymentId: activeDeploymentId || '',
158    };
159    var refreshEvent = new CustomEvent(beforeHardRefreshEventName, {
160      cancelable: true,
161      detail: refreshRequest,
162    });
163    if (!window.dispatchEvent(refreshEvent)) {
164      rememberPendingRefresh(refreshRequest);
165      return;
166    }
167
168    pendingRefresh = null;
169
170    var refreshKey = 'ashler:deployment-hard-refresh:' + deploymentId + ':' + reason;
171    var refreshFallbackMarker = deploymentId + ':' + reason;
172    if (hasHardRefreshFallbackMarker(refreshFallbackMarker)) return;
173
174    var useUrlFallback = true;
175    try {
176      var refreshStorage = window.sessionStorage;
177      if (refreshStorage && refreshStorage.getItem(refreshKey) === '1') {
178        return;
179      }
180      if (refreshStorage) {
181        refreshStorage.setItem(refreshKey, '1');
182        useUrlFallback = false;
183      }
184    } catch (_) {
185      useUrlFallback = true;
186    }
187
188    refreshScheduled = true;
189    window.setTimeout(function() {
190      if (useUrlFallback) {
191        var fallbackUrl = hardRefreshFallbackUrl(refreshFallbackMarker);
192        if (fallbackUrl && typeof window.location.replace === 'function') {
193          rememberReloadReason(reason);
194          window.location.replace(fallbackUrl);
195          return;
196        }
197      }
198      rememberReloadReason(reason);
199      window.location.reload();
200    }, 0);
201  }
202
203  window.addEventListener(hardRefreshReadyEventName, function() {
204    if (!pendingRefresh) return;
205    hardRefreshForDeploymentSkew(
206      pendingRefresh.reason,
207      pendingRefresh.activeDeploymentId,
208    );
209  });
210
211  function inspectResponseForDeploymentSkew(response, requestHadDeploymentPin) {
212    if (!response || !response.headers) return;
213
214    var activeDeploymentId = response.headers.get(activeDeploymentHeaderName);
215    if (activeDeploymentId && activeDeploymentId !== deploymentId) {
216      hardRefreshForDeploymentSkew('active-mismatch', activeDeploymentId);
217      return;
218    }
219
220    if (requestHadDeploymentPin && response.status === 503) {
221      hardRefreshForDeploymentSkew('pinned-503', '');
222    }
223  }
224
225  function clearResumeDeploymentCheck() {
226    resumeDeploymentCheckInFlight = false;
227  }
228
229  function checkActiveDeploymentOnResume() {
230    if (!deploymentId || refreshScheduled || resumeDeploymentCheckInFlight) return;
231    var now = Date.now();
232    if (lastResumeDeploymentCheckAt && now - lastResumeDeploymentCheckAt < resumeDeploymentCheckMinIntervalMs) return;
233    lastResumeDeploymentCheckAt = now;
234    resumeDeploymentCheckInFlight = true;
235    var headers = new Headers();
236    headers.set(deploymentHeaderName, stableDeploymentId);
237    originalFetch(resumeDeploymentCheckUrl, {
238      method: 'GET',
239      headers: headers,
240      cache: 'no-store',
241      credentials: 'same-origin',
242    }).then(function(response) {
243      inspectResponseForDeploymentSkew(response, false);
244    }).then(clearResumeDeploymentCheck, clearResumeDeploymentCheck);
245  }
246
247  if (typeof document.addEventListener === 'function') {
248    document.addEventListener('visibilitychange', function() {
249      if (document.visibilityState === 'visible') checkActiveDeploymentOnResume();
250    });
251  }
252
253  window.addEventListener('pageshow', function(event) {
254    if (event && event.persisted) checkActiveDeploymentOnResume();
255  });
256
257  function isDeploymentChunkScript(target) {
258    if (!target || String(target.tagName || '').toUpperCase() !== 'SCRIPT') return false;
259
260    var rawSrc = target.src || '';
261    if (!rawSrc) return false;
262
263    try {
264      var targetUrl = new URL(rawSrc, window.location.href);
265      var targetDeploymentId = deploymentIdFromStaticAsset(targetUrl.href);
266      if (!deploymentId && targetDeploymentId) deploymentId = targetDeploymentId;
267      return targetUrl.origin === window.location.origin
268        && targetUrl.pathname.indexOf('/_next/static/chunks/') === 0
269        && !!targetDeploymentId
270        && targetDeploymentId === deploymentId;
271    } catch (_) {
272      return false;
273    }
274  }
275
276  window.addEventListener('error', function(event) {
277    if (!isDeploymentChunkScript(event && event.target)) return;
278    hardRefreshForDeploymentSkew('chunk-load-error', '');
279  }, true);
280
281  window.fetch = function(input, init) {
282    var rawUrl = requestUrl(input);
283    if (!deploymentId) deploymentId = resolveDeploymentId();
284    if (!deploymentId || !isSameOriginRequest(rawUrl)) {
285      return originalFetch(input, init);
286    }
287
288    var method = requestMethod(input, init);
289    var existingHeaders = collectHeaders(input, init);
290    var shouldPinApiRequest = isSameOriginApiRequest(rawUrl)
291      && isSafeMethod(method)
292      && !hasDeploymentPin(existingHeaders);
293    var requestHadDeploymentPin = hasDeploymentPin(existingHeaders) || shouldPinApiRequest;
294    var fetchPromise;
295
296    if (shouldPinApiRequest) {
297      var headers = mergeHeaders(input, init);
298
299      if (typeof Request !== 'undefined' && input instanceof Request) {
300        fetchPromise = originalFetch(new Request(input, Object.assign({}, init || {}, { headers: headers })));
301      } else {
302        fetchPromise = originalFetch(input, Object.assign({}, init || {}, { headers: headers }));
303      }
304    } else {
305      fetchPromise = originalFetch(input, init);
306    }
307
308    return fetchPromise.then(function(response) {
309      inspectResponseForDeploymentSkew(response, requestHadDeploymentPin);
310      return response;
311    });
312  };
313})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.