algolia / algoliasearch-client-javascript

⚡️ A fully-featured and blazing-fast JavaScript API client to interact with Algolia.
https://www.algolia.com/doc/api-client/javascript/getting-started/
MIT License
1.33k stars 222 forks source link

[bug]: Unable to use search client in cloudflare workers: Cannot bundle Node.js built-in "crypto" #1567

Open TorbjornHoltmon opened 4 weeks ago

TorbjornHoltmon commented 4 weeks ago

Description

Using version 5.10.2 we cannot bundle algolia search client for cloudflare workers since it uses Node built ins.

We tried updating from 5.2.1, which still works fine.

Client

All

Version

5.10.2

Relevant log output

No response

TorbjornHoltmon commented 4 weeks ago

Its hard to point at any code in the repo since everything is generated.

Browsing the code in npm, the import is at:

@algolia/client-search/dist/builds/fetch.js line number 2294 in 5.10.2

image
Haroenv commented 4 weeks ago

In the source code I'm seeing node:crypto being used https://github.com/algolia/algoliasearch-client-javascript/blob/197056452660881835a9f15d8d4b8d68e6a90fca/packages/client-search/builds/fetch.ts#L30, but maybe that would also be problematic. I wonder if the global crypto.subtle.importKey and crypto.subtle.sign instead would work for this build?

As a workaround if you're not using that function, you can in the mean time remove the import of createHmac from node:crypto and your deploy likely works

TorbjornHoltmon commented 4 weeks ago

I took a look at it, and it gets complicated fast, it would require some code to make it work with crypto.subtle Using crypto.subtle would also make the function async.

Some examples of hmac sha256: https://github.com/aws/aws-sdk-js-crypto-helpers/blob/master/packages/sha256-browser/src/webCryptoSha256.ts

https://lukasmurdock.com/web-hmac/

I am sure there are better examples or libraries to use as a starting point. I can run polyfills, but I would prefer that a worker build had no node imports.

EDIT Boils down to an implementation looking something like this: (not tested)

// Utility function to convert string to Uint8Array
const str2buf = (str) => {
  return new TextEncoder().encode(str);
};

// Utility function to convert ArrayBuffer to hex string
const buf2hex = (buffer) => {
  return Array.from(new Uint8Array(buffer))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
};

// Create key material from parent API key
const keyMaterial = await crypto.subtle.importKey(
  'raw',
  str2buf(parentApiKey),
  { name: 'HMAC', hash: 'SHA-256' },
  false,
  ['sign']
);

// Generate HMAC
const signature = await crypto.subtle.sign(
  'HMAC',
  keyMaterial,
  str2buf(queryParameters)
);

// Convert signature to hex and concatenate with query parameters
const combined = buf2hex(signature) + queryParameters;

// Convert to base64 
// In our code base we use our own base64 implementation instead of btoa, I think it has something to do with "URL safe" base64
// btoa also does not exist in node, and since we work in node until we build and deploy its better to not use btoa
return btoa(combined);
lassediercks commented 1 day ago

Because we are thinking about levering the new method browse in order to ssr initial page views on cloudflare I was wondering if this is something algolia is planning on addressing

Haroenv commented 1 day ago

@lassediercks for now the workaround would be to remove the createHmac import with eg. patch-package. Any enduring solution is probably what @TorbjornHoltmon suggested, but that's up to @shortcuts to implement