Fastly JS Compute Runtime

repository·main·Indexed 42 days ago

https://github.com/fastly/js-compute-runtime

A JavaScript SDK and CLI for building and deploying high-performance applications on the Fastly Compute platform. Includes the @fastly/js-compute SDK (v3.46.0) and the js-compute-runtime CLI for compiling JavaScript source into WebAssembly (.wasm) files. Features tools for managing Object Stores via compute-file-server and a comprehensive integration testing framework for validating requests, responses, and logs.

Tokens
240.7K
Snippets
991
Records
1.2K
Agent score
53%

What's inside Fastly JS Compute

  1. How to cancel an interval with clearInterval()

    main

    When you call setInterval(), it returns a non-zero numeric intervalID. To cancel the recurring execution, pass this ID to clearInterval().

    While setInterval() and setTimeout() share the same pool of IDs—meaning clearInterval() and clearTimeout() can technically be used interchangeably—it is best practice to match them correctly (setInterval with clearInterval) to maintain code clarity.

  2. Compare substr(), slice(), and substring()

    main

    Because substr() is non-standard, its behavior differs from slice() and substring() when handling negative lengths or specific indices.

    When str = "01234", a = 1, and l = -2:

    • substr(a, l) returns "" (empty string).
    • slice(a, a + l) returns "123".
    • substring(a, a + l) returns "0".

    When refactoring legacy code that uses substr(), you cannot simply swap it for slice() or substring() without verifying the range of the start and length arguments, as the results may differ.

  3. Configure manualFramingHeaders for Fastly Responses

    main

    The manualFramingHeaders option is a Fastly-specific boolean that determines how the runtime handles HTTP framing headers.

    • false (default/Automatic mode): The runtime automatically generates framing headers. It uses Content-Length if the body size is known beforehand. If the body is streamed (e.g., via a ReadableStream), it automatically uses Transfer-Encoding: chunked.
    • manual mode (true): The runtime honors any Content-Length or Transfer-Encoding headers you provide.

    Warning: When using manual mode, you must ensure headers comply with the HTTP/1.1 specification. If headers are invalid, the runtime reverts to automatic mode and logs a diagnostic message. If Content-Length is provided but does not match the actual body size, the body may be truncated (if too long) or the connection may hang up early (if too short).

    new Response(body, {
      manualFramingHeaders: true,
      headers: {
        'Content-Length': '123'
      }
    });
  4. Use Symbol.matchAll for regular expression matching

    main

    The Symbol.matchAll well-known symbol specifies the method used to return an iterator that yields all matches of a regular expression against a string. This symbol is specifically used by the String.prototype.matchAll() method to identify how the regular expression should perform its matching logic.

    When implementing custom matching behavior for a regular expression object, you can define a method on the RegExp.prototype using this symbol.

  5. Optimize string sorting with Intl.Collator

    main

    When comparing large numbers of strings (such as sorting large arrays), using String.prototype.localeCompare() repeatedly can be less efficient because it may recreate collation logic on every call.

    For better performance, create an Intl.Collator object once and use its .compare() method for the sorting operation.

    // Instead of this:
    largeArray.sort((a, b) => a.localeCompare(b, 'en'));
    
    // Do this for better performance:
    const collator = new Intl.Collator('en');
    largeArray.sort(collator.compare);
  6. Behavior of split() with different separators

    main

    The behavior of split() varies significantly based on the type of separator provided:

    • Non-empty string: Splits the string at every occurrence of the sequence. If the separator is at the start or end, an empty string "" will appear at the first or last position of the array.
    • Empty string (""): Converts the string into an array of its UTF-16 code units.
      • Warning: This does not split by user-perceived characters (grapheme clusters) or Unicode codepoints, which can break surrogate pairs.
      • "".split("") is the only way to produce an empty array [] from a string.
    • Regular Expression:
      • If the regex matches empty strings, the behavior depends on the u (unicode) flag. Using the u flag allows splitting by Unicode codepoints instead of UTF-16 code units.
      • If the regex contains capturing groups, the captured groups are spliced into the output array.
    • Object with Symbol.split: If the separator is an object implementing Symbol.split, that method is called with the target string and limit as arguments.
    • Other values: Any other value is coerced to a string before being used as the separator.
    // Splitting by UTF-16 code units (breaks surrogate pairs)
    "😄😄".split(/(?:)/); // [ "\ud83d", "\ude04", "\ud83d", "\ude04" ]
    
    // Splitting by Unicode codepoints using the 'u' flag
    "😄😄".split(/(?:)/u); // [ "😄", "😄" ]
  7. Use Symbol.asyncIterator for async iteration

    main

    The Symbol.asyncIterator well-known symbol specifies the default async iterator for an object. If an object has this property set, it is considered an async iterable. This allows the object to be consumed using a for await...of loop, which is useful for handling streams or other asynchronous data sources where each iteration might involve a Promise.

    // Example of an async iterable object
    const asyncIterable = {
      [Symbol.asyncIterator]() {
        return {
          next() {
            return Promise.resolve({
              value: 'data',
              done: false
            });
          }
        };
      }
    };
    
    // Consuming the async iterable
    for await (const item of asyncIterable) {
      console.log(item);
    }
  8. Use Symbol.replace for custom string replacement logic

    main

    The Symbol.replace well-known symbol specifies the method used to replace matched substrings within a string. This method is automatically invoked by the String.prototype.replace() method when the first argument passed to replace() is an object that implements this symbol.

    By implementing [Symbol.replace] on a custom object, you can define specialized replacement behavior that goes beyond standard regular expression matching.

  9. Understand the difference between Object.is(), ===, and ==

    main

    When comparing values in the JS Compute Runtime, choose the appropriate equality check based on how you want to handle type coercion and special numeric values:

    Object.is()

    • No coercion: Does not convert types before comparing.
    • NaN handling: Considers NaN equal to NaN.
    • Signed zeros: Distinguishes between +0 and -0 (treats them as different).

    Strict Equality (===)

    • No coercion: Does not convert types before comparing.
    • NaN handling: Considers NaN not equal to NaN.
    • Signed zeros: Treats +0 and -0 as equal.

    Equality Operator (==)

    • Coercion: Applies type coercion to both sides if they are not the same type (e.g., "" == false is true).
    • NaN handling: Considers NaN not equal to NaN.
    • Signed zeros: Treats +0 and -0 as equal.
  10. Use the SimpleCache class for simplified caching

    main

    The SimpleCache class provides a high-level, static interface for interacting with Fastly's Cache. It is designed to simplify the process of inserting and retrieving entries without managing the underlying cache storage mechanics manually.

    Key characteristics:

    • All methods are static; you do not instantiate the class.
    • It is imported from the fastly:cache module.
    • A common pattern is using SimpleCache.getOrSet to implement a 'cache-aside' pattern: attempt to retrieve a value, and if it is missing, execute a provided asynchronous function to generate the value and store it with a specified Time-To-Live (TTL).
    import { SimpleCache } from 'fastly:cache';
    
    // Example of the getOrSet pattern
    const data = await SimpleCache.getOrSet(key, async () => {
      return {
        value: await fetchData(),
        ttl: 60 // Store for 60 seconds
      };
    });