299 Matching Annotations
  1. Sep 2023
    1. permit streams to be transferred between workers, frames and anywhere else that postMessage() can be used. Chunks can be anything which is cloneable by postMessage(). Initially chunks enqueued in such a stream will always be cloned, ie. all data will be copied. Future work will extend the Streams APIs to support transferring objects (ie. zero copy).

      js const rs = new ReadableStream({ start(controller) { controller.enqueue('hello'); } }); const w = new Worker('worker.js'); w.postMessage(rs, [rs]);

      js onmessage = async (evt) => { const rs = evt.data; const reader = rs.getReader(); const {value, done} = await reader.read(); console.log(value); // logs 'hello'. };

  2. Jul 2023
    1. html <meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width"> ... <picture> <!-- serve WebP to Chrome and Opera --> <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w, /image/thing-800.webp 800w, /image/thing-1200.webp 1200w, /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w" type="image/webp"> <source sizes="(min-width: 30em) 100vw" srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w, /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w, /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w" type="image/webp"> <!-- serve JPEGXR to Edge --> <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w, /image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w, /image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w" type="image/vnd.ms-photo"> <source sizes="(min-width: 30em) 100vw" srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w, /image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w, /image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w" type="image/vnd.ms-photo"> <!-- serve JPEG to others --> <source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w, /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w, /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w"> <source sizes="(min-width: 30em) 100vw" srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w, /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w, /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w"> <!-- fallback for browsers that don't support picture --> <img src="/image/thing.jpg" width="50%"> </picture>

  3. Jun 2023
    1. ```js /* * Response from cache / self.addEventListener('fetch', event => { const response = self.caches.open('example') .then(caches => caches.match(event.request)) .then(response => response || fetch(event.request));

      event.respondWith(response); });

      /* * Response to SSE by text / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';

      if (!isSSERequest) { return; }

      event.respondWith(new Response('Hello!')); });

      /* * Response to SSE by stream / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';

      if (!isSSERequest) { return; }

      const responseText = 'Hello!'; const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); const stream = new ReadableStream({ start: controller => controller.enqueue(responseData) }); const response = new Response(stream);

      event.respondWith(response); });

      /* * SSE chunk data / const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) => ${key}: ${value}) .join('\n') + '\n\n';

      /* * Success response to SSE from SW / self.addEventListener('fetch', event => { const { headers } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';

      if (!isSSERequest) { return; }

      const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) => ${key}: ${value}) .join('\n') + '\n\n';

      const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', };

      const responseText = sseChunkData('Hello!'); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); const stream = new ReadableStream({ start: controller => controller.enqueue(responseData) }); const response = new Response(stream, { headers: sseHeaders });

      event.respondWith(response); });

      /* * Result / self.addEventListener('fetch', event => { const { headers, url } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';

      // Process only SSE connections if (!isSSERequest) { return; }

      // Headers for SSE response const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', }; // Function for formatting message to SSE response const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) => ${key}: ${value}) .join('\n') + '\n\n';

      // Map with server connections, where key - url, value - EventSource const serverConnections = {}; // For each request opens only one server connection and use it for next requests with the same url const getServerConnection = url => { if (!serverConnections[url]) { serverConnections[url] = new EventSource(url); }

      return serverConnections[url];
      

      }; // On message from server forward it to browser const onServerMessage = (controller, { data, type, retry, lastEventId }) => { const responseText = sseChunkData(data, type, retry, lastEventId); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); controller.enqueue(responseData); }; const stream = new ReadableStream({ start: controller => getServerConnection(url).onmessage = onServerMessage.bind(null, controller) }); const response = new Response(stream, { headers: sseHeaders });

      event.respondWith(response); }); ```

    1. ```js self.addEventListener('fetch', event => { const { headers, url } = event.request; const isSSERequest = headers.get('Accept') === 'text/event-stream';

      // We process only SSE connections if (!isSSERequest) { return; }

      // Response Headers for SSE const sseHeaders = { 'content-type': 'text/event-stream', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', }; // Function formatting data for SSE const sseChunkData = (data, event, retry, id) => Object.entries({ event, id, data, retry }) .filter(([, value]) => ![undefined, null].includes(value)) .map(([key, value]) => ${key}: ${value}) .join('\n') + '\n\n'; // Table with server connections, where key is url, value is EventSource const serverConnections = {}; // For each url, we open only one connection to the server and use it for subsequent requests const getServerConnection = url => { if (!serverConnections[url]) serverConnections[url] = new EventSource(url);

      return serverConnections[url];
      

      }; // When we receive a message from the server, we forward it to the browser const onServerMessage = (controller, { data, type, retry, lastEventId }) => { const responseText = sseChunkData(data, type, retry, lastEventId); const responseData = Uint8Array.from(responseText, x => x.charCodeAt(0)); controller.enqueue(responseData); }; const stream = new ReadableStream({ start: controller => getServerConnection(url).onmessage = onServerMessage.bind(null, controller) }); const response = new Response(stream, { headers: sseHeaders });

      event.respondWith(response); }); ```

  4. May 2023
  5. Mar 2023
    1. HTML templating and streaming response library for Worker Runtimes such as Cloudflare Workers.

      js function handleRequest(event: FetchEvent) { return new HTMLResponse(pageLayout('Hello World!', html` <h1>Hello World!</h1> ${async () => { const timeStamp = new Date( await fetch('https://time.api/now').then(r => r.text()) ); return html`<p>The current time is ${timeEl(timeStamp)}.</p>` }} `)); }

    1. Streaming across worker threads

      ```js import { ReadableStream } from 'node:stream/web'; import { Worker } from 'node:worker_threads';

      const readable = new ReadableStream(getSomeSource());

      const worker = new Worker('/path/to/worker.js', { workerData: readable, transferList: [readable], }); ```

      ```js const { workerData: stream } = require('worker_threads');

      const reader = stream.getReader(); reader.read().then(console.log); ```

  6. Feb 2023
  7. Dec 2022
  8. Nov 2022
  9. Sep 2022
    1. I mean something very precise by that so I'm going to try to explain with an analogy imagined imagine you adopt a puppy so 00:15:01 your Duff is puppy and your name um puddles you take puddles home and you give them a little snack and you stick puddles in the cage and you lock the door forever and never open it again and 00:15:14 puddle slips out the rest of his poor life trapped inside this little cage so that's wrong I think most of us would agree that's sadistic that obviously inhumane so well let's ask what exactly 00:15:27 is wrong about that you know what what is inhuman about sticking puddles in the cage well you know we kind of have a notion of what it means to live a full doggy life right dogs have to run around dogs run 00:15:40 around and they sniff other dogs and they pee on things and that's kind of what it means to be a dog they've inherited this the set of capabilities capabilities and traits from their wolf ancestors and we recognize that a dog 00:15:53 has to be allowed the full free expression of its entire range of capabilities by sticking the cage or constraining his range of experience you're not letting him do all the things that dogs can do and this is exactly 00:16:07 what we've done to ourselves we've invented media that severely constrained our range of intellectual experience that of all the many capabilities that 00:16:19 we have all the many ways of thinking that we have we have constrained ourselves to a tiny subset we're not allowed to use our full intellect

      !- analogy : inhumanity of knowledge work - compared to a dog stuck in a cage

    2. you can think about the invention of powerful representations and the invention of powerful media to host powerful 00:11:27 representations as being one of the big drivers of last 2,000 years of the intellectual progress of humanity because each representation allows us to think thoughts that we couldn't think before we kind of 00:11:39 continuously expand our think about territory so you can think of this as tied to you know the grand meta-narrative of the scent of humanity moving away from myth and superstition 00:11:51 and ignorance and towards a deeper understanding of ourselves in the world around us I bring this up explicitly because I think it's good for people to acknowledge the motivation for their 00:12:02 work and this is this story of the intellectual progress of humanity is something that I find very motivating inspiring and is something that I feel like I want to contribute to but I think 00:12:16 that if this if you take this as your motivation you kind of have to be honest with yourself that that there definitely has been ascent we have improved in many 00:12:27 ways but there are also other ways in which our history has not been ascent so we invent technology we media technology 00:12:39 to kind of help us make this this climb but every technology is a double-edged sword every technology enables us has a potential de navels in certain ways while debilitating us in other ways and 00:12:51 that's especially true for representations because the way the reputations work is they draw on certain capabilities that we have so if we go all in in a particular medium like we 00:13:03 did with print so the capabilities that are not well supported in that medium they get neglected in they atrophy and we atrophy I wish I knew who drew the picture 00:13:20 because it's it's a wonderful depiction of what I'm trying to express here and even a little misleading because the person the last stage they're kind of hunched over is tiny rectangle we reach 00:13:31 that stage accomplish that stage with the printing press and cheap paper book based knowledge the invention of paper-based bureaucracy paper-based 00:13:44 working we invented this lifestyle this way of working where to do knowledge work meant to sit at a desk and stare at your little tiny rectangle make a little motions of your hand you know started 00:13:56 out as sitting at a desk staring at papers or books and making little motions with a pen and now it's sitting at a desk staring at a computer screen making a little motions with your on a keyboard but it's basically the same 00:14:08 thing we've this is what it means to do knowledge work nowadays this is what it means to be a thinker it means to be sitting and working with symbols on a little tiny rectangle to the extent that 00:14:20 again it almost seems inseparable you can't separate the representation for what it actually is and and this is basically just an accident of history this is just the way that our media 00:14:32 technology happen to evolve and then we kind of designed a way of knowledge work for that media that we happen to have and I think so I'm going to make the claim that this style of knowledge work 00:14:47 this lifestyle is inhumane

      !- for : symbolic representation - language - the representation is closely tied to the media - a knowledge worker sits at a desk and plays with symbols in a small area all day! - This is actually inhumane when you think about it

  10. Aug 2022
  11. Jul 2022
    1. I was particularly interested in Chris Aldrich’s observation that knowledge workers tend to talk in spatial terms about their work, especially if distracted. Following interruptions by colleagues or phone calls at work, people may frequently ask themselves “where was I?” more frequently than “what was I doing?” This colloquialism isn’t surprising as our memories for visual items and location are much stronger than actions. Knowledge workers will look around at their environments for contextual clues for what they were doing and find them in piles of paper on their desks, tabs in their computer browser, or even documents (physical or virtual) on their desktops.
  12. Jun 2022
  13. May 2022
  14. Apr 2022
  15. Feb 2022
  16. Jan 2022
    1. A note on setting worker-loader’s publicPath with webpack 5

      Webpack 5 introduced a mechanism to detect the publicPath that should be used automatically

      [...]

      Webpack 5 exposes a global variable called __webpack_public_path__ that allows you to do that.

      // Updates the `publicPath` at runtime, overriding whatever was set in the
      // webpack's `output` section.
      __webpack_public_path__ = "/workers/";
      
      const myWorker = new Worker(
        new URL("/workers/worker.js");
      );
      
      // Eventually, restore the `publicPath` to whatever was set in output.
      __webpack_public_path__ = "https://my-static-cdn/";
      
    1. Barry McAree 💙. (2022, January 6). Teachers on these islands will get FFP2(rightly so).Healthcare workers on other parts of these islands..nah!..Surgical masks/spit guards/not PPE,for working with COVID-positive patients risking other patient’s, our own & our family’s health.”Protect the NHS”🤔⁦@CMO_England⁩ https://t.co/OngrD5BBPU [Tweet]. @BarryMcAree. https://twitter.com/BarryMcAree/status/1478883258305814536

  17. Dec 2021
    1. Web Workers

      As of webpack 5, you can use Web Workers without worker-loader.

      Syntax

      new Worker(new URL('./worker.js', import.meta.url));
      
    1. // main.js
      const { RemoteReadableStream, RemoteWritableStream } = RemoteWebStreams;
      (async () => {
        const worker = new Worker('./worker.js');
        // create a stream to send the input to the worker
        const { writable, readablePort } = new RemoteWritableStream();
        // create a stream to receive the output from the worker
        const { readable, writablePort } = new RemoteReadableStream();
        // transfer the other ends to the worker
        worker.postMessage({ readablePort, writablePort }, [readablePort, writablePort]);
      
        const response = await fetch('./some-data.txt');
        await response.body
          // send the downloaded data to the worker
          // and receive the results back
          .pipeThrough({ readable, writable })
          // show the results as they come in
          .pipeTo(new WritableStream({
            write(chunk) {
              const results = document.getElementById('results');
              results.appendChild(document.createTextNode(chunk)); // tadaa!
            }
          }));
      })();
      
      // worker.js
      const { fromReadablePort, fromWritablePort } = RemoteWebStreams;
      self.onmessage = async (event) => {
        // create the input and output streams from the transferred ports
        const { readablePort, writablePort } = event.data;
        const readable = fromReadablePort(readablePort);
        const writable = fromWritablePort(writablePort);
      
        // process data
        await readable
          .pipeThrough(new TransformStream({
            transform(chunk, controller) {
              controller.enqueue(process(chunk)); // do the actual work
            }
          }))
          .pipeTo(writable); // send the results back to main thread
      };
      
    1. What you're trying to do is known as the "Application Shell" architectural pattern.

      The trick is to have your service worker's fetch handler check to see whether an incoming request is a navigation (event.request.mode === 'navigate'), and if so, respond with the cached App Shell HTML (which sounds like /index.html in your case).

      A generic way of doing this would be:

      self.addEventListener('fetch', (event) => {
        if (event.request.mode === 'navigate') {
          event.respondWith(caches.match('/index.html'));
        } else {
          // Your other response logic goes here.
        }
      });
      

      This will cause your service worker to behave in a similar fashion to how you're web server is already configured.

    1. Fetch and modify response properties which are immutable by creating a copy first.
      /**
       * @param {string} headerNameSrc Header to get the new value from
       * @param {string} headerNameDst Header to set based off of value in src
       */
      const headerNameSrc = "foo" //"Orig-Header"
      const headerNameDst = "Last-Modified"
      
      async function handleRequest(request) {
        /**
         * Response properties are immutable. To change them, construct a new
         * Response and pass modified status or statusText in the ResponseInit
         * object. Response headers can be modified through the headers `set` method.
         */
        const originalResponse = await fetch(request)
      
        // Change status and statusText, but preserve body and headers
        let response = new Response(originalResponse.body, {
          status: 500,
          statusText: "some message",
          headers: originalResponse.headers,
        })
      
        // Change response body by adding the foo prop
        const originalBody = await originalResponse.json()
        const body = JSON.stringify({ foo: "bar", ...originalBody })
        response = new Response(body, response)
      
        // Add a header using set method
        response.headers.set("foo", "bar")
      
        // Set destination header to the value of the source header
        const src = response.headers.get(headerNameSrc)
      
        if (src != null) {
          response.headers.set(headerNameDst, src)
          console.log(
            `Response header "${headerNameDst}" was set to "${response.headers.get(
              headerNameDst,
            )}"`,
          )
        }
        return response
      }
      
      addEventListener("fetch", event => {
        event.respondWith(handleRequest(event.request))
      })
      
    1. shinydoc. (2021, December 12). I love how I, an actual GP...who was involved in the initial covid vaccination programme ...has to tune in at 8pm with the public to find out that apparently we are vaccinating the entire adult population with boosters by the end of the year [Tweet]. @irishayesha. https://twitter.com/irishayesha/status/1470123478221303810

    1. // To know the maximum numbers of thread that can be used
      // on user’s system, use
      
      window.navigator.hardwareConcurrency property.
      
  18. Nov 2021
  19. Oct 2021
    1. Kenneth Baillie. (2021, October 27). When a healthcare system fails, increasing numbers of people suffer and die needlessly. That’s all. If you aren’t a patient or staff, you don’t see it. But this is happening, now, all over the UK. 2/n [Tweet]. @kennethbaillie. https://twitter.com/kennethbaillie/status/1453422360795680769