Could not resolve "crypto" (or "node:crypto")?
✘ [ERROR] Could not resolve "node:crypto"
convex/workpools/queues.nobundle.ts:24:27:
24 │ import { createHash } from "node:crypto";
╵ ~~~~~~~~~~~~~
The package "node:crypto" wasn't found on the file system but is built into node. Are you
trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this
error.
✖
It looks like you are using Node APIs from a file without the "use node" directive.
Split out actions using Node.js APIs like this into a new file only containing actions that uses "use node" so these actions will run in a Node.js environment.
For more information see https://docs.convex.dev/functions/runtimes#nodejs-runtime✘ [ERROR] Could not resolve "node:crypto"
convex/workpools/queues.nobundle.ts:24:27:
24 │ import { createHash } from "node:crypto";
╵ ~~~~~~~~~~~~~
The package "node:crypto" wasn't found on the file system but is built into node. Are you
trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this
error.
✖
It looks like you are using Node APIs from a file without the "use node" directive.
Split out actions using Node.js APIs like this into a new file only containing actions that uses "use node" so these actions will run in a Node.js environment.
For more information see https://docs.convex.dev/functions/runtimes#nodejs-runtimeYet, here is my file:
/**
* Deterministically bucket any string into [1..10] using a SHA-256 hash.
* @param input - The string to bucket. Often a Convex document _id
* @param salt - An optional salt to add to the input. Salt lets you "reshuffle"
* buckets without changing inputs (use a constant like a namespace or version).
* @returns A number between 1 and 10
*/
"use node";
import { createHash } from "node:crypto";
/** Deterministically bucket any string into [1..n] */
export function bucket1toN(input: string, salt = "", n: number): number {
// Hash (optionally namespaced via `salt`)
const digest = createHash("sha256")
.update(salt)
.update("\0")
.update(input)
.digest();
// Use 32-bit words; reject the tiny top range to remove modulo bias
const N = 2 ** 32;
const limit = Math.floor(N / n) * n; // 4,294,967,290
for (let i = 0; i < digest.length; i += 4) {
const word = digest.readUInt32BE(i);
if (word < limit) return (word % n) + 1;
}
// Vanishingly rare fallback: rehash deterministically and try again
const d2 = createHash("sha256").update(digest).update("!").digest();
const w2 = d2.readUInt32BE(0);
return ((w2 % limit) % n) + 1;
}/**
* Deterministically bucket any string into [1..10] using a SHA-256 hash.
* @param input - The string to bucket. Often a Convex document _id
* @param salt - An optional salt to add to the input. Salt lets you "reshuffle"
* buckets without changing inputs (use a constant like a namespace or version).
* @returns A number between 1 and 10
*/
"use node";
import { createHash } from "node:crypto";
/** Deterministically bucket any string into [1..n] */
export function bucket1toN(input: string, salt = "", n: number): number {
// Hash (optionally namespaced via `salt`)
const digest = createHash("sha256")
.update(salt)
.update("\0")
.update(input)
.digest();
// Use 32-bit words; reject the tiny top range to remove modulo bias
const N = 2 ** 32;
const limit = Math.floor(N / n) * n; // 4,294,967,290
for (let i = 0; i < digest.length; i += 4) {
const word = digest.readUInt32BE(i);
if (word < limit) return (word % n) + 1;
}
// Vanishingly rare fallback: rehash deterministically and try again
const d2 = createHash("sha256").update(digest).update("!").digest();
const w2 = d2.readUInt32BE(0);
return ((w2 % limit) % n) + 1;
}