Skip to content

Instantly share code, notes, and snippets.

@boeboe
Created October 19, 2023 11:13
Show Gist options
  • Save boeboe/8d8d322a255e13cddbee426801093e03 to your computer and use it in GitHub Desktop.
Save boeboe/8d8d322a255e13cddbee426801093e03 to your computer and use it in GitHub Desktop.
var _process, _process$env;
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly && (symbols = symbols.filter(function(sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable
})),
keys.push.apply(keys, symbols)
}
return keys
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function(key) {
_defineProperty(target, key, source[key])
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key))
})
}
return target
}
function _defineProperty(obj, key, value) {
return key in obj ? Object.defineProperty(obj, key, {
value,
enumerable: !0,
configurable: !0,
writable: !0
}) : obj[key] = value,
obj
}
self.__serviceWorkerCacheStoragePrefix = "https://slack.com/fake-url-for-service-worker-item-storage",
self.__serviceWorkerCacheKey = "gantry-1697711245",
self.__serviceWorkerManifest = [{
url: "https://a.slack-edge.com/bv1-10/lato-italic-5bbcd6c.woff2"
}, {
url: "https://a.slack-edge.com/bv1-10/NotoSansTC-Regular.min-9f3f03b.woff2"
}];
let CACHE_KEY = self.__serviceWorkerCacheKey || "dev";
const CACHE_STORAGE_PREFIX = self.__serviceWorkerCacheStoragePrefix || ""
, CACHE_KEY_PREFIX = "gantry-"
, CACHE_KEY_QS_NAME = "cacheKey"
, shouldRecordTelemetry = Math.floor(101 * Math.random()) < 5;
let shouldTrace = Math.floor(101 * Math.random()) < 5;
const MAXIMUM_CACHE_AGE = 345600
, ACCEPTABLE_CACHE_AGE = 86400
, MINIMUM_BUCKETS = 3
, MAXIMUM_BUCKETS = 5
, FAKE_SERVICE_WORKER_ENDPOINT = "/api/api.test?service-worker-test"
, MAX_REQUESTS_IN_FLIGHT = 4
, gantryClientURLMatch = /app\.(?:[^.]+\.)?(slack|slack-gov).com\/([a-z-0-9]+)\/T[A-Z0-9]{8,}/;
let simpleMetrics, simpleTracer, manifestURLs = self.__serviceWorkerManifest && self.__serviceWorkerManifest.map(entry=>entry.url), selfProfilingEnabled = !1;
const SELF_PROFILING_KEY = "self-profiling"
, idbKeyval = (()=>{
let dbInstance;
const DB_NAME = "serviceworkerFlags"
, OBJECT_STORE_NAME = "keyval";
async function withStore(type, callback) {
const db = await (dbInstance || (dbInstance = new Promise((resolve,reject)=>{
const openreq = indexedDB.open(DB_NAME, 1);
openreq.onerror = (()=>{
reject(openreq.error)
}
),
openreq.onupgradeneeded = (()=>{
openreq.result.createObjectStore(OBJECT_STORE_NAME)
}
),
openreq.onsuccess = (()=>{
resolve(openreq.result)
}
)
}
)),
dbInstance);
return new Promise((resolve,reject)=>{
const transaction = db.transaction(OBJECT_STORE_NAME, type);
transaction.oncomplete = (()=>resolve()),
transaction.onerror = (()=>reject(transaction.error)),
callback(transaction.objectStore(OBJECT_STORE_NAME))
}
)
}
return {
async get(key) {
let request;
return await withStore("readonly", store=>{
request = store.get(key)
}
),
request.result
},
set: (key,value)=>withStore("readwrite", store=>{
store.put(value, key)
}
),
delete: key=>withStore("readwrite", store=>{
store.delete(key)
}
)
}
}
)();
async function messageHandler(event) {
if (event.data && "self_profiling" === event.data.type) {
const isEnabled = !0 === event.data.enabled;
selfProfilingEnabled = isEnabled;
try {
isEnabled ? await idbKeyval.set(SELF_PROFILING_KEY, 1) : await idbKeyval.delete(SELF_PROFILING_KEY)
} catch (e) {
log(event, "error setting self_profiling_key", e)
}
} else if (event.data && "canvas_prefetch" === event.data.type) {
const fetches = maxParallelIterator(event.data.urls, url=>dipAndCache(url, event).then(result=>!!result || fetchAndCache(url, event)), MAX_REQUESTS_IN_FLIGHT);
try {
const responses = await Promise.all(fetches);
log(event, "All canvas files fetched", responses)
} catch (err) {
log(event, "Error fetching canvas files for cache", err)
}
}
}
function errorHandler(event) {
if (log(event, `errorHandler triggered for event, type "${null == event ? void 0 : event.type}" error "${null == event ? void 0 : event.message}"`),
simpleTracer && shouldTrace) {
const errorSpan = simpleTracer.startSpan("sw:error_event");
errorSpan.addTags({
state: null == event ? void 0 : event.type,
error: null == event ? void 0 : event.message
}),
errorSpan.close(),
simpleTracer.report([errorSpan])
} else
log(event, `Not firing traces for this error, tracer exists: ${!!simpleTracer}, shouldTrace: ${!!shouldTrace}`)
}
function installHandler(event) {
const installStart = performance.now();
log(event, "install event", event.target);
try {
simpleMetrics = new SimpleMetrics(event),
simpleTracer = getSimpleTracer(event)
} catch (e) {
log(event, "Error instantiating metrics", e)
}
const installSpan = simpleTracer.startSpan("sw:install");
return installSpan.addTags({
asset_volume: manifestURLs.length
}),
simpleMetrics && shouldRecordTelemetry && (simpleMetrics.recordCount("sw_install"),
simpleMetrics.recordCount("sw_cached_asset_volume", manifestURLs.length),
getValidCacheKeys().then(keys=>{
simpleMetrics.recordCount("sw_cache_bucket_count", keys.length)
}
)),
messageClients("installStart"),
getValidCacheKeys().then(cacheKeys=>{
if (installSpan.addTags({
cache_bucket_count: cacheKeys.length
}),
cacheKeys.length > 0) {
if ("1" === new URL(event.target.location.href).searchParams.get("new_enough_acceptable")) {
const currTime = Math.round(Date.now() / 1e3);
if (cacheKeys.map(key=>parseInt(key.replace(/[^\d]+/, ""), 10)).filter(key=>!isNaN(key)).sort().reverse()[0] > currTime - ACCEPTABLE_CACHE_AGE)
return simpleMetrics && shouldRecordTelemetry && simpleMetrics.recordCount("sw_install_new_enough_acceptable"),
log(event, "Skipped fetching assets for cache because what we have is new enough"),
Promise.resolve({});
simpleMetrics.recordCount("sw_install_not_new_enough_acceptable"),
log(event, "Not skipping fetching assets because cached assets are too old")
}
}
const fetches = maxParallelIterator(manifestURLs, url=>dipAndCache(url, event).then(result=>!!result || fetchAndCache(url, event)), MAX_REQUESTS_IN_FLIGHT);
return Promise.all(fetches).then(responses=>{
const installDuration = performance.now() - installStart;
return simpleMetrics && shouldRecordTelemetry && simpleMetrics.recordTiming("sw_install_timing", installDuration),
log(event, "All files fetched and put in cache", `${installDuration}ms`),
messageClients("installEnd"),
self.skipWaiting(),
responses
}
).catch(err=>{
log(event, "Error fetching all files for cache", err),
messageClients("installEnd"),
shouldTrace && (installSpan.addTags({
state: "error",
error: null == err ? void 0 : err.message
}),
installSpan.close(),
simpleTracer.report([installSpan]))
}
)
}
)
}
async function activateHandler(event) {
let activateSpan, timer;
log(event, "activate event", event),
simpleMetrics && shouldRecordTelemetry && simpleMetrics.recordCount("sw_activate"),
simpleTracer && shouldTrace && (activateSpan = simpleTracer.startSpan("sw:activate")),
clients.claim();
const setFlagStateWithTimeout = Promise.race([idbKeyval.get(SELF_PROFILING_KEY), new Promise(resolve=>{
timer = setTimeout(()=>{
simpleMetrics && simpleMetrics.recordCount("sw_idb_timeout"),
resolve(0)
}
, 500)
}
)]).then(value=>{
clearTimeout(timer),
selfProfilingEnabled = 1 === value
}
, ()=>{
selfProfilingEnabled = !1
}
);
return event.waitUntil(setFlagStateWithTimeout),
getInvalidCacheKeys().then(cacheKeys=>deleteCacheKeys(event, cacheKeys, activateSpan))
}
function fetchHandler(event) {
let fetchSpan;
const spans = [];
simpleTracer && shouldTrace && (fetchSpan = simpleTracer.startSpan("sw:fetch"),
spans.push(fetchSpan));
try {
var _event$request;
const fetchStart = performance.now()
, requestURL = null === (_event$request = event.request) || void 0 === _event$request ? void 0 : _event$request.url;
if ("string" != typeof requestURL)
throw simpleTracer && shouldTrace && (fetchSpan.addTags({
state: "error",
url: requestURL,
reason: "invalid url string"
}),
fetchSpan.close(),
simpleTracer.report(spans)),
new Error(`${requestURL} is not a valid url string`);
if (requestURL.endsWith(FAKE_SERVICE_WORKER_ENDPOINT)) {
const fakeServiceWorkerEndpointResponse = {
ok: !0,
totes_fake: !0
};
return simpleTracer && shouldTrace && (fetchSpan.addTags({
state: "success",
url: requestURL,
reason: "fake_sw_endpoint"
}),
fetchSpan.close(),
simpleTracer.report(spans)),
event.respondWith(Promise.resolve(new Response(JSON.stringify(fakeServiceWorkerEndpointResponse),{
headers: {
"Content-Type": "application/json"
}
})))
}
const parsedFetchURL = new URL(requestURL)
, fetchURL = requestURL.split("?")[0]
, cacheMatchRequest = new Request(fetchURL);
if (fetchURL.match(gantryClientURLMatch))
return handleInitialPageFetch(event);
if (!doesExistInManifest(event, fetchURL) && !isCachedCanvasAsset(event, fetchURL))
return Promise.resolve();
const cacheKey = parsedFetchURL.searchParams.get(CACHE_KEY_QS_NAME) || CACHE_KEY;
return event.respondWith(caches.open(cacheKey).then(cache=>cache.match(cacheMatchRequest)).then(response=>clients.get(event.clientId).then(client=>{
const url = client ? client.url : event.request.url
, {preventCachedResponse, preventCachedResponseForDev} = shouldGetCachedResponse(url);
if (response && !preventCachedResponse) {
if (!hasCacheBucketExpired()) {
if (log(event, "responding from cache", fetchURL),
simpleMetrics && shouldRecordTelemetry && simpleMetrics.recordTiming("sw_fetch_hit_timing", performance.now() - fetchStart),
selfProfilingEnabled) {
const clonedHeaders = new Headers(response.headers);
return clonedHeaders.append("document-policy", "js-profiling"),
new Response(response.body,{
status: response.status,
statusText: response.statusText,
headers: clonedHeaders
})
}
return response
}
log(event, "asset is too old to use", fetchURL),
simpleMetrics && shouldRecordTelemetry && simpleMetrics.recordCount("sw_too_old_cache_hit"),
simpleTracer && shouldTrace && fetchSpan.addTags({
asset_expired: !0
})
}
let fetchRequestSpan;
var _fetchSpan, _fetchSpan2;
(log(event, `responding with fetch ${response && preventCachedResponse ? ` (${preventCachedResponseForDev ? "because this is dev" : "because of force_cold_boot"})` : ""}`, fetchURL),
simpleTracer && shouldTrace) && (fetchRequestSpan = simpleTracer.startSpan("sw:fetch_request", {
parentSpanId: null === (_fetchSpan = fetchSpan) || void 0 === _fetchSpan ? void 0 : _fetchSpan.id,
traceId: null === (_fetchSpan2 = fetchSpan) || void 0 === _fetchSpan2 ? void 0 : _fetchSpan2.traceId
}));
return fetch(event.request.clone()).catch(err=>(log(event, "ERROR updating cache from network", err.message),
simpleTracer && shouldTrace && (fetchRequestSpan.addTags({
state: "error",
error: null == err ? void 0 : err.message,
reason: "error updating cache from network"
}),
fetchRequestSpan.close(),
spans.push(fetchRequestSpan)),
Promise.reject(err)))
}
)).catch(err=>(log(event, "ERROR opening and matching in cache", err.message),
simpleTracer && shouldTrace && (fetchSpan.addTags({
state: "error",
error: null == err ? void 0 : err.message,
reason: "error opening and matching cache"
}),
fetchSpan.close(),
simpleTracer.report(spans)),
fetch(event.request.url))))
} catch (e) {
return log(event, "ERROR exception thrown in the fetch handler", e.message, event),
simpleTracer && shouldTrace && (fetchSpan.addTags({
state: "error",
error: null == e ? void 0 : e.message,
reason: "fetch handler error"
}),
fetchSpan.close(),
simpleTracer.report(spans)),
fetch(event.request.url)
}
}
function handleInitialPageFetch(event) {
var _event$target, _event$target$locatio;
const requestURL = event.request.url
, parsedFetchURL = new URL(requestURL)
, fetchURL = requestURL.split("?")[0]
, origin = null == event ? void 0 : null === (_event$target = event.target) || void 0 === _event$target ? void 0 : null === (_event$target$locatio = _event$target.location) || void 0 === _event$target$locatio ? void 0 : _event$target$locatio.origin
, isDev = !(null == origin || !origin.match(/dev[0-9]*/))
, isQa = !(null == origin || !origin.match(/qa[0-9]*/));
log(event, "normalizing request URL for cache", fetchURL);
const isNumberedDev = !(null == origin || !origin.match(/dev[0-9]+\.*/))
, isNumberedQa = !(null == origin || !origin.match(/qa[0-9]+\.*/));
let fetchURLPath = "/boot/";
(isDev || isQa) && (fetchURLPath = "/dev-cdn/buildy/assets/"),
(isNumberedDev || isNumberedQa) && (fetchURLPath = "/dev-cdn/buildy/js/static/.buildy/");
const isJsPath = !!parsedFetchURL.searchParams.get("js_path")
, urlMatch = fetchURL.match(gantryClientURLMatch);
return event.respondWith(async function() {
let appName = urlMatch[2];
const spaceParam = parsedFetchURL.searchParams.get("space");
"docs" === appName && "1" === spaceParam && (appName = "quip");
const experimentSoGv2StorageKey = `${CACHE_STORAGE_PREFIX}/is_sonic_on_gantry_v2_enabled`;
if ("client" === appName)
try {
const cache = await caches.open(CACHE_KEY)
, response = await cache.match(experimentSoGv2StorageKey);
response && await response.json() && (log(event, "Sonic on Gantry-v2 Enabled, serving client-v2.html"),
appName = "client-v2")
} catch (e) {
log(event, "Failed to retrieve experiment state from cache", experimentSoGv2StorageKey, e),
appName = "client"
}
const fetchURLFilename = getBootFileNameForGantryApp(appName, isJsPath)
, normalizedURL = `${fetchURLPath}${fetchURLFilename}`
, cacheMatchRequest = new Request(normalizedURL)
, cacheKey = parsedFetchURL.searchParams.get(CACHE_KEY_QS_NAME) || CACHE_KEY
, {preventCachedResponse, preventCachedResponseForDev} = shouldGetCachedResponse(requestURL);
if (!doesExistInManifest(event, normalizedURL))
return fetch(event.request.clone());
if (preventCachedResponse || preventCachedResponseForDev)
return log(event, `responding with fetch (${preventCachedResponseForDev ? "because this is dev" : "because of force_cold_boot"})`, normalizedURL),
fetch(event.request.clone());
try {
const cache = await caches.open(cacheKey)
, response = await cache.match(cacheMatchRequest);
if (response) {
if (!hasCacheBucketExpired()) {
if (log(event, "responding from cache (normalized)", normalizedURL),
selfProfilingEnabled) {
const clonedHeaders = new Headers(response.headers);
return clonedHeaders.append("document-policy", "js-profiling"),
new Response(response.body,{
status: response.status,
statusText: response.statusText,
headers: clonedHeaders
})
}
return response
}
log(event, "asset is too old to use", normalizedURL)
}
log(event, "responding with fetch", fetchURL);
try {
return fetch(event.request.clone())
} catch (err) {
throw log(event, "ERROR updating cache from network", err.message),
err
}
} catch (err) {
return log(event, "ERROR opening and matching in cache", err.message),
fetch(event.request.clone())
}
}())
}
function isCachedCanvasAsset(event, normalizedURL) {
const isCanvasAsset = /canvas_blob/.test(normalizedURL);
return isCanvasAsset || debug(event, `requested path ${normalizedURL} is not a canvas cached asset`),
isCanvasAsset
}
function doesExistInManifest(event, normalizedURL) {
const manifestMatchURL = 0 === normalizedURL.indexOf("http") ? new URL(normalizedURL).pathname : normalizedURL;
return !manifestURLs.every(url=>!url.includes(manifestMatchURL)) || (debug(event, `requested path ${manifestMatchURL} is not in the manifest, exiting early`),
!1)
}
function shouldGetCachedResponse(url) {
var _parsedURL$origin;
const parsedURL = new URL(url)
, isDev = !(null === (_parsedURL$origin = parsedURL.origin) || void 0 === _parsedURL$origin || !_parsedURL$origin.match(/dev[0-9]*/))
, forceColdBootParam = parsedURL.searchParams.get("force_cold_boot")
, isJsPath = !!parsedURL.searchParams.get("js_path")
, forcingWarmBoot = "0" === forceColdBootParam
, forcingColdBootForDev = isDev && !forcingWarmBoot;
return {
preventCachedResponse: "1" === forceColdBootParam || forcingColdBootForDev || isJsPath && !forcingWarmBoot,
preventCachedResponseForDev: forcingColdBootForDev
}
}
function getCacheKeys() {
return caches.keys().then(keys=>keys.filter(cacheKey=>cacheKey.startsWith(CACHE_KEY_PREFIX) && cacheKey !== CACHE_KEY).sort((a,b)=>a > b ? -1 : 1))
}
function getValidCacheKeys() {
const currTime = Math.round(Date.now() / 1e3);
return getCacheKeys().then(keys=>keys.filter((cacheKey,index)=>index < MINIMUM_BUCKETS || !(index >= MAXIMUM_BUCKETS) && parseInt(cacheKey.replace(/[^\d]+/, ""), 10) > currTime - MAXIMUM_CACHE_AGE))
}
function getInvalidCacheKeys() {
const currTime = Math.round(Date.now() / 1e3);
return getCacheKeys().then(keys=>keys.slice(MINIMUM_BUCKETS).filter((cacheKey,index)=>MINIMUM_BUCKETS + index >= MAXIMUM_BUCKETS || parseInt(cacheKey.replace(/[^\d]+/, ""), 10) < currTime - MAXIMUM_CACHE_AGE))
}
let purgeExcessCacheKeysPromise;
function purgeExcessCacheKeys(event) {
return purgeExcessCacheKeysPromise || (log(event, "Removing old cache buckets because of a cache write failure"),
simpleMetrics && shouldRecordTelemetry && simpleMetrics.recordCount("sw_purge_excess_cache_due_to_failed_write"),
purgeExcessCacheKeysPromise = getCacheKeys().then(keys=>{
const keysToPurge = keys.slice(MINIMUM_BUCKETS);
return deleteCacheKeys(event, keysToPurge)
}
).finally(()=>{
log(event, "Removing old cache buckets complete!"),
purgeExcessCacheKeysPromise = void 0
}
)),
purgeExcessCacheKeysPromise
}
function deleteCacheKeys(event, cacheKeys) {
let initialSpan = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : simpleTracer && shouldTrace ? simpleTracer.startSpan("sw:delete_cache_keys") : void 0;
const purgeSpans = []
, cacheDeletions = cacheKeys.map(cacheKey=>{
let purgeBucketSpan;
return simpleTracer && shouldTrace && (purgeBucketSpan = simpleTracer.startSpan("sw:purge_failed", {
traceId: null == initialSpan ? void 0 : initialSpan.traceId,
parentSpanId: null == initialSpan ? void 0 : initialSpan.id
})),
caches.delete(cacheKey).then(()=>{
log(event, "purged cache of old cache bucket", cacheKey)
}
).catch(e=>{
log(event, "failed to purge cache of old cache bucket", cacheKey, e),
simpleTracer && shouldTrace && (purgeBucketSpan.addTags({
state: "error",
error: null == e ? void 0 : e.message
}),
purgeBucketSpan.close(),
purgeSpans.push(purgeBucketSpan))
}
)
}
)
, cacheDeletionP = Promise.all(cacheDeletions);
return simpleTracer && shouldTrace && cacheDeletionP.finally(()=>{
if (!purgeSpans.length)
return;
initialSpan && initialSpan.close();
const spans = initialSpan ? [initialSpan, ...purgeSpans] : purgeSpans;
simpleTracer.report(spans)
}
),
cacheDeletionP
}
function dipAndCache(url, event) {
return url.match(/\.html$/) ? Promise.resolve(!1) : getValidCacheKeys().then(keys=>{
if (0 === keys.length)
return Promise.resolve(!1);
const cacheKey = keys[0];
log(event, `checking if asset is in an existing cache bucket: ${cacheKey}`, url);
const cacheMatchRequest = new Request(url,{
credentials: "same-origin",
cache: "default"
});
return Promise.all(keys.map(key=>caches.open(key))).then(openCaches=>Promise.all(openCaches.map(openCache=>openCache.match(cacheMatchRequest)))).then(matches=>{
const match = matches.find(element=>!!element);
return match ? (log(event, "asset found in existing cache bucket, adding to new bucket", url),
caches.open(CACHE_KEY).then(cache=>(log(event, "putting asset in cache", url),
cache.put(cacheMatchRequest, match).catch(()=>{
purgeExcessCacheKeys(event)
}
),
!0))) : (log(event, "asset not found in an existing cache bucket", url),
!1)
}
)
}
).catch(err=>{
throw log(event, "ERROR dipping and caching asset", err),
err
}
)
}
function fetchAndCache(url, event) {
const requestCache = url.match(/\.html$/) ? "no-cache" : "default";
if (!!url.match(/\/api\//))
return log(event, "Caching API requests is not supported."),
null;
const request = new Request(url,{
credentials: "same-origin",
cache: requestCache
});
return log(event, "fetching asset", url),
fetch(request).then(networkResponse=>networkResponse.ok ? caches.open(CACHE_KEY).then(cache=>(log(event, "putting asset in cache", url),
cache.put(request, networkResponse).catch(error=>{
throw purgeExcessCacheKeys(event),
error
}
))) : (log(event, "not caching asset as it returned a non 200 code", url),
null)).catch(err=>{
throw log(event, "ERROR fetching and caching asset", err),
err
}
)
}
function getBootFileNameForGantryApp(appName, isJsPath) {
return `${appName}${isJsPath ? ".jspath" : ""}.html`
}
function hasCacheBucketExpired() {
const currentTime = Math.round(Date.now() / 1e3);
let cacheBucketTimeStamp;
try {
cacheBucketTimeStamp = parseInt(CACHE_KEY.replace(CACHE_KEY_PREFIX, ""), 10)
} catch (e) {
cacheBucketTimeStamp = Math.round(Date.now() / 1e3)
}
return navigator.onLine && cacheBucketTimeStamp < currentTime - MAXIMUM_CACHE_AGE
}
function messageClients(msg) {
clients.matchAll({
includeUncontrolled: !0
}).then(clients=>{
clients.forEach(client=>{
client.postMessage(msg)
}
)
}
)
}
self.addEventListener("install", installHandler),
self.addEventListener("error", errorHandler),
self.addEventListener("activate", activateHandler),
self.addEventListener("fetch", fetchHandler),
self.addEventListener("message", messageHandler);
const enableFeatureForUser = percentage=>Math.floor(100 * Math.random()) < percentage;
function log(event) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++)
args[_key - 1] = arguments[_key];
return outputToConsole("info", event, ...args)
}
function debug(event) {
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++)
args[_key2 - 1] = arguments[_key2];
return outputToConsole("debug", event, ...args)
}
function outputToConsole(type, event) {
for (var _len3 = arguments.length, args = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++)
args[_key3 - 2] = arguments[_key3];
return clients.get(event.clientId).then(client=>{
var _event$target2;
let url;
if (client ? url = client.url : null != event && event.request ? url = event.request.url : null != event && null !== (_event$target2 = event.target) && void 0 !== _event$target2 && _event$target2.location && (url = event.target.location.href),
!url)
return !1;
if (shouldLog(url, type)) {
const logDate = makeLogDate();
return console["info" === type ? "log" : type](`${logDate} [SERVICE-WORKER]`, ...args),
!0
}
return !1
}
)
}
function shouldLog(urlString, forType) {
const CONSOLE_FNS = {
0: "debug",
1: "info",
2: "warn",
3: "error"
}
, forTypeIdx = Object.keys(CONSOLE_FNS).findIndex(typeIdx=>CONSOLE_FNS[typeIdx] === forType)
, parsedUrl = new URL(urlString)
, isDev = parsedUrl.host.match(/dev[0-9]*/)
, logLevel = parsedUrl.searchParams.get("log_level") || null;
if (logLevel) {
return Object.keys(CONSOLE_FNS).findIndex(typeIdx=>CONSOLE_FNS[typeIdx] === logLevel) >= forTypeIdx
}
return isDev ? forTypeIdx >= 2 : forTypeIdx >= 1
}
function makeLogDate() {
const date = new Date
, mo = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getMonth()]
, d = date.getDate()
, time = {
h: date.getHours(),
mi: date.getMinutes(),
s: date.getSeconds(),
ms: date.getMilliseconds()
};
return Object.keys(time).map(moment=>("ms" === moment ? time[moment] < 10 ? time[moment] = `${time[moment]}00` : time[moment] < 100 && (time[moment] = `${time[moment]}0`) : time[moment] < 10 && (time[moment] = `0 ${time[moment]}`),
null)),
`${mo}-${d} ${time.h}:${time.mi}:${time.s}.${time.ms}`
}
function shouldReportToDev(event) {
var _event$target3, _event$target3$locati;
const hostname = null == event ? void 0 : null === (_event$target3 = event.target) || void 0 === _event$target3 ? void 0 : null === (_event$target3$locati = _event$target3.location) || void 0 === _event$target3$locati ? void 0 : _event$target3$locati.hostname;
return (null == hostname ? void 0 : hostname.match(/(^|\.)(dev|qa)[0-9]*\./)) || (null == hostname ? void 0 : hostname.match(/(^|\.)slack-gov-dev.com$/))
}
function isGovSlack(event) {
var _event$target4, _event$target4$locati;
const hostname = null == event ? void 0 : null === (_event$target4 = event.target) || void 0 === _event$target4 ? void 0 : null === (_event$target4$locati = _event$target4.location) || void 0 === _event$target4$locati ? void 0 : _event$target4$locati.hostname;
return (null == hostname ? void 0 : hostname.match(/(^|\.)slack-gov.com$/)) || (null == hostname ? void 0 : hostname.match(/(^|\.)slack-gov-dev.com$/))
}
function getBeaconDomain(event) {
const isGov = isGovSlack(event);
return shouldReportToDev(event) ? isGov ? "slack-gov-dev.com" : "dev.slack.com" : isGov ? "slack-gov.com" : "slack.com"
}
function getBeaconUrl(event) {
return `https://${getBeaconDomain(event)}/beacon/timing`
}
function getTraceDomain(event) {
const isGov = isGovSlack(event);
return shouldReportToDev(event) ? isGov ? "slackb-gov-dev.com" : "dev.slackb.com" : isGov ? "slackb-gov.com" : "slackb.com"
}
function getTraceUrl(event) {
return `https://${getTraceDomain(event)}/traces/v1/list_of_spans/json`
}
const METRICS_POST_INTERVAL = 25e3
, MAX_URL_LENGTH = 2e3;
class SimpleMetrics {
constructor(event) {
this.isEnabled = enableFeatureForUser(100),
this.beaconURL = getBeaconUrl(event),
this.metricsQueue = {},
this.event = event,
log(this.event, "SimpleMetrics starting interval"),
setInterval(this.beaconMetrics.bind(this), METRICS_POST_INTERVAL)
}
beaconMetrics() {
if (!this.isEnabled)
return !1;
const metricsKeys = Object.keys(this.metricsQueue);
if (0 === metricsKeys.length)
return !1;
let data = [];
const urls = []
, version = "dev" === CACHE_KEY ? CACHE_KEY : parseInt(CACHE_KEY.replace(CACHE_KEY_PREFIX, ""), 10)
, makeBeaconURL = beaconData=>`${this.beaconURL}?ver=${version}&data=${(items=>encodeURIComponent(items.join(";")))(beaconData)}`;
log(this.event, "SimpleMetrics beaconing interval", this.metricsQueue);
for (let i = 0; i < metricsKeys.length; i++) {
const label = metricsKeys[i]
, item = `${label}:${this.metricsQueue[label].join(",")}`;
makeBeaconURL(data.concat(item)).length <= MAX_URL_LENGTH ? data.push(item) : (urls.push(makeBeaconURL(data)),
data = [item])
}
return data.length > 0 && urls.push(makeBeaconURL(data)),
log(this.event, "SimpleMetrics calling URLs", urls),
urls.forEach(url=>{
const request = new Request(url,{
mode: "no-cors"
});
fetch(request)
}
),
this.metricsQueue = {},
urls
}
recordCount(label) {
let value = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 1;
if (!this.isEnabled)
return;
const metricsLabel = `${label}|count`;
this.metricsQueue[metricsLabel] || (this.metricsQueue[metricsLabel] = []),
this.metricsQueue[metricsLabel].push(value)
}
recordTiming(label, value) {
if (!this.isEnabled)
return;
const metricsLabel = `${label}|timing`;
this.metricsQueue[metricsLabel] || (this.metricsQueue[metricsLabel] = []),
this.metricsQueue[metricsLabel].push(Math.round(value))
}
}
const TAG_TYPES = {
string: [0, "v_str"],
boolean: [1, "v_bool"],
number: [2, "v_int64"]
};
function getSimpleTracer(event) {
return new SimpleTracer(event)
}
const BEACON_TRACE_INTERVAL = 6e4;
class SimpleTracer {
constructor(event) {
this.traceURL = getTraceUrl(event),
this.processSpanForReport = this.processSpanForReport.bind(this),
this.report = this.report.bind(this),
this.sharedTags = this.getSharedTags(navigator),
this.tracesQueue = [],
setInterval(this.ratelimitedBeaconTraces.bind(this), BEACON_TRACE_INTERVAL)
}
getSharedTags(navigator) {
const userAgent = null == navigator ? void 0 : navigator.userAgent
, environmentData = getEnvironmentData(userAgent)
, version = "dev" === CACHE_KEY ? CACHE_KEY : parseInt(CACHE_KEY.replace(CACHE_KEY_PREFIX, ""), 10);
return _objectSpread(_objectSpread({}, environmentData), {}, {
user_agent: userAgent,
version,
service_name: "desktop",
service_subcomponent: "service_worker"
})
}
startSpan(name, options) {
const span = new SimpleSpan(this,options);
return span.setOperationName(name),
span
}
ratelimitedBeaconTraces() {
if (0 === this.tracesQueue.length)
return;
const trace = this.tracesQueue[0];
this.beaconSpans(trace),
this.tracesQueue = []
}
report(spans) {
let tags = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {};
const spansForReporting = spans.map(this.processSpanForReport)
, sharedTags = this.processTagsForBeacon(_objectSpread(_objectSpread({}, tags), this.sharedTags));
this.tracesQueue.push({
spans: spansForReporting,
tags: sharedTags
})
}
beaconSpans() {
let {spans, tags={}} = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
const data = {
spans,
tags
};
fetch(new Request(this.traceURL,{
method: "POST",
body: JSON.stringify(data)
}))
}
processSpanForReport() {
let span = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {};
if (!(span && span instanceof SimpleSpan))
return null;
const {id, traceId, parentSpanId, name, startTimeMs, closeTimeMs, tags={}} = span
, startTsMicros = 1e3 * startTimeMs
, durationMicros = 1e3 * (closeTimeMs ? closeTimeMs - startTimeMs : 0);
return closeTimeMs || (tags._flush = !0),
{
id: btoa(id),
trace_id: btoa(traceId),
parent_id: btoa(parentSpanId),
name,
start_timestamp_micros: startTsMicros,
duration_micros: durationMicros || 1,
tags: this.processTagsForBeacon(tags)
}
}
processTagsForBeacon(tags) {
if (!tags)
return [];
return Object.keys(tags).map(key=>{
const value = tags[key];
return this.convertToTracerTag(key, value)
}
)
}
convertToTracerTag(key, value) {
const type = typeof value;
return TAG_TYPES[type] ? {
key,
[TAG_TYPES[type][1]]: value,
v_type: TAG_TYPES[type][0]
} : {}
}
}
function getEnvironmentData(userAgent) {
if (!userAgent)
return {};
const isSSB = !!/(Slack)/g.test(userAgent);
if (!isSSB)
return {
is_ssb: !1
};
const parts = userAgent.split("/");
return {
is_ssb: isSSB,
app_version: parts[parts.length - 1],
user_agent: userAgent
}
}
class SimpleSpan {
constructor(tracer) {
let {tags={}, parentSpanId="", traceId} = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {};
this.tracer = tracer,
this.tags = tags,
this.parentSpanId = parentSpanId,
this.id = simpleUUIDGenerator(),
this.traceId = traceId || this.id,
this.startTimeMs = Date.now()
}
setOperationName(name) {
return this.name = name,
this
}
addTags(tags) {
return this.tags = _objectSpread(_objectSpread({}, this.tags), tags),
this
}
close() {
return this.closeTimeMs = Date.now(),
this
}
}
function simpleUUIDGenerator() {
let dt = (new Date).getTime();
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c=>{
const r = (dt + 16 * Math.random()) % 16 | 0;
return dt = Math.floor(dt / 16),
("x" === c ? r : 3 & r | 8).toString(16)
}
).replace(/-/g, "")
}
const test = {
makeLogDate,
SimpleMetrics,
SimpleTracer,
manifestURLs,
FAKE_SERVICE_WORKER_ENDPOINT,
CACHE_KEY
};
function maxParallelIterator(source, handler, maxParallel) {
const blockers = []
, unblockNext = ()=>{
const next = blockers.shift();
next && next()
}
, output = source.map(input=>{
return new Promise(resolve=>{
blockers.push(resolve)
}
).then(()=>handler(input)).finally(unblockNext)
}
);
for (let i = 0; i < maxParallel; i++)
unblockNext();
return output
}
Object.defineProperty(test, "log", {
get: ()=>log,
set: v=>{
log = v
}
}),
Object.defineProperty(test, "debug", {
get: ()=>debug,
set: v=>{
debug = v
}
}),
Object.defineProperty(test, "shouldTrace", {
get: ()=>shouldTrace,
set: v=>{
shouldTrace = v
}
}),
Object.defineProperty(test, "simpleTracer", {
get: ()=>simpleTracer,
set: v=>{
simpleTracer = v
}
}),
Object.defineProperty(test, "simpleMetrics", {
get: ()=>simpleMetrics,
set: v=>{
simpleMetrics = v
}
}),
Object.defineProperty(test, "manifestURLs", {
get: ()=>manifestURLs,
set: v=>{
manifestURLs = v
}
}),
Object.defineProperty(test, "getSimpleTracer", {
get: ()=>getSimpleTracer,
set: v=>{
getSimpleTracer = v
}
}),
Object.defineProperty(test, "hasCacheBucketExpired", {
get: ()=>hasCacheBucketExpired,
set: v=>{
hasCacheBucketExpired = v
}
}),
Object.defineProperty(test, "getValidCacheKeys", {
get: ()=>getValidCacheKeys,
set: v=>{
getValidCacheKeys = v
}
}),
Object.defineProperty(test, "getInvalidCacheKeys", {
get: ()=>getInvalidCacheKeys,
set: v=>{
getInvalidCacheKeys = v
}
}),
Object.defineProperty(test, "dipAndCache", {
get: ()=>dipAndCache,
set: v=>{
dipAndCache = v
}
}),
Object.defineProperty(test, "fetchAndCache", {
get: ()=>fetchAndCache,
set: v=>{
fetchAndCache = v
}
}),
Object.defineProperty(test, "CACHE_KEY", {
get: ()=>fetchAndCache,
set: v=>{
CACHE_KEY = v
}
}),
Object.defineProperty(test, "purgeExcessCacheKeysPromise", {
get: ()=>purgeExcessCacheKeysPromise,
set: v=>{
purgeExcessCacheKeysPromise = v
}
}),
"undefined" == typeof ServiceWorkerGlobalScope && "test" === (null === (_process = process) || void 0 === _process ? void 0 : null === (_process$env = _process.env) || void 0 === _process$env ? void 0 : _process$env.NODE_ENV) && (module.exports = {
CACHE_KEY,
gantryClientURLMatch,
installHandler,
errorHandler,
activateHandler,
fetchHandler,
test,
maxParallelIterator
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment