PageSourceSearch

https://xipher.org/lib/StreamSaver/sw.js

js xipher.org collected 2026-10-05 01:08:49 UTC 3,681 bytes, 129 lines download raw bytes

vendor: 3,681 bytes, lines 1-129
1/* global self ReadableStream Response */
2
3self.addEventListener('install', () => {
4  self.skipWaiting()
5})
6
7self.addEventListener('activate', event => {
8  event.waitUntil(self.clients.claim())
9})
10
11const map = new Map()
12
13// This should be called once per download
14// Each event has a dataChannel that the data will be piped through
15self.onmessage = event => {
16  // We send a heartbeat every x second to keep the
17  // service worker alive if a transferable stream is not sent
18  if (event.data === 'ping') {
19    return
20  }
21
22  const data = event.data
23  const downloadUrl = data.url || self.registration.scope + Math.random() + '/' + (typeof data === 'string' ? data : data.filename)
24  const port = event.ports[0]
25  const metadata = new Array(3) // [stream, data, port]
26
27  metadata[1] = data
28  metadata[2] = port
29
30  // Note to self:
31  // old streamsaver v1.2.0 might still use `readableStream`...
32  // but v2.0.0 will always transfer the stream through MessageChannel #94
33  if (event.data.readableStream) {
34    metadata[0] = event.data.readableStream
35  } else if (event.data.transferringReadable) {
36    port.onmessage = evt => {
37      port.onmessage = null
38      metadata[0] = evt.data.readableStream
39    }
40  } else {
41    metadata[0] = createStream(port)
42  }
43
44  map.set(downloadUrl, metadata)
45  port.postMessage({ download: downloadUrl })
46}
47
48function createStream (port) {
49  // ReadableStream is only supported by chrome 52
50  return new ReadableStream({
51    start (controller) {
52      // When we receive data on the messageChannel, we write
53      port.onmessage = ({ data }) => {
54        if (data === 'end') {
55          return controller.close()
56        }
57
58        if (data === 'abort') {
59          controller.error('Aborted the download')
60          return
61        }
62
63        controller.enqueue(data)
64      }
65    },
66    cancel (reason) {
67      console.log('user aborted', reason)
68      port.postMessage({ abort: true })
69    }
70  })
71}
72
73self.onfetch = event => {
74  const url = event.request.url
75
76  // this only works for Firefox
77  if (url.endsWith('/ping')) {
78    return event.respondWith(new Response('pong'))
79  }
80
81  const hijacke = map.get(url)
82
83  if (!hijacke) return null
84
85  const [ stream, data, port ] = hijacke
86
87  map.delete(url)
88
89  // Not comfortable letting any user control all headers
90  // so we only copy over the length & disposition
91  const responseHeaders = new Headers({
92    'Content-Type': 'application/octet-stream; charset=utf-8',
93
94    // To be on the safe side, The link can be opened in a iframe.
95    // but octet-stream should stop it.
96    'Content-Security-Policy': "default-src 'none'",
97    'X-Content-Security-Policy': "default-src 'none'",
98    'X-WebKit-CSP': "default-src 'none'",
99    'X-XSS-Protection': '1; mode=block'
100  })
101
102  let headers = new Headers(data.headers || {})
103
104  if (headers.has('Content-Length')) {
105    responseHeaders.set('Content-Length', headers.get('Content-Length'))
106  }
107
108  if (headers.has('Content-Disposition')) {
109    responseHeaders.set('Content-Disposition', headers.get('Content-Disposition'))
110  }
111
112  // data, data.filename and size should not be used anymore
113  if (data.size) {
114    console.warn('Depricated')
115    responseHeaders.set('Content-Length', data.size)
116  }
117
118  let fileName = typeof data === 'string' ? data : data.filename
119  if (fileName) {
120    console.warn('Depricated')
121    // Make filename RFC5987 compatible
122    fileName = encodeURIComponent(fileName).replace(/['()]/g, escape).replace(/\*/g, '%2A')
123    responseHeaders.set('Content-Disposition', "attachment; filename*=UTF-8''" + fileName)
124  }
125
126  event.respondWith(new Response(stream, { headers: responseHeaders }))
127
128  port.postMessage({ debug: 'Download started' })
129}

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.