/** * Browser-backed implementation of Effect's Crypto service. * * This module provides a `Crypto.Crypto` layer backed by the Web Crypto API. * The {@link WebCrypto} context reference defaults to `globalThis.crypto`, so * browser programs can use the standard implementation while tests or embedded * runtimes can provide their own `Crypto` object. * * @since 1.0.0 */ import * as Context from "effect/Context" import * as EffectCrypto from "effect/Crypto" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as PlatformError from "effect/PlatformError" /** * Provides Browser Web Crypto APIs used by the Crypto service implementation. * * **When to use** * * Use to override the browser `Crypto` object used by the platform crypto * layer. * * @category references * @since 1.0.0 */ export const WebCrypto = Context.Reference("@effect/platform-browser/Crypto/WebCrypto", { defaultValue: () => globalThis.crypto }) /** * Layer that directly interfaces with the Web Crypto API. * * **When to use** * * Use to provide cryptographic randomness, UUID generation, and digest * operations in browser runtimes backed by `globalThis.crypto`. * * **Details** * * Random bytes are produced with `crypto.getRandomValues`. Digests are computed * with `crypto.subtle.digest` and returned as `Uint8Array` values. * * **Gotchas** * * The layer dies if the Web Crypto object is unavailable. Digest operations * fail with `PlatformError` when `crypto.subtle.digest` is unavailable or the * browser rejects the digest request. * * @category layers * @since 1.0.0 */ export const layer: Layer.Layer = Layer.effect( EffectCrypto.Crypto, Effect.gen(function*() { const crypto = yield* WebCrypto if (!crypto) { return yield* Effect.die(new Error("Web Crypto API is not available")) } const randomBytes = (size: number): Uint8Array => { const bytes = new Uint8Array(size) crypto.getRandomValues(bytes) return bytes } const digest: EffectCrypto.Crypto["digest"] = (algorithm, data) => { if (typeof crypto.subtle.digest !== "function") { return Effect.fail(PlatformError.systemError({ module: "Crypto", method: "digest", _tag: "Unknown", description: "crypto.subtle.digest is not available" })) } return Effect.map( Effect.tryPromise({ try: () => crypto.subtle.digest(algorithm, new Uint8Array(data)), catch: (cause) => PlatformError.systemError({ module: "Crypto", method: "digest", _tag: "Unknown", description: "Could not compute digest", cause }) }), (buffer) => new Uint8Array(buffer) ) } return EffectCrypto.make({ randomBytes, digest }) }) )