Skip to content

Commit

Permalink
Improving commenting for events package
Browse files Browse the repository at this point in the history
  • Loading branch information
mzkrasner committed Dec 19, 2024
1 parent d4c7bad commit 0a00f05
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 27 deletions.
72 changes: 64 additions & 8 deletions packages/events/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ import type { CID } from 'multiformats/cid'
import { SignedEvent } from './codecs.js'
import { getSignedEventPayload } from './signing.js'

/** Container for a signed Ceramic event */
/**
* A container for a signed Ceramic event.
*
* @typeParam Payload - The type of the event's payload.
*
* @property signed - Indicates that the event is signed (`true`).
* @property cid - The CID of the linked block for the event.
* @property payload - The decoded payload of the event.
* @property verified - The result of verifying the event's JWS.
* @property cacaoBlock - (Optional) A block containing a Cacao (Capability Object).
*/
export type SignedEventContainer<Payload> = {
signed: true
cid: CID
Expand All @@ -14,26 +24,60 @@ export type SignedEventContainer<Payload> = {
cacaoBlock?: Uint8Array
}

/** Container for an unsigned Ceramic event */
/**
* A container for an unsigned Ceramic event.
*
* @typeParam Payload - The type of the event's payload.
*
* @property signed - Indicates that the event is unsigned (`false`).
* @property payload - The decoded payload of the event.
*/
export type UnsignedEventContainer<Payload> = {
signed: false
payload: Payload
}

/** Container for a Ceramic event, providing additional metadata about the event */
/**
* A container for a Ceramic event, which can be either signed or unsigned.
*
* @typeParam Payload - The type of the event's payload.
*/
export type EventContainer<Payload> =
| SignedEventContainer<Payload>
| UnsignedEventContainer<Payload>

/** Decode an unsigned Ceramic event into a container using the provided payload decoder */
/**
* Decodes an unsigned Ceramic event into a container.
*
* @param codec - The codec used to decode the event's payload.
* @param event - The unsigned Ceramic event to decode.
* @returns An `UnsignedEventContainer` containing the decoded payload.
*
* @remarks
* - This function assumes that the event is unsigned and decodes it accordingly.
* - Use `eventToContainer` if the event type (signed or unsigned) is unknown.
*/
export function unsignedEventToContainer<Payload>(
codec: Decoder<unknown, Payload>,
event: unknown,
): UnsignedEventContainer<Payload> {
return { signed: false, payload: decode(codec, event) }
}

/** Decode a signed Ceramic event into a container using the provided verifier DID and payload decoder */
/**
* Decodes a signed Ceramic event into a container.
*
* @param did - The DID used to verify the event's JWS.
* @param codec - The codec used to decode the event's payload.
* @param event - The signed Ceramic event to decode.
* @returns A promise that resolves to a `SignedEventContainer` containing the decoded payload and metadata.
*
* @throws Will throw an error if the linked block CID is missing or if verification fails.
*
* @remarks
* - This function verifies the event's JWS and decodes its payload simultaneously.
* - It also includes additional metadata such as the verification result and `cacaoBlock` if present.
*/
export async function signedEventToContainer<Payload>(
did: DID,
codec: Decoder<unknown, Payload>,
Expand All @@ -44,13 +88,25 @@ export async function signedEventToContainer<Payload>(
throw new Error('Missing linked block CID')
}
const [verified, payload] = await Promise.all([
did.verifyJWS(event.jws),
getSignedEventPayload(codec, event),
did.verifyJWS(event.jws), // Verify the JWS signature.
getSignedEventPayload(codec, event), // Decode the payload.
])
return { signed: true, cid, verified, payload, cacaoBlock: event.cacaoBlock }
}

/** Decode a Ceramic event into a container using the provided verifier DID and payload decoder */
/**
* Decodes a Ceramic event (signed or unsigned) into a container.
*
* @param did - The DID used to verify the event's JWS if it is signed.
* @param codec - The codec used to decode the event's payload.
* @param event - The Ceramic event to decode (can be signed or unsigned).
* @returns A promise that resolves to an `EventContainer` containing the decoded payload and metadata.
*
* @remarks
* - This function determines the type of the event (signed or unsigned) and processes it accordingly.
* - For signed events, it verifies the JWS and decodes the payload.
* - For unsigned events, it simply decodes the payload.
*/
export async function eventToContainer<Payload>(
did: DID,
codec: Decoder<unknown, Payload>,
Expand Down
85 changes: 76 additions & 9 deletions packages/events/src/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { sha256 } from 'multihashes-sync/sha2'
import { SignedEvent } from './codecs.js'
import { base64urlToJSON, restrictBlockSize } from './utils.js'

// Initialize a CAR factory with support for DAG-JOSE and DAG-JSON codecs.
const carFactory = new CARFactory()
carFactory.codecs.add(dagJose)
carFactory.codecs.add(dagJson)
Expand All @@ -18,15 +19,31 @@ carFactory.hashers.add(sha256)
/** @internal */
export type Base = keyof typeof bases

/** @internal */
/** Default base encoding for CAR files (Base64) */
export const DEFAULT_BASE: Base = 'base64'

/** Encode a CAR into a string, using the given base (defaults to base64) */
/**
* Encodes a CAR into a string using the specified base encoding.
*
* @param car - The CAR to encode.
* @param base - The base encoding to use (defaults to Base64).
* @returns The string-encoded CAR.
*
* @throws Will throw an error if the base encoding is not supported.
*/
export function carToString(car: CAR, base: Base = DEFAULT_BASE): string {
return car.toString(base)
}

/** Decode a CAR from a string, using the given base (defaults to base64) */
/**
* Decodes a CAR from a string using the specified base encoding.
*
* @param value - The string-encoded CAR.
* @param base - The base encoding used to decode the CAR (defaults to Base64).
* @returns The decoded CAR object.
*
* @throws Will throw an error if the base encoding is not supported.
*/
export function carFromString(value: string, base: Base = DEFAULT_BASE): CAR {
const codec = bases[base]
if (codec == null) {
Expand All @@ -35,7 +52,16 @@ export function carFromString(value: string, base: Base = DEFAULT_BASE): CAR {
return carFactory.fromBytes(codec.decode(value))
}

/** Encode a signed event into a CAR */
/**
* Encodes a signed event into a CAR format.
*
* @param event - The signed event to encode.
* @returns A CAR object representing the signed event.
*
* @remarks
* - Encodes the JWS, linked block, and optional `cacaoBlock` into the CAR.
* - Validates block sizes using `restrictBlockSize` to ensure consistency.
*/
export function signedEventToCAR(event: SignedEvent): CAR {
const { jws, linkedBlock, cacaoBlock } = event
const car = carFactory.build()
Expand Down Expand Up @@ -68,7 +94,16 @@ export function signedEventToCAR(event: SignedEvent): CAR {
return car
}

/** Encode an unsigned event into a CAR using the provided codec */
/**
* Encodes an unsigned event into a CAR using the provided codec.
*
* @param codec - The codec used to encode the event.
* @param event - The unsigned event to encode.
* @returns A CAR object representing the unsigned event.
*
* @remarks
* Encodes the event as the root of the CAR file using the specified codec.
*/
export function encodeEventToCAR(codec: Codec<unknown>, event: unknown): CAR {
const car = carFactory.build()
const cid = car.put(codec.encode(event), { isRoot: true })
Expand All @@ -78,14 +113,30 @@ export function encodeEventToCAR(codec: Codec<unknown>, event: unknown): CAR {
return car
}

/** Encode an event into a CAR using the provided codec for unsigned events */
/**
* Encodes an event into a CAR. Supports both signed and unsigned events.
*
* @param codec - The codec used for unsigned events.
* @param event - The event to encode (signed or unsigned).
* @returns A CAR object representing the event.
*
* @remarks
* Uses `signedEventToCAR` for signed events and `encodeEventToCAR` for unsigned events.
*/
export function eventToCAR(codec: Codec<unknown>, event: unknown): CAR {
return SignedEvent.is(event)
? signedEventToCAR(event)
: encodeEventToCAR(codec, event)
}

/** Encode an event into a string using the provided codec for unsigned events and the given base (defaults to base64) */
/**
* Encodes an event into a string using the specified codec and base encoding.
*
* @param codec - The codec used for unsigned events.
* @param event - The event to encode (signed or unsigned).
* @param base - The base encoding to use (defaults to Base64).
* @returns The string-encoded CAR representing the event.
*/
export function eventToString(
codec: Codec<unknown>,
event: unknown,
Expand All @@ -94,7 +145,16 @@ export function eventToString(
return carToString(eventToCAR(codec, event), base)
}

/** Decode an event from a string using the provided codec for unsigned events */
/**
* Decodes an event from a CAR object using the specified decoder.
*
* @param decoder - The decoder to use for unsigned events.
* @param car - The CAR object containing the event.
* @param eventCID - (Optional) The CID of the event to decode.
* @returns The decoded event, either a `SignedEvent` or a custom payload.
*
* @throws Will throw an error if the linked block is missing or decoding fails.
*/
export function eventFromCAR<Payload = unknown>(
decoder: Decoder<unknown, Payload>,
car: CAR,
Expand Down Expand Up @@ -124,7 +184,14 @@ export function eventFromCAR<Payload = unknown>(
return decode(decoder, root)
}

/** Decode an event from a string using the provided codec for unsigned events and the given base (defaults to base64) */
/**
* Decodes an event from a string using the specified decoder and base encoding.
*
* @param decoder - The decoder to use for unsigned events.
* @param value - The string-encoded CAR containing the event.
* @param base - The base encoding used (defaults to Base64).
* @returns The decoded event, either a `SignedEvent` or a custom payload.
*/
export function eventFromString<Payload = unknown>(
decoder: Decoder<unknown, Payload>,
value: string,
Expand Down
55 changes: 51 additions & 4 deletions packages/events/src/signing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,20 @@ import {
type SignedEvent,
} from './codecs.js'

/** Sign an event payload using the provided DID */
/**
* Signs an event payload using the provided DID.
*
* @param did - The DID instance used to sign the payload.
* @param payload - The data to be signed, provided as a key-value map.
* @param options - (Optional) Additional options for creating the JWS.
* @returns A promise that resolves to a `SignedEvent` containing the JWS and linked block.
*
* @throws Will throw an error if the DID is not authenticated.
*
* @remarks
* This function uses the DID's `createDagJWS` method to sign the payload and
* returns the signed event, including the linked block as a `Uint8Array`.
*/
export async function signEvent(
did: DID,
payload: Record<string, unknown>,
Expand All @@ -24,12 +37,33 @@ export async function signEvent(
return { ...rest, linkedBlock: new Uint8Array(linkedBlock) }
}

// Make controllers optional in the event header as createSignedEvent() will inject it as needed
/**
* A partial version of the initialization event header, with optional controllers.
*
* @remarks
* This type is used to allow the creation of initialization events without requiring
* the `controllers` field, as it will be injected automatically during the signing process.
*/
export type PartialInitEventHeader = Omit<InitEventHeader, 'controllers'> & {
controllers?: [DIDString]
}

/** Create a signed init event using the provided DID, data and header */
/**
* Creates a signed initialization event using the provided DID, data, and header.
*
* @param did - The DID instance used to sign the initialization event.
* @param data - The initialization data to be included in the event.
* @param header - The header for the initialization event, with optional controllers.
* @param options - (Optional) Additional options for creating the JWS.
* @returns A promise that resolves to a `SignedEvent` representing the initialization event.
*
* @throws Will throw an error if the DID is not authenticated.
*
* @remarks
* - If `controllers` are not provided in the header, they will be automatically set
* based on the DID's parent (if available) or the DID itself.
* - The payload is encoded as an `InitEventPayload` before signing.
*/
export async function createSignedInitEvent<T>(
did: DID,
data: T,
Expand All @@ -49,7 +83,20 @@ export async function createSignedInitEvent<T>(
return await signEvent(did, payload, options)
}

/** Decode the payload of a signed event using the provided decoder */
/**
* Decodes the payload of a signed event using the provided decoder.
*
* @param decoder - The decoder used to interpret the event's payload.
* @param event - The signed event containing the payload to decode.
* @returns A promise that resolves to the decoded payload.
*
* @throws Will throw an error if the linked block CID is missing or if decoding fails.
*
* @remarks
* - The function reconstructs the block from the event's linked block and CID,
* using the DAG-CBOR codec and SHA-256 hasher.
* - The payload is decoded using the provided decoder to ensure its validity and type safety.
*/
export async function getSignedEventPayload<Payload = Record<string, unknown>>(
decoder: Decoder<unknown, Payload>,
event: SignedEvent,
Expand Down
Loading

0 comments on commit 0a00f05

Please sign in to comment.