EdgeAIG's picture
download
raw
5.44 kB
/**
* Defines a platform-independent service for cryptographic operations.
*
* Runtime packages provide concrete implementations backed by the host
* platform's cryptography APIs. This module defines the service interface and a
* constructor from random-byte and digest primitives. The service provides
* secure random bytes and numbers, UUIDv4 and UUIDv7 generation, shuffling, and
* SHA message digests.
*
* @since 4.0.0
*/
import * as Context from "./Context.js";
import * as Effect from "./Effect.js";
import * as PlatformError from "./PlatformError.js";
const TypeId = "~effect/platform/Crypto";
/**
* Service tag for platform cryptography.
*
* **When to use**
*
* Use when you need to provide or retrieve the full platform cryptography
* service from an effect's context.
*
* **Details**
*
* Providing this service supplies platform-agnostic cryptographic operations
* such as hashing, UUID generation, and secure random values.
*
* @see {@link make} for constructing a Crypto service from primitive operations
*
* @category services
* @since 4.0.0
*/
export const Crypto = /*#__PURE__*/Context.Service("effect/Crypto");
/**
* Creates a `Crypto` service from the primitive implementation, deriving the
* random generator helpers and UUID generation from those primitives.
*
* **When to use**
*
* Use to build a Crypto service for a platform integration, test layer, or
* custom runtime from primitive random-byte and digest operations.
*
* **Details**
*
* The constructor derives random numbers, booleans, integer ranges, shuffling,
* and UUID generation from `impl.randomBytes`. Digest operations delegate to
* `impl.digest`.
*
* **Gotchas**
*
* `impl.randomBytes` must return cryptographically secure bytes of the
* requested length. UUID formatting mutates the byte array returned for UUID
* generation, so the implementation should return a fresh array for each call.
*
* **Example** (Creating a Crypto service)
*
* ```ts
* import { Crypto, Effect, Layer } from "effect"
*
* const TestCrypto = Layer.succeed(
* Crypto.Crypto,
* Crypto.make({
* randomBytes: (size) => new Uint8Array(size),
* digest: (_algorithm, data) => Effect.succeed(data)
* })
* )
* ```
*
* @category constructors
* @since 4.0.0
*/
export const make = impl => {
const randomBytesUnsafe = impl.randomBytes;
const randomBytes = size => Effect.map(validateSize("randomBytes", size), randomBytesUnsafe);
const nextDoubleUnsafe = () => {
const bytes = randomBytesUnsafe(7);
const value = (bytes[0] & 0x1f) * 2 ** 48 + bytes[1] * 2 ** 40 + bytes[2] * 2 ** 32 + bytes[3] * 2 ** 24 + bytes[4] * 2 ** 16 + bytes[5] * 2 ** 8 + bytes[6];
return value / 2 ** 53;
};
const nextIntUnsafe = () => Math.floor(nextDoubleUnsafe() * (Number.MAX_SAFE_INTEGER - Number.MIN_SAFE_INTEGER + 1)) + Number.MIN_SAFE_INTEGER;
return Crypto.of({
[TypeId]: TypeId,
randomBytes,
nextDoubleUnsafe,
nextIntUnsafe,
digest: impl.digest,
random: Effect.sync(() => nextDoubleUnsafe()),
randomBoolean: Effect.sync(() => nextDoubleUnsafe() > 0.5),
randomInt: Effect.sync(() => nextIntUnsafe()),
randomBetween: (min, max) => Effect.sync(() => nextDoubleUnsafe() * (max - min) + min),
randomIntBetween(min, max, options) {
const extra = options?.halfOpen === true ? 0 : 1;
return Effect.sync(() => {
const minInt = Math.ceil(min);
const maxInt = Math.floor(max);
return Math.floor(nextDoubleUnsafe() * (maxInt - minInt + extra)) + minInt;
});
},
randomShuffle: elements => Effect.sync(() => {
const buffer = Array.from(elements);
for (let i = buffer.length - 1; i >= 1; i = i - 1) {
const index = Math.min(i, Math.floor(nextDoubleUnsafe() * (i + 1)));
const value = buffer[i];
buffer[i] = buffer[index];
buffer[index] = value;
}
return buffer;
}),
randomUUIDv4: Effect.sync(() => formatUUIDv4(randomBytesUnsafe(16))),
randomUUIDv7: Effect.clockWith(clock => Effect.succeed(formatUUIDv7(clock.currentTimeMillisUnsafe(), randomBytesUnsafe(16))))
});
};
const validateSize = (method, size) => Number.isSafeInteger(size) && size >= 0 ? Effect.succeed(size) : Effect.fail(PlatformError.badArgument({
module: "Crypto",
method,
description: "size must be a non-negative safe integer"
}));
const hex = byte => byte.toString(16).padStart(2, "0");
const formatUUID = bytes => {
const segments = [bytes.subarray(0, 4), bytes.subarray(4, 6), bytes.subarray(6, 8), bytes.subarray(8, 10), bytes.subarray(10, 16)];
return segments.map(segment => Array.from(segment, hex).join("")).join("-");
};
const formatUUIDv4 = bytes => {
bytes[6] = bytes[6] & 0x0f | 0x40;
bytes[8] = bytes[8] & 0x3f | 0x80;
return formatUUID(bytes);
};
const maxUUIDv7Timestamp = 2 ** 48 - 1;
const formatUUIDv7 = (timestampMillis, bytes) => {
const timestamp = Math.min(Math.max(0, Math.trunc(timestampMillis)), maxUUIDv7Timestamp);
bytes[0] = Math.floor(timestamp / 2 ** 40);
bytes[1] = Math.floor(timestamp / 2 ** 32) & 0xff;
bytes[2] = Math.floor(timestamp / 2 ** 24) & 0xff;
bytes[3] = Math.floor(timestamp / 2 ** 16) & 0xff;
bytes[4] = Math.floor(timestamp / 2 ** 8) & 0xff;
bytes[5] = timestamp & 0xff;
bytes[6] = bytes[6] & 0x0f | 0x70;
bytes[8] = bytes[8] & 0x3f | 0x80;
return formatUUID(bytes);
};
//# sourceMappingURL=Crypto.js.map

Xet Storage Details

Size:
5.44 kB
·
Xet hash:
c283303bb1c500a2db50d71c2c24573bd76229b6bd3443b5a3a88a45db97f6c9

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.