reference files from blob object url when path from root might change

I’m generating the contents of a container (iframe) from data stored/generated in the hosting page.

parent.App.Plugins.GetInstance(obj).Compile(obj.content).then((html) => {
    var blob = new Blob([html],{type:"text/html"});
    var url = URL.createObjectURL(blob);
    window.location.href = url;
});

if the html of the page inside the blob wants to load content relative to the path of the parent (host) page, what is the relative url to use?

for instance, if inside the generated html there was

<html>
<head>
  <script src="https://unpkg.com/jquery/dist/jquery.min.js"></script>
  <script src="./modules/loader.js" type="module"></script>
  <script src="js/main.js"></script>
</head>
<body>
  <img src="https://images.unsplash.com/photo-1472740378865-80aab8e73251">
  <img src="/images/some-file.jpg">
</body>
</html>

the external references to files will work (https://blah etc), not files served from my server (js/main.js, /images/file.jpg etc).

Does a hardcode float num a.bc… (0<=n<100 decimals,MIN_VALUE<=a.bc…<=MAX_VALUE) always equals to Number.parseFloat((a.bc…).toFixed(n))?

For example, I know 0.12,ie:

const a=0.12;

rounds to something due to float number errors. However, I’m not asking why the error happens and I don’t care the errors currently, instead I want to know if the hardcode form always equals to the value after adding toFixed following Number.parseFloat.

Consider the following:

console.log(0.12 == Number.parseFloat(0.12.toFixed(2)));
console.log(987.123 == Number.parseFloat(987.123.toFixed(3)));
console.log(98765.12345678 == Number.parseFloat(98765.12345678.toFixed(8)));

the hardcode values above equals to the value after adding toFixed(n) and Number.parseFloat.

However, I don’t know if other cases are also true. So my question is, for a float number a.bc… with n decimals 0-100 and value between MIN_VALUE and MAX_VALUE, does its original hardcode form rounds to the value always the same as Number.parseFloat(a.bc.toFixed(n)) ? ie:

console.log(a.bc... == Number.parseFloat((a.bc...).toFixed(n))); //"bc..." means n decimals, 0 <= n <= 100 , MIN_VALUE <= a.bc <= MAX_VALUE

does the code above always true? If it does, does it also true for -a.bc..? And if it doesn’t, what is the reason?

How to mock the same API call twice, but with different query parameters?

I am working in React and Typescript.

Using React testing Library, I am trying to test one of my components.
Within that component I make a call to the same API twice, but with different parameters.

In a separate file, I have something along

  getInfo(
    isOverlow
  ): {
    const requestOptionsn= {
      headers: {
        accept: "application/json",
      },
      method: "GET",
      signal,
    };

   new Promise((resolve, reject) => {
  return fetch('sample-url.com/someparam?isOverflow=${isOverflow}').then(response => {
    if (response.ok) {
      resolve(response)
    } else {
      reject(new Error('error'))
    }
  }, error => {
    reject(new Error(error.message))
  })
})
  },

in the component I am calling the function, I have two calls

getInfo(true)
.then(response => setIsOverflowResponse(repsonse))
.catch(error => setError(error))

getInfo(false)
.then(response => setNonOverflowResponse(repsonse))
.catch(error => setError(error))

how can I mock the response for these two calls separately?

the function

I tried

jest.spyOn(SbiService, ‘getInfo’)
.mockResolvedValueOnce(isOVerflowResponse)
.mockResolvedValueOnce(nonOverflowResponse);

bit it doesnt seem to work

Looping over items in an array returns the whole array instead of an items

I’m looping over an array with 2 items, where it should be giving me one item in the loop. However it just gives the entire array after looping.

[
  {
    status: 200,
    statusText: 'OK',
    headers: AxiosHeaders {
      date: 'Fri, 17 Mar 2023 00:45:58 GMT',
      'content-type': 'application/json',
      'transfer-encoding': 'chunked',
      connection: 'close',
      'set-cookie': [Array],
      'strict-transport-security': 'max-age=31536000; includeSubDomains; preload',
      'x-ratelimit-bucket': 'f7ead6a7674e5a323d93786263b66cb1',
      'x-ratelimit-limit': '50',
      'x-ratelimit-remaining': '49',
      'x-ratelimit-reset': '1679013958.858',
      'x-ratelimit-reset-after': '0.019',
      via: '1.1 google',
      'alt-svc': 'h3=":443"; ma=86400, h3-29=":443"; ma=86400',
      'cf-cache-status': 'DYNAMIC',
      'report-to': '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=xKTf0hVCUg0G348XsfiUpPVF2S%2FRtHec4bcEjhc2ZHidVhzQ%2F8aZz1Gxu6IOlbC925BGHbB3cLCMHWyYyW4fOq9qmEdBHP3OVkm0kiQEyNOvtaoP%2BfslLDUdLgrn"}],"group":"cf-nel","max_age":604800}',
      nel: '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}',
      'x-content-type-options': 'nosniff',
      server: 'cloudflare',
      'cf-ray': '7a91375a5ce4ad33-ATL'
    },
    config: {
      transitional: [Object],
      adapter: [Array],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [AxiosHeaders],
      baseURL: 'https://discord.com/api/v10',
      method: 'get',
      url: '/channels/1071951518946828429',
      data: undefined
    },
    request: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: true,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: true,
      socket: [TLSSocket],
      _header: 'GET /api/v10/channels/1071951518946828429 HTTP/1.1rn' +
        'Accept: application/json, text/plain, */*rn' +
        'Authorization: Bot +
        'User-Agent: axios/1.3.4rn' +
        'Accept-Encoding: gzip, compress, deflate, brrn' +
        'Host: discord.comrn' +
        'Connection: closern' +
        'rn',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/api/v10/channels/1071951518946828429',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'discord.com',
      protocol: 'https:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    data: {
      id: '1071951518946828429',
      last_message_id: '1086060000423706675',
      type: 0,
      name: 'free-games',
      position: 18,
      flags: 0,
      parent_id: '753760260266655826',
      topic: null,
      guild_id: '753760255900516402',
      permission_overwrites: [Array],
      rate_limit_per_user: 0,
      nsfw: false
    }
  },
  {
    status: 200,
    statusText: 'OK',
    headers: AxiosHeaders {
      date: 'Fri, 17 Mar 2023 00:45:59 GMT',
      'content-type': 'application/json',
      'transfer-encoding': 'chunked',
      connection: 'close',
      'set-cookie': [Array],
      'strict-transport-security': 'max-age=31536000; includeSubDomains; preload',
      'x-ratelimit-bucket': 'f7ead6a7674e5a323d93786263b66cb1',
      'x-ratelimit-limit': '50',
      'x-ratelimit-remaining': '49',
      'x-ratelimit-reset': '1679013959.102',
      'x-ratelimit-reset-after': '0.019',
      via: '1.1 google',
      'alt-svc': 'h3=":443"; ma=86400, h3-29=":443"; ma=86400',
      'cf-cache-status': 'DYNAMIC',
      'report-to': '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=%2BMv3IziW0ZzOch3ojFNTbd1MUrjAlXAQZoWuHmCbSPqVYGNB3h2Rt%2Fdc7z1B%2BIpSk%2BQOJkE0j%2By%2BXHZFjD4PejweFa4ywleT1aZdbKRe%2B4PlZGa7yhU98697pOH0"}],"group":"cf-nel","max_age":604800}',
      nel: '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}',
      'x-content-type-options': 'nosniff',
      server: 'cloudflare',
      'cf-ray': '7a91375beb58b032-ATL'
    },
    config: {
      transitional: [Object],
      adapter: [Array],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [AxiosHeaders],
      baseURL: 'https://discord.com/api/v10',
      method: 'get',
      url: '/channels/1071972385831526420',
      data: undefined
    },
    request: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: true,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: true,
      socket: [TLSSocket],
      _header: 'GET /api/v10/channels/1071972385831526420 HTTP/1.1rn' +
        'Accept: application/json, text/plain, */*rn' +
        'Authorization: Bot +
        'User-Agent: axios/1.3.4rn' +
        'Accept-Encoding: gzip, compress, deflate, brrn' +
        'Host: discord.comrn' +
        'Connection: closern' +
        'rn',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/api/v10/channels/1071972385831526420',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'discord.com',
      protocol: 'https:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    data: {
      id: '1071972385831526420',
      last_message_id: '1086060739615273091',
      type: 0,
      name: 'free-games',
      position: 32,
      flags: 0,
      parent_id: '983858995691405322',
      topic: null,
      guild_id: '983858506140639352',
      permission_overwrites: [Array],
      rate_limit_per_user: 0,
      nsfw: false
    }
  },
]

There are two items this array and I’m trying to loop over them using this code:

    /**
     * @param {Interaction[]} channels
     */
    async sendFreeGames(...channels) {
        const games = await this.#createEpicFreeGamesEmbeds();
        const buttons = await this.#createEpicFreeButtons();

        if (games.length === 0) return;

        for (const channel of channels) {
            // @ts-ignore
            const discordAxios = axios.create({ baseURL: 'https://discord.com/api/v10', headers: { Authorization: `Bot ${process.env.DISCORD_TOKEN}`,},});
            console.log(channel.data.type)
            discordAxios.post(`/channels/${channel.data.id}/messages`, { embeds: games, components: buttons });
        }
    }
}

However when looping and accessing channel it just returns the whole array instead of each item in the array leading to channel.data.typebeing undefined but channel[0].data.type being valid. I’ve been trying to solve this for 4 hours with no luck. Any help would be appreciated.

Showing Sezzle Widget text after Afterpay Widget text

I am trying to get the “Sezzle text” below the “Afterpay” widget text

Please refer to the below image

Screenshot

I have Added this script in the script tag

<script>
  ymaps.ready(function() {

    if (ymaps.geolocation.country == "New Zealand") {
      $('#afterpay-block').css({"display": "block"});
    } 
         else if (ymaps.geolocation.country == "Australia") { 
      $('#afterpay-block').css({"display": "block"});
    }
           else if (ymaps.geolocation.country == "United States") { 
     
              $('.sezzle-checkout-button').css({"display": "none"});
             $('#afterpay-block').css({"display": "block"});
              $('.sezzle-button-text').css({"display": "block"});
             $('#shopify-zip-prod-widget').css({"display": "none"});
             $('#klarna').css({"display": "none"});
             $('#klarna-text').css({"display": "none"});
             
           
            
    }
    else {
      $('#afterpay-block').css({"display": "none"});
    }


  });
</script>

I am aware I could not hide a block and display it again in the same block. I was trying to hide it before “Afterpay Widget” and then show the “Sezzle” widget after the “Afterpay Widget”. But obviosly it is not working

Here is my HTML code

<div class="container">
  <div class="jumbotron">
    <div style="display: none" id="afterpay-block">
        <div class="sezzle-hide">
        <!-- Begin Afterpay Liquid Snippet for Shopify v2.2.0 -->
        {% assign afterpay_minimum_value_in_cents = 10000 %}
        {% assign afterpay_maximum_value_in_cents = 100000 %}
        {% assign afterpay_logo_theme = 'colour' %}
        {% assign afterpay_count_eligible_variants = 0 %}
        {% assign afterpay_supported_currencies = 'AUD, CAD, USD, NZD' | split: ', ' %}
        {% if afterpay_supported_currencies contains shop.currency %}
          {% for afterpay_variant in product.variants %}
            {% if afterpay_variant.available and afterpay_variant.price >= afterpay_minimum_value_in_cents and afterpay_variant.price <= afterpay_maximum_value_in_cents %}
              {% assign afterpay_count_eligible_variants = afterpay_count_eligible_variants | plus: 1 %}
            {% endif %}
          {% endfor %}
        {% endif %}
        <script type="text/javascript">
        (
          function(i,s,o,g,r,a,m){
            i['GoogleAnalyticsObject']=r;
            i[r]=i[r]||function(){
              (i[r].q=i[r].q||[]).push(arguments);
            },i[r].l=1*new Date();
            a=s.createElement(o),m=s.getElementsByTagName(o)[0];
            a.async=1;
            a.src=g;
            m.parentNode.insertBefore(a,m);
          }
        )(window,document,'script','https://www.google-analytics.com/analytics.js','afterpay_ga');
        afterpay_ga('create', 'UA-105673112-1', 'auto');
        afterpay_ga('send', 'event', 'Liquid Snippet', 'Report Version', '2.2.0');
        afterpay_ga('send', 'event', 'Theme', 'Report Name', {{ theme.name | json }});
        afterpay_ga('send', 'event', 'Shop', 'Report Currency', {{ shop.currency | json }});
        </script>
        {% if afterpay_count_eligible_variants > 0 %}
          <p class="afterpay-paragraph" style="display:{% if product.selected_or_first_available_variant.available and product.selected_or_first_available_variant.price >= afterpay_minimum_value_in_cents and product.selected_or_first_available_variant.price <= afterpay_maximum_value_in_cents %}block{% else %}none{% endif %};" data-product-id="{{ product.id }}">
            Make 4 interest-free payments of
            <strong><span class="afterpay-instalments">${{ product.selected_or_first_available_variant.price | divided_by: 4.0 | round | money_without_currency }}&nbsp;{{ shop.currency }}</span></strong>
            fortnightly with 
            <a style="display:inline-block; margin-bottom:10px;" href="https://www.afterpay.com/terms" target="_blank">
              <img style="vertical-align:middle;" src="https://static.afterpay.com/integration/product-page/logo-afterpay-{{ afterpay_logo_theme }}.png" srcset="https://static.afterpay.com/integration/product-page/logo-afterpay-{{ afterpay_logo_theme }}.png 1x, https://static.afterpay.com/integration/product-page/logo-afterpay-{{ afterpay_logo_theme }}@2x.png 2x, https://static.afterpay.com/integration/product-page/logo-afterpay-{{ afterpay_logo_theme }}@3x.png 3x" width="75" height="15" alt="Afterpay" />
              <span style="font-size:12px;"><u></u></span>
            </a>
          <span id="klarna-text">or with</span> 
            <a style="display:inline-block; margin-bottom:10px;" href="yourwebsite.com/pages/buy-now-pay-later-australia" target="_blank">
              <img id="klarna" style="vertical-align:middle;" src="https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg" srcset="https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg" alt="Klarna" />
              <span style="font-size:12px;"><u></u></span>
            </a>
          </p>
          {% if product.price_varies or afterpay_count_eligible_variants < product.variants.size %}
            <script type="text/javascript">
              if (typeof Afterpay === 'undefined') {
                var Afterpay = {products:[]};
                Afterpay.loadScript = function(url, callback) {
                  var script = document.createElement('script');
                  script.type = 'text/javascript';
                  if (script.readyState) { // I.E.
                    script.onreadystatechange = function() {
                      if (script.readyState == 'loaded' || script.readyState == 'complete') {
                        script.onreadystatechange = null;
                        callback();
                      }
                    };
                  } else { // Others
                    script.onload = function() {
                      callback();
                    };
                  }
                  script.src = url;
                  document.getElementsByTagName('head')[0].appendChild(script);
                };
                Afterpay.init = function($) {
                  $('body').on('change', $('form[action^="/cart/add"]'), function(event) {
                    var $form = $(event.target).closest('form');
                    var $afterpay_paragraph = null;
                    var should_show_instalments = false;
                    var selected_variant_id = parseInt($('input[name=id]:checked, select[name=id], input[name=id], hidden[name=id]', $form).val(), 10);
                    if (isNaN(selected_variant_id)) {
                      var $variant_options = $form.find('input[name=id][value], select[name=id] option[value], hidden[name=id][value]').filter(function(index, element) {
                        return $(element).attr('value').match(/^[1-9][0-9]*$/);
                      });
                      $variant_options.each(function(index, element) {
                        $.each(Afterpay.products, function(product_index, product) {
                          $.each(product.variants, function(variant_index, variant) {
                            if (variant.id == $(element).val()) {
                              $afterpay_paragraph = $('.afterpay-paragraph[data-product-id=' + product.id + ']');
                              return false;
                            }
                          });
                          if ($afterpay_paragraph !== null) {
                            return false;
                          }
                        });
                        if ($afterpay_paragraph !== null) {
                          return false;
                        }
                      });
                    } else if (selected_variant_id > 0) {
                      $.each(Afterpay.products, function(product_index, product) {
                        $.each(product.variants, function(variant_index, variant) {
                          if (variant.id == selected_variant_id) {
                            $afterpay_paragraph = $('.afterpay-paragraph[data-product-id=' + product.id + ']');
                            if (variant.available && variant.price >= {{ afterpay_minimum_value_in_cents }} && variant.price <= {{ afterpay_maximum_value_in_cents }}) {
                              should_show_instalments = true;
                              $afterpay_paragraph.find('.afterpay-instalments').html('$' + (Math.round(variant.price / 4) / 100).toFixed(2) + '&nbsp;{{ shop.currency }}');
                            }
                          }
                        });
                      });
                    }
                    if ($afterpay_paragraph !== null) {
                      if (should_show_instalments) {
                        $afterpay_paragraph.show();
                      } else {
                        $afterpay_paragraph.hide();
                      }
                    }
                  });
                };
                if (typeof jQuery === 'undefined' || parseFloat(jQuery.fn.jquery) < 1.7) {
                  Afterpay.loadScript('https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js', function() {
                    var jQuery_1_12_4 = jQuery.noConflict(true);
                    Afterpay.init(jQuery_1_12_4);
                  });
                } else {
                  Afterpay.init(jQuery);
                }
              }
              Afterpay.products.push({{ product | json }});
            </script>
          
                      
            {% endif %}  
         
          {%include 'zip-widget'%} 
          
          {% endif %}
        <!-- End Afterpay Liquid Snippet for Shopify v2.2.0 -->
          
                                        <!-- Zip WIDGET -->

<style>
  
  .container {
    text-align: center;
  }
  
  #shopify-zip-prod-widget > iframe { 
    height: 60px!important;
    max-width: min-content!important;
  }
  
  .sezzle-checkout-button.sezzle-left {
    float: none;
    -ms-flex-pack: left;
    justify-content: left;
    background: #fbfbfb;
}
  
  .sezzle-checkout-button-wrapper .sezzle-button-text.sezzle-thin {
    max-width: 100%;
    width: unset;
    line-height: 28px;
    margin-top: 10px;
}

.sezzle-button-text.sezzle-left {
    text-align: center;
    margin: 0;
}
</style>
<span style="cursor:pointer" id="shopify-zip-prod-widget" data-zm-asset="productwidget" data-zm-widget="popup" data-zm-popup-asset="termsdialog"></span>
<!-- end Zip WIDGET -->                  
        </div>
    </div>
  </div>
</div>

For United State customers it should be only showing “Afterpay” and “Sezzle”

That is why I hide other Buy Now, Pay Later options

If someone can help me to get the sezzle-widget below the “Afterpay” widget text it would be greately appreciated.

I am working on Shopify by the way and the theme file I am editing is afterpay-block.liquid

The previous developer has integrated all the Buy now Pay later options in this template file

Finding (and highlighting) URL text fragments in a web page

I’d like to use JavaScript to implement the WIGC spec for text selection from URL fragments on a webpage.

From the linked document:

Allow specifying text as part of the URL fragment:

https://example.com#:~:text=prefix-,startText,endText,-suffix

Using this syntax

:~:text=[prefix-,]textStart[,textEnd][,-suffix]

         context  |-------match-----|  context

(Square brackets indicate an optional parameter)

Navigating to such a URL will cause the browser to indicate the first instance of the matched text.

This is currently implemented in Chrome, but I would like to make it work in other browsers too if someone lands on one of my pages with such a fragment.

Here’s an example link that doesn’t use prefix or suffix:

https://example.com/#:~:text=This%20domain%20is%20for%20use%20in%20illustrative%20examples%20in%20documents

And one that uses both:

https://example.com/#:~:text=You%20may%20use-,this,-domain%20in%20literature

(you can see the highlights if you open these links with a recent version of Chrome).

I’d like to be able to fallback nicely if e.g. I can’t find the textStart or textEnd but can find the prefix or suffix

I know that I can use Ranges to select and highlight text on the page, and I assume this is the best approach. It seems that Ranges require a node and a text- or child-based offset, so the question becomes how to find the relevant node and offset.

I have played around with window.find, but I’m not sure how to turn that into a node/offset pair, plus it has additional behaviour in some browsers that I don’t really want (lack of control over the highlights, scrolling around, etc.).

google-cloud/logging-winston TypeError: The “original” argument must be of type Function

I am trying to add @google-cloud/logging to my VueJS project. I followed these https://www.npmjs.com/package/@google-cloud/logging and https://cloud.google.com/logging/docs/samples/logging-winston-quickstart very closely. I also searched for 3 days online.

I am getting "TypeError: The "original" argument must be of type Function"

How can this line const {LoggingWinston} = require('@google-cloud/logging-winston') cause the issue? This line is directly from the documentation. Any advice or solutions? Thank you!

const winston = require('winston');

// error here:
const {LoggingWinston} = require('@google-cloud/logging-winston');
// code does not pass any further


const gcloudWinston = new LoggingWinston();

const logConfiguration = {
    format: winston.format.json(),
    'transports': [
        new winston.transports.Console(),
        // Add Cloud Logging
        gcloudWinston,
    ]
};

const logger = winston.createLogger(logConfiguration);

export default { logger }

iterate over a sparse array, skip over undefined, but keep the original index

I have a sparse array like this

> arr = ["a",,"b"]
[ 'a', <1 empty item>, 'b' ]

I want to iterate over the values, skipping the undefineds, but keep the original index.

From the example array above I want [0,"a"] then [2,"b"].

When I just naively loop over the array I will get the undefineds

> for(let [i,v] of arr.entries()) {console.log(i,v)}
0 a
1 undefined
2 b

I learned that forEach skips the undefineds

> arr.forEach((v,i)=>console.log(i,v))
0 a
2 b

But this will not work if I want to yield from the loop. It seems to be not possible to yield from inside the function.

I could filter the undefineds but then I lose the index.

> for(let [i,v] of arr.filter(v=>v!==undefined).entries()) {console.log(i,v)}
0 a
1 b

It seems I have to do it manually

> for(let [i,v] of arr.entries()) { if(v!==undefined) {console.log(i,v)} }
0 a
2 b

Or this abomination

> for(let [i,v] of [...arr.entries()].filter(([i,v])=>v!==undefined)) {console.log(i,v)}
0 a
2 b

Is there a better/more concise way?

The arrays are not huge. Performance is not really an issue.


Background

The sparse array is created like this

let arr = [];
for(let [i,v] of somecalculations(somedata)) {
    arr[i] = v;
}

There is no constraint of how the indexes come out (other than that it stays in sane dimensions). I can get something like this ["a","b","c"] or like this [,,,,,"a"] or like this [].

And to make things more interesting I do not just iterate over the array but iterate pairwise (the [1,2],[2,3] variant)

function *pairwise(arr) {
    let it = [...arr.entries()].filter(([i,v])=>v!==undefined).values();
    let curr = it.next();
    let next = it.next();
    while(!next.done) {
        yield [curr.value, next.value];
        curr = next;
        next = it.next();
    }
}

For an array like this ["a",,"b","c"] the output is [[0,"a"],[2,"b"]] and [[2,"b"],[3,"c"]]. If there is just one element in the array then there is no output.

And consume like this

for(let [[i1,v1],[i2,v2]] of pairwise(arr)) {
    // do calculation on pair
    // aggregate results
}
// further calculations on results

AWS AppSync – Javascript Resolver – DynamoDB Batch Query

I want to write a batch query that will return all of the items from the dynamo db who’s ReviewStatus is equal to one of the statuses in the status array


i know that if the status was a string i could write

import { util } from '@aws-appsync/utils';

export function request(ctx) {
  const { limit = 20, nextToken, status } = ctx.arguments;
  const index = 'ReviewStatus-index';
  const query = JSON.parse( 
    util.transform.toDynamoDBFilterExpression({ 
        ReviewStatus: { 
             eq: status 
        } 
    })
  );
  
  return { operation: 'Query', index, query, limit, nextToken };
}

export function response(ctx) {
  const { items, nextToken } = ctx.result;
  return { items, nextToken };
}

but what about if status was an array ?

I thought I tried many different queries but none worked

Ideally something like the code below would be nice if it worked

import { util } from '@aws-appsync/utils';

export function request(ctx) {
  const { limit = 20, nextToken, status } = ctx.arguments;
  const index = 'ReviewStatus-index';
  const query = JSON.parse( 
    util.transform.toDynamoDBFilterExpression({
        "or": status.map( s => ({ ReviewStatus: { eq: s } }))
    })
  );
  
  return { operation: 'Query', index, query, limit, nextToken };
}

export function response(ctx) {
  const { items, nextToken } = ctx.result;
  return { items, nextToken };
}

i would even appreciate any resources that could help me figure this out
i looked in the documentation but they only have information about batch queries in VTL templates not in Javascript templates

How to use OrbitControls in Three.js Library? I’m trying to use it but I’ve got some errors

I’m trying to use three.js library to show and manage a 3D object, rotate it and some other things. Here is my code, but I’ve got an error with using OrbitControls. here is my error:

THREE.OrbitControls is not a constructor at init

This is my code:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <link rel="stylesheet" type="text/css" href="assets/styles/style.css" />
        <script type="importmap">
            {
                "imports": {
                    "three": "./assets/scripts/three.js-master/build/three.module.js",
                    "three/examples/jsm/controls/OrbitControls": "./assets/scripts/three.js-master/examples/jsm/controls/OrbitControls.js",
                    "three/examples/jsm/loaders/GLTFLoader": "./assets/scripts/three.js-master/examples/jsm/loaders/GLTFLoader.js"
                }
            }
        </script>
    </head>
    <body>
        <script type="module">
            import * as THREE from "three";
            import { OrbitControls } from "https://threejs.org/examples/jsm/controls/OrbitControls.js";
            import { GLTFLoader } from "https://threejs.org/examples/jsm/loaders/GLTFLoader.js";

            let scene, camera, renderer;

            function init() {
                scene = new THREE.Scene();
                console.log(scene);
                scene.background = new THREE.Color(0xdddddd);

                camera = new THREE.PerspectiveCamera(
                    40,
                    window.innerWidth / window.innerHeight,
                    1,
                    5000
                );
                camera.rotation.y = (45 / 180) * Math.PI;
                camera.position.x = 800;
                camera.position.y = 100;
                camera.position.z = 1000;

                controls = new THREE.OrbitControls(camera);
                controls.addEventListener("change", renderer);

                hlight = new THREE.AmbientLight(0x404040, 100);
                scene.add(hlight);

                directionalLight = new THREE.DirectionalLight(0xffffff, 100);
                directionalLight.position.set(0, 1, 0);
                directionalLight.castShadow = true;
                scene.add(directionalLight);
                light = new THREE.PointLight(0xc4c4c4, 10);
                light.position.set(0, 300, 500);
                scene.add(light);
                light2 = new THREE.PointLight(0xc4c4c4, 10);
                light2.position.set(500, 100, 0);
                scene.add(light2);
                light3 = new THREE.PointLight(0xc4c4c4, 10);
                light3.position.set(0, 100, -500);
                scene.add(light3);
                light4 = new THREE.PointLight(0xc4c4c4, 10);
                light4.position.set(-500, 300, 500);
                scene.add(light4);

                renderer = new THREE.WebGLRenderer({ antialias: true });
                renderer.setSize(window.innerWidth, window.innerHeight);
                document.body.appendChild(renderer.domElement);

                let loader = new THREE.GLTFLoader();
                loader.load(
                    "./assets/Model_3D/Car/scene.gltf",
                    function (gltf) {
                        car = gltf.scene.children[0];
                        car.scale.set(0.5, 0.5, 0.5);
                        scene.add(gltf.scene);
                        animate();
                    }
                );
            }
            function animate() {
                renderer.render(scene, camera);
                requestAnimationFrame(animate);
            }
            init();
        </script>
    </body>
</html>

Help me to debug this code and complete it. I want to make a 3D object and rotate or resize that object, I’m following a tutorial from this link to do that but I’ve got errors. Link is here:
3D Model Using Three js

Help me to debug and complete this code.

simple Hello World Node API fails to connect

I am trying to create a simple hello world route with nodejs and express. When I run node index.js, I get listening on port 3000 in the console, but when i go to http://localhost:3000/helloworld, nothing works. It keeps trying to connect.

I am probably missing something somewhere in the routes.

index.js

const express = require("express");
const app = express();
const helloworld_route = require("./routes/helloworld");

const dotenv = require("dotenv");
dotenv.config({ path: "../.env.sample" });
const port = process.env.APP_SERVER_PORT;

app.use(express.json);

app.listen(port, () => {
  console.log(`Listening on ${process.env.APP_SERVER_PORT}`);
});


app.use("/", helloworld_route);

controllers/hello.js

const helloTest = (req, res) => {
  res.send("Hello World!");
  console.log("hello");
};

const homepage = (req, res) => {
  res.send(
    "Hello World API version " +
      " in " +
      process.env.NODE_ENV +
      " is up & running"
  );
};
module.exports = {
  helloTest,
  homepage,
};

routes.helloworld.js

const express = require("express");
const router = express.Router();

const HelloWorld = require("../controllers/hello");


router.get("/helloworld", HelloWorld.helloTest);
router.get("/home", HelloWorld.homepage);

module.exports = router;

Replacing IPs in MongoDB

I have a nested schema for mongoDB collection. It looks smth like this:

{
   "_id":"61d99bf5544f4822bd963bda0a9c213b",
   "execution": {
        "test_split":0,
        "artifacts":{
            "9ed39_output": {
                "uri": "http://100.com/somefile"
            },
            "8d777_output":{
                "uri": "http://100.com/anotherfile"
            }
        }
    }
}

Notice that artifacts keys are unique. I need to replace the ip stored in uri (in this dummy example “100”) with some other ip (lets say “200”). I need to write some find and foreach. But I am really confused by variable keys under “artifacts”. Any help is much appreciated. Thanks

Database is part of ClearML. There is an example how to change location for models: https://clear.ml/docs/latest/docs/faq/#relocate_models. But I couldnt succeed adapting that rather simple schema to this usecase.

Redux hydration issue, data getting missed after app restart

Currently one of my app’s reducer getting reset to default value after restarting the app, also some of the data getting persisted in the same reducer. I am unable to understand how this is possible.
I checked all the actions which is getting called after and before restart but doesn’t find any clue as there is no action getting dispatched to the reducer but still data getting updated.
This only happens with android and not ios