diff --git a/lib/biome.json b/lib/biome.json index 1283752..faefa44 100644 --- a/lib/biome.json +++ b/lib/biome.json @@ -1,22 +1,28 @@ { - "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "style": { - "noUselessElse": "off" - }, - "suspicious": { - "noExplicitAny": "off", - "noShadowRestrictedNames": "off" - } - } - }, - "files": { - "include": ["src/**/*"] - } + "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "style": { + "noUselessElse": "off" + }, + "suspicious": { + "noExplicitAny": "off", + "noShadowRestrictedNames": "off" + } + } + }, + "formatter": { + "enabled": true, + "lineWidth": 100, + "indentStyle": "space", + "indentWidth": 3 + }, + "files": { + "include": ["src/**/*"] + } } diff --git a/lib/src/bindgen/bindgen.d.ts b/lib/src/bindgen/bindgen.d.ts index 28687b4..517e0e9 100644 --- a/lib/src/bindgen/bindgen.d.ts +++ b/lib/src/bindgen/bindgen.d.ts @@ -14,10 +14,7 @@ export function golemCreatePromise(): PromiseId; export function golemAwaitPromise(promiseId: PromiseId): Uint8Array; -export function golemCompletePromise( - promiseId: PromiseId, - data: Uint8Array, -): boolean; +export function golemCompletePromise(promiseId: PromiseId, data: Uint8Array): boolean; export function golemDeletePromise(promiseId: PromiseId): void; export function getSelfUri(functionName: string): Uri; export function getOplogIndex(): OplogIndex; @@ -28,33 +25,27 @@ export function markEndOperation(begin: OplogIndex): void; export function getRetryPolicy(): RetryPolicy; export function setRetryPolicy(newRetryPolicy: RetryPolicy): void; export function getOplogPersistenceLevel(): PersistenceLevel; -export function setOplogPersistenceLevel( - newPersistenceLevel: PersistenceLevel, -): void; +export function setOplogPersistenceLevel(newPersistenceLevel: PersistenceLevel): void; export function getIdempotenceMode(): boolean; export function setIdempotenceMode(idempotent: boolean): void; export function generateIdempotencyKey(): Uuid; export function updateWorker( - workerId: WorkerId, - targetVersion: ComponentVersion, - mode: UpdateMode, + workerId: WorkerId, + targetVersion: ComponentVersion, + mode: UpdateMode, ): void; export class GetWorkers { - constructor( - componentId: ComponentId, - filter: WorkerAnyFilter | undefined, - precise: boolean, - ); - getNext(): WorkerMetadata[] | undefined; + constructor(componentId: ComponentId, filter: WorkerAnyFilter | undefined, precise: boolean); + getNext(): WorkerMetadata[] | undefined; } export interface Uuid { - highBits: bigint; - lowBits: bigint; + highBits: bigint; + lowBits: bigint; } export interface ComponentId { - uuid: Uuid; + uuid: Uuid; } /** * # Variants @@ -67,14 +58,10 @@ export interface ComponentId { * * ## `"not-like"` */ -export type StringFilterComparator = - | "equal" - | "not-equal" - | "like" - | "not-like"; +export type StringFilterComparator = "equal" | "not-equal" | "like" | "not-like"; export interface WorkerNameFilter { - comparator: StringFilterComparator; - value: string; + comparator: StringFilterComparator; + value: string; } /** * # Variants @@ -92,12 +79,12 @@ export interface WorkerNameFilter { * ## `"less"` */ export type FilterComparator = - | "equal" - | "not-equal" - | "greater-equal" - | "greater" - | "less-equal" - | "less"; + | "equal" + | "not-equal" + | "greater-equal" + | "greater" + | "less-equal" + | "less"; /** * # Variants * @@ -116,101 +103,101 @@ export type FilterComparator = * ## `"exited"` */ export type WorkerStatus = - | "running" - | "idle" - | "suspended" - | "interrupted" - | "retrying" - | "failed" - | "exited"; + | "running" + | "idle" + | "suspended" + | "interrupted" + | "retrying" + | "failed" + | "exited"; export interface WorkerStatusFilter { - comparator: FilterComparator; - value: WorkerStatus; + comparator: FilterComparator; + value: WorkerStatus; } export interface WorkerVersionFilter { - comparator: FilterComparator; - value: bigint; + comparator: FilterComparator; + value: bigint; } export interface WorkerCreatedAtFilter { - comparator: FilterComparator; - value: bigint; + comparator: FilterComparator; + value: bigint; } export interface WorkerEnvFilter { - name: string; - comparator: StringFilterComparator; - value: string; + name: string; + comparator: StringFilterComparator; + value: string; } export type WorkerPropertyFilter = - | WorkerPropertyFilterName - | WorkerPropertyFilterStatus - | WorkerPropertyFilterVersion - | WorkerPropertyFilterCreatedAt - | WorkerPropertyFilterEnv; + | WorkerPropertyFilterName + | WorkerPropertyFilterStatus + | WorkerPropertyFilterVersion + | WorkerPropertyFilterCreatedAt + | WorkerPropertyFilterEnv; export interface WorkerPropertyFilterName { - tag: "name"; - val: WorkerNameFilter; + tag: "name"; + val: WorkerNameFilter; } export interface WorkerPropertyFilterStatus { - tag: "status"; - val: WorkerStatusFilter; + tag: "status"; + val: WorkerStatusFilter; } export interface WorkerPropertyFilterVersion { - tag: "version"; - val: WorkerVersionFilter; + tag: "version"; + val: WorkerVersionFilter; } export interface WorkerPropertyFilterCreatedAt { - tag: "created-at"; - val: WorkerCreatedAtFilter; + tag: "created-at"; + val: WorkerCreatedAtFilter; } export interface WorkerPropertyFilterEnv { - tag: "env"; - val: WorkerEnvFilter; + tag: "env"; + val: WorkerEnvFilter; } export interface WorkerAllFilter { - filters: WorkerPropertyFilter[]; + filters: WorkerPropertyFilter[]; } export interface WorkerAnyFilter { - filters: WorkerAllFilter[]; + filters: WorkerAllFilter[]; } export interface WorkerId { - componentId: ComponentId; - workerName: string; + componentId: ComponentId; + workerName: string; } export interface WorkerMetadata { - workerId: WorkerId; - args: string[]; - env: [string, string][]; - status: WorkerStatus; - componentVersion: bigint; - retryCount: bigint; + workerId: WorkerId; + args: string[]; + env: [string, string][]; + status: WorkerStatus; + componentVersion: bigint; + retryCount: bigint; } export type OplogIndex = bigint; export interface PromiseId { - workerId: WorkerId; - oplogIdx: OplogIndex; + workerId: WorkerId; + oplogIdx: OplogIndex; } export interface Uri { - value: string; + value: string; } export type Duration = bigint; export interface RetryPolicy { - maxAttempts: number; - minDelay: Duration; - maxDelay: Duration; - multiplier: number; + maxAttempts: number; + minDelay: Duration; + maxDelay: Duration; + multiplier: number; } export type PersistenceLevel = - | PersistenceLevelPersistNothing - | PersistenceLevelPersistRemoteSideEffects - | PersistenceLevelSmart; + | PersistenceLevelPersistNothing + | PersistenceLevelPersistRemoteSideEffects + | PersistenceLevelSmart; export interface PersistenceLevelPersistNothing { - tag: "persist-nothing"; + tag: "persist-nothing"; } export interface PersistenceLevelPersistRemoteSideEffects { - tag: "persist-remote-side-effects"; + tag: "persist-remote-side-effects"; } export interface PersistenceLevelSmart { - tag: "smart"; + tag: "smart"; } export type ComponentVersion = bigint; /** diff --git a/lib/src/bindgen/bindgen.js b/lib/src/bindgen/bindgen.js index 1765a72..41c1566 100644 --- a/lib/src/bindgen/bindgen.js +++ b/lib/src/bindgen/bindgen.js @@ -13,97 +13,97 @@ // limitations under the License. import { - GetWorkers, - generateIdempotencyKey as generateIdempotencyKeyImpl, - getIdempotenceMode as getIdempotenceModeImpl, - getOplogIndex as getOplogIndexImpl, - getOplogPersistenceLevel as getOplogPersistenceLevelImpl, - getRetryPolicy as getRetryPolicyImpl, - getSelfUri as getSelfUriImpl, - golemAwaitPromise as golemAwaitPromiseImpl, - golemCompletePromise as golemCompletePromiseImpl, - golemCreatePromise as golemCreatePromiseImpl, - golemDeletePromise as golemDeletePromiseImpl, - markBeginOperation as markBeginOperationImpl, - markEndOperation as markEndOperationImpl, - oplogCommit as oplogCommitImpl, - setIdempotenceMode as setIdempotenceModeImpl, - setOplogIndex as setOplogIndexImpl, - setOplogPersistenceLevel as setOplogPersistenceLevelImpl, - setRetryPolicy as setRetryPolicyImpl, - updateWorker as updateWorkerImpl, + GetWorkers, + generateIdempotencyKey as generateIdempotencyKeyImpl, + getIdempotenceMode as getIdempotenceModeImpl, + getOplogIndex as getOplogIndexImpl, + getOplogPersistenceLevel as getOplogPersistenceLevelImpl, + getRetryPolicy as getRetryPolicyImpl, + getSelfUri as getSelfUriImpl, + golemAwaitPromise as golemAwaitPromiseImpl, + golemCompletePromise as golemCompletePromiseImpl, + golemCreatePromise as golemCreatePromiseImpl, + golemDeletePromise as golemDeletePromiseImpl, + markBeginOperation as markBeginOperationImpl, + markEndOperation as markEndOperationImpl, + oplogCommit as oplogCommitImpl, + setIdempotenceMode as setIdempotenceModeImpl, + setOplogIndex as setOplogIndexImpl, + setOplogPersistenceLevel as setOplogPersistenceLevelImpl, + setRetryPolicy as setRetryPolicyImpl, + updateWorker as updateWorkerImpl, } from "golem:api/host@0.2.0"; export { GetWorkers }; export function golemCreatePromise() { - return golemCreatePromiseImpl(); + return golemCreatePromiseImpl(); } export function golemAwaitPromise(promiseId) { - return golemAwaitPromiseImpl(promiseId); + return golemAwaitPromiseImpl(promiseId); } export function golemCompletePromise(promiseId, data) { - return golemCompletePromiseImpl(promiseId, data); + return golemCompletePromiseImpl(promiseId, data); } export function golemDeletePromise(promiseId) { - return golemDeletePromiseImpl(promiseId); + return golemDeletePromiseImpl(promiseId); } export function getSelfUri(functionName) { - return getSelfUriImpl(functionName); + return getSelfUriImpl(functionName); } export function getOplogIndex() { - return getOplogIndexImpl(); + return getOplogIndexImpl(); } export function setOplogIndex(oplogIdx) { - return setOplogIndexImpl(oplogIdx); + return setOplogIndexImpl(oplogIdx); } export function oplogCommit(replicas) { - return oplogCommitImpl(replicas); + return oplogCommitImpl(replicas); } export function markBeginOperation() { - return markBeginOperationImpl(); + return markBeginOperationImpl(); } export function markEndOperation(begin) { - return markEndOperationImpl(begin); + return markEndOperationImpl(begin); } export function getRetryPolicy() { - return getRetryPolicyImpl(); + return getRetryPolicyImpl(); } export function setRetryPolicy(newRetryPolicy) { - return setRetryPolicyImpl(newRetryPolicy); + return setRetryPolicyImpl(newRetryPolicy); } export function getOplogPersistenceLevel() { - return getOplogPersistenceLevelImpl(); + return getOplogPersistenceLevelImpl(); } export function setOplogPersistenceLevel(newPersistenceLevel) { - return setOplogPersistenceLevelImpl(newPersistenceLevel); + return setOplogPersistenceLevelImpl(newPersistenceLevel); } export function getIdempotenceMode() { - return getIdempotenceModeImpl(); + return getIdempotenceModeImpl(); } export function setIdempotenceMode(idempotent) { - return setIdempotenceModeImpl(idempotent); + return setIdempotenceModeImpl(idempotent); } export function generateIdempotencyKey() { - return generateIdempotencyKeyImpl(); + return generateIdempotencyKeyImpl(); } export function updateWorker(workerId, targetVersion, mode) { - return updateWorkerImpl(workerId, targetVersion, mode); + return updateWorkerImpl(workerId, targetVersion, mode); } diff --git a/lib/src/guard.ts b/lib/src/guard.ts index d76008e..8a9069e 100644 --- a/lib/src/guard.ts +++ b/lib/src/guard.ts @@ -13,17 +13,17 @@ // limitations under the License. import { - type OplogIndex, - type PersistenceLevel, - type RetryPolicy, - getIdempotenceMode, - getOplogPersistenceLevel, - getRetryPolicy, - markBeginOperation, - markEndOperation, - setIdempotenceMode, - setOplogPersistenceLevel, - setRetryPolicy, + type OplogIndex, + type PersistenceLevel, + type RetryPolicy, + getIdempotenceMode, + getOplogPersistenceLevel, + getRetryPolicy, + markBeginOperation, + markEndOperation, + setIdempotenceMode, + setOplogPersistenceLevel, + setRetryPolicy, } from "./bindgen/bindgen"; /** @@ -31,10 +31,10 @@ import { * You must call drop on the guard once you are finished using it. */ export class PersistenceLevelGuard { - constructor(private originalLevel: PersistenceLevel) {} - drop() { - setOplogPersistenceLevel(this.originalLevel); - } + constructor(private originalLevel: PersistenceLevel) {} + drop() { + setOplogPersistenceLevel(this.originalLevel); + } } /** @@ -44,9 +44,9 @@ export class PersistenceLevelGuard { * @returns A PersistenceLevelGuard instance. */ export function usePersistenceLevel(level: PersistenceLevel) { - const originalLevel = getOplogPersistenceLevel(); - setOplogPersistenceLevel(level); - return new PersistenceLevelGuard(originalLevel); + const originalLevel = getOplogPersistenceLevel(); + setOplogPersistenceLevel(level); + return new PersistenceLevelGuard(originalLevel); } /** @@ -55,12 +55,9 @@ export function usePersistenceLevel(level: PersistenceLevel) { * @param f - The function to execute. * @returns The result of the executed function. */ -export function withPersistenceLevel( - level: PersistenceLevel, - f: () => R, -): R { - const guard = usePersistenceLevel(level); - return executeWithDrop([guard], f); +export function withPersistenceLevel(level: PersistenceLevel, f: () => R): R { + const guard = usePersistenceLevel(level); + return executeWithDrop([guard], f); } /** @@ -68,10 +65,10 @@ export function withPersistenceLevel( * You must call drop on the guard once you are finished using it. */ export class IdempotenceModeGuard { - constructor(private original: boolean) {} - drop() { - setIdempotenceMode(this.original); - } + constructor(private original: boolean) {} + drop() { + setIdempotenceMode(this.original); + } } /** @@ -81,9 +78,9 @@ export class IdempotenceModeGuard { * @returns An IdempotenceModeGuard instance. */ export function useIdempotenceMode(mode: boolean): IdempotenceModeGuard { - const original = getIdempotenceMode(); - setIdempotenceMode(mode); - return new IdempotenceModeGuard(original); + const original = getIdempotenceMode(); + setIdempotenceMode(mode); + return new IdempotenceModeGuard(original); } /** @@ -93,8 +90,8 @@ export function useIdempotenceMode(mode: boolean): IdempotenceModeGuard { * @returns The result of the executed function. */ export function withIdempotenceMode(mode: boolean, f: () => R): R { - const guard = useIdempotenceMode(mode); - return executeWithDrop([guard], f); + const guard = useIdempotenceMode(mode); + return executeWithDrop([guard], f); } /** @@ -102,10 +99,10 @@ export function withIdempotenceMode(mode: boolean, f: () => R): R { * You must call drop on the guard once you are finished using it. */ export class RetryPolicyGuard { - constructor(private original: RetryPolicy) {} - drop() { - setRetryPolicy(this.original); - } + constructor(private original: RetryPolicy) {} + drop() { + setRetryPolicy(this.original); + } } /** @@ -115,9 +112,9 @@ export class RetryPolicyGuard { * @returns A RetryPolicyGuard instance. */ export function useRetryPolicy(policy: RetryPolicy): RetryPolicyGuard { - const original = getRetryPolicy(); - setRetryPolicy(policy); - return new RetryPolicyGuard(original); + const original = getRetryPolicy(); + setRetryPolicy(policy); + return new RetryPolicyGuard(original); } /** @@ -127,8 +124,8 @@ export function useRetryPolicy(policy: RetryPolicy): RetryPolicyGuard { * @returns The result of the executed function. */ export function withRetryPolicy(policy: RetryPolicy, f: () => R): R { - const guard = useRetryPolicy(policy); - return executeWithDrop([guard], f); + const guard = useRetryPolicy(policy); + return executeWithDrop([guard], f); } /** @@ -136,10 +133,10 @@ export function withRetryPolicy(policy: RetryPolicy, f: () => R): R { * You must call drop on the guard once you are finished using it. */ export class AtomicOperationGuard { - constructor(private begin: OplogIndex) {} - drop() { - markEndOperation(this.begin); - } + constructor(private begin: OplogIndex) {} + drop() { + markEndOperation(this.begin); + } } /** @@ -148,8 +145,8 @@ export class AtomicOperationGuard { * @returns An AtomicOperationGuard instance. */ export function markAtomicOperation(): AtomicOperationGuard { - const begin = markBeginOperation(); - return new AtomicOperationGuard(begin); + const begin = markBeginOperation(); + return new AtomicOperationGuard(begin); } /** @@ -158,8 +155,8 @@ export function markAtomicOperation(): AtomicOperationGuard { * @returns The result of the executed function. */ export function atomically(f: () => T): T { - const guard = markAtomicOperation(); - return executeWithDrop([guard], f); + const guard = markAtomicOperation(); + return executeWithDrop([guard], f); } /** @@ -169,17 +166,17 @@ export function atomically(f: () => T): T { * @returns The result of the executed function. */ export function executeWithDrop void }, R>( - resources: [Resource], - fn: () => R, + resources: [Resource], + fn: () => R, ): R { - try { - const result = fn(); - dropAll(true, resources); - return result; - } catch (e) { - dropAll(false, resources); - throw e; - } + try { + const result = fn(); + dropAll(true, resources); + return result; + } catch (e) { + dropAll(false, resources); + throw e; + } } /** @@ -188,28 +185,28 @@ export function executeWithDrop void }, R>( * @throws DropError if any errors occur during the dropping process. */ export function dropAll void }>( - throwOnError: boolean, - resources: [Resource], + throwOnError: boolean, + resources: [Resource], ) { - const errors = []; - for (const resource of resources) { - try { - resource.drop(); - } catch (e) { - errors.push(e); - } - } - if (throwOnError && errors.length > 0) { - throw new DropError(errors); - } + const errors = []; + for (const resource of resources) { + try { + resource.drop(); + } catch (e) { + errors.push(e); + } + } + if (throwOnError && errors.length > 0) { + throw new DropError(errors); + } } /** * Custom error class for errors that occur during the dropping of resources. */ class DropError extends Error { - constructor(public errors: Error[]) { - const message = errors.map((e) => e.message).join(", "); - super(`Error dropping resources: ${message}`); - } + constructor(public errors: Error[]) { + const message = errors.map((e) => e.message).join(", "); + super(`Error dropping resources: ${message}`); + } } diff --git a/lib/src/result.ts b/lib/src/result.ts index bace04e..9e075b7 100644 --- a/lib/src/result.ts +++ b/lib/src/result.ts @@ -1,299 +1,249 @@ const prototype = { - /** - * Returns `this.value` if `this` is a successful result, otherwise throws a TypeError. - * @example Returns the payload of a successful result. - * Result.ok(123).unwrap() // 123 - * @example Throws the payload of a failed result. - * Result.err('error').unwrap() // throws 'error' - */ - unwrap, + /** + * Returns `this.value` if `this` is a successful result, otherwise throws a TypeError. + * @example Returns the payload of a successful result. + * Result.ok(123).unwrap() // 123 + * @example Throws the payload of a failed result. + * Result.err('error').unwrap() // throws 'error' + */ + unwrap, - /** - * - * Returns `this.error` if `this` is a failed result, otherwise throws a TypeError. - * @example Returns the payload of a failed result. - * Result. - */ - unwrapErr, - /** - * Returns the payload of the result. - * @example Returns the payload of a successful result. - * Result.ok(123).toUnion() // 123 - * @example Returns the payload of a failed result. - * Result.err('error').toUnion() // 'error' - */ - toUnion, - /** - * Return the result of applying one of the given functions to the payload. - * @example - * Result.ok(123).match((x) => x * 2, (x: string) => x + '!') // 246 - * Result.err('error').match((x: number) => x * 2, (x) => x + '!') // 'error!' - */ - match, - /** - * Creates a Result value by modifying the payload of the successful result using the given function. - * @example - * Result.ok(123).map((x) => x * 2) // Result.ok(246) - * Result.err('error').map((x: number) => x * 2) // Result.err('error') - */ - map, - /** - * Creates a Result value by modifying the payload of the failed result using the given function. - * @example - * Result.ok(123).mapError((x: string) => x + '!') // Result.ok(123) - * Result.err('error').mapError((x) => x + '!') // Result.err('error!') - */ - mapError, - /** - * Calls the given function with the payload of the result and returns the result unchanged. - */ - tap, - /** - * Maps the payload of the successful result and flattens the nested Result type. - * @example - * Result.ok(123).flatMap((x) => Result.ok(x * 2)) // Result.ok(246) - * Result.ok(123).flatMap((x) => Result.err('error')) // Result.err('error') - * Result.err('error').flatMap((x: number) => Result.ok(x * 2)) // Result.err('error') - * Result.err('error').flatMap((x) => Result.err('failure')) // Result.err('error') - */ - flatMap, - /** - * Flattens the nested Result type. - * @example - * Result.ok(Result.ok(123)).flatten() // Result.ok(123) - * Result.ok(Result.err('error')).flatten() // Result.err('error') - * Result.err('error').flatten() // Result.err('error') - */ - flatten, - /** - * Perform a safe cast of the error type to the given class. If the payload of the failed result is not instance of constructor, throws TypeError. - * @example - * const result: Result = Result.tryCatch(() => { - * if (Math.random() >= 0) { - * throw new Error('error') - * } else { - * return 123 - * } - * }).assertErrorInstanceOf(Error) - */ - assertErrorInstanceOf, + /** + * + * Returns `this.error` if `this` is a failed result, otherwise throws a TypeError. + * @example Returns the payload of a failed result. + * Result. + */ + unwrapErr, + /** + * Returns the payload of the result. + * @example Returns the payload of a successful result. + * Result.ok(123).toUnion() // 123 + * @example Returns the payload of a failed result. + * Result.err('error').toUnion() // 'error' + */ + toUnion, + /** + * Return the result of applying one of the given functions to the payload. + * @example + * Result.ok(123).match((x) => x * 2, (x: string) => x + '!') // 246 + * Result.err('error').match((x: number) => x * 2, (x) => x + '!') // 'error!' + */ + match, + /** + * Creates a Result value by modifying the payload of the successful result using the given function. + * @example + * Result.ok(123).map((x) => x * 2) // Result.ok(246) + * Result.err('error').map((x: number) => x * 2) // Result.err('error') + */ + map, + /** + * Creates a Result value by modifying the payload of the failed result using the given function. + * @example + * Result.ok(123).mapError((x: string) => x + '!') // Result.ok(123) + * Result.err('error').mapError((x) => x + '!') // Result.err('error!') + */ + mapError, + /** + * Calls the given function with the payload of the result and returns the result unchanged. + */ + tap, + /** + * Maps the payload of the successful result and flattens the nested Result type. + * @example + * Result.ok(123).flatMap((x) => Result.ok(x * 2)) // Result.ok(246) + * Result.ok(123).flatMap((x) => Result.err('error')) // Result.err('error') + * Result.err('error').flatMap((x: number) => Result.ok(x * 2)) // Result.err('error') + * Result.err('error').flatMap((x) => Result.err('failure')) // Result.err('error') + */ + flatMap, + /** + * Flattens the nested Result type. + * @example + * Result.ok(Result.ok(123)).flatten() // Result.ok(123) + * Result.ok(Result.err('error')).flatten() // Result.err('error') + * Result.err('error').flatten() // Result.err('error') + */ + flatten, + /** + * Perform a safe cast of the error type to the given class. If the payload of the failed result is not instance of constructor, throws TypeError. + * @example + * const result: Result = Result.tryCatch(() => { + * if (Math.random() >= 0) { + * throw new Error('error') + * } else { + * return 123 + * } + * }).assertErrorInstanceOf(Error) + */ + assertErrorInstanceOf, } as const; /** Type representing success or failure. */ export type Result = Result.Ok | Result.Err; export namespace Result { - /** - * The type of a successful result. - * @example - * const success: Result.ok = Result.ok(123) - */ - export type Ok = typeof prototype & { - readonly value: T; - readonly error?: never; - readonly isOk: true; - readonly isErr: false; - }; + /** + * The type of a successful result. + * @example + * const success: Result.ok = Result.ok(123) + */ + export type Ok = typeof prototype & { + readonly value: T; + readonly error?: never; + readonly isOk: true; + readonly isErr: false; + }; - /** - * A failed result. - * @example - * const failure: Result.err = Result.err('error') - */ - export type Err = typeof prototype & { - readonly value?: never; - readonly error: E; - readonly isOk: false; - readonly isErr: true; - }; + /** + * A failed result. + * @example + * const failure: Result.err = Result.err('error') + */ + export type Err = typeof prototype & { + readonly value?: never; + readonly error: E; + readonly isOk: false; + readonly isErr: true; + }; - const UNIT = ok(undefined); + const UNIT = ok(undefined); - /** - * @returns A successful result with no payload. - */ - export function unit(): Ok { - return UNIT; - } + /** + * @returns A successful result with no payload. + */ + export function unit(): Ok { + return UNIT; + } - /** - * Create a successful result. - */ - export function ok(value: T): Result.Ok { - return withPrototype({ value, isOk: true, isErr: false }, prototype); - } + /** + * Create a successful result. + */ + export function ok(value: T): Result.Ok { + return withPrototype({ value, isOk: true, isErr: false }, prototype); + } - /** - * Create an error result. - */ - export function err(error: E): Err { - return withPrototype({ error, isOk: false, isErr: true }, prototype); - } + /** + * Create an error result. + */ + export function err(error: E): Err { + return withPrototype({ error, isOk: false, isErr: true }, prototype); + } - /** - * Create a result from a function that may throw an error. - */ - export function tryCatch(f: () => T): Result { - try { - return ok(f()); - } catch (error) { - return err(error); - } - } + /** + * Create a result from a function that may throw an error. + */ + export function tryCatch(f: () => T): Result { + try { + return ok(f()); + } catch (error) { + return err(error); + } + } - export function fromNullish(value: null): Result.Err; - export function fromNullish(value: undefined): Result.Err; - export function fromNullish( - value: null | undefined, - ): Result.Err; - export function fromNullish(value: T): Result.Ok; - export function fromNullish(value: T | null): Result; - export function fromNullish(value: T | undefined): Result; - export function fromNullish( - value: T | null | undefined, - ): Result; - export function fromNullish(value: T | null | undefined) { - return value != null ? Result.ok(value) : Result.err(value); - } + export function fromNullish(value: null): Result.Err; + export function fromNullish(value: undefined): Result.Err; + export function fromNullish(value: null | undefined): Result.Err; + export function fromNullish(value: T): Result.Ok; + export function fromNullish(value: T | null): Result; + export function fromNullish(value: T | undefined): Result; + export function fromNullish(value: T | null | undefined): Result; + export function fromNullish(value: T | null | undefined) { + return value != null ? Result.ok(value) : Result.err(value); + } - export function all(results: readonly Result.Ok[]): Result.Ok; - export function all(results: readonly Result[]): Result; - export function all(results: readonly Result[]): Result { - const values: T[] = []; - for (const result of results) { - if (result.isErr) return result; - values.push(result.value); - } - return Result.ok(values); - } + export function all(results: readonly Result.Ok[]): Result.Ok; + export function all(results: readonly Result[]): Result; + export function all(results: readonly Result[]): Result { + const values: T[] = []; + for (const result of results) { + if (result.isErr) return result; + values.push(result.value); + } + return Result.ok(values); + } } -function withPrototype( - target: T, - prototype: P, -): T & Omit { - return Object.assign(Object.create(prototype), target); +function withPrototype(target: T, prototype: P): T & Omit { + return Object.assign(Object.create(prototype), target); } function unwrap(this: Result.Ok): T; function unwrap(this: Result.Err): never; function unwrap(this: Result): T; function unwrap(this: Result): T { - if (this.isOk) return this.value; - else throw new TypeError(`unwrap·called·on·Err·result:·${this.error}`); + if (this.isOk) return this.value; + else throw new TypeError(`unwrap·called·on·Err·result:·${this.error}`); } function unwrapErr(this: Result.Ok): never; function unwrapErr(this: Result.Err): E; function unwrapErr(this: Result): E; function unwrapErr(this: Result): E { - if (this.isOk) - throw new TypeError(`unwrapErr·called·on·Ok·result:·${this.value}`); - else return this.error; + if (this.isOk) throw new TypeError(`unwrapErr·called·on·Ok·result:·${this.value}`); + else return this.error; } function toUnion(this: Result.Ok): T; function toUnion(this: Result.Err): E; function toUnion(this: Result): T | E; function toUnion(this: Result): T | E { - if (this.isOk) return this.value; - else return this.error; + if (this.isOk) return this.value; + else return this.error; } +function match(this: Result.Ok, f: (value: T) => T2, g: (error: E) => E2): T2; +function match(this: Result.Err, f: (value: T) => T2, g: (error: E) => E2): E2; +function match(this: Result, f: (value: T) => T2, g: (error: E) => E2): T2 | E2; function match( - this: Result.Ok, - f: (value: T) => T2, - g: (error: E) => E2, -): T2; -function match( - this: Result.Err, - f: (value: T) => T2, - g: (error: E) => E2, -): E2; -function match( - this: Result, - f: (value: T) => T2, - g: (error: E) => E2, -): T2 | E2; -function match( - this: Result, - f: (value: T) => T2, - g: (error: E) => E2, + this: Result, + f: (value: T) => T2, + g: (error: E) => E2, ): T2 | E2 { - if (this.isOk) return f(this.value); - else return g(this.error); + if (this.isOk) return f(this.value); + else return g(this.error); } function map(this: Result.Ok, f: (value: T) => T2): Result.Ok; function map(this: Result.Err, f: (value: T) => T2): Result.Err; function map(this: Result, f: (value: T) => T2): Result; function map(this: Result, f: (value: T) => T2): Result { - if (this.isErr) return this; - else return Result.ok(f(this.value)); + if (this.isErr) return this; + else return Result.ok(f(this.value)); } -function mapError( - this: Result.Ok, - f: (error: E) => E2, -): Result.Ok; -function mapError( - this: Result.Err, - f: (error: E) => E2, -): Result.Err; -function mapError( - this: Result, - f: (error: E) => E2, -): Result; -function mapError( - this: Result, - f: (error: E) => E2, -): Result { - if (this.isOk) return this; - else return Result.err(f(this.error)); +function mapError(this: Result.Ok, f: (error: E) => E2): Result.Ok; +function mapError(this: Result.Err, f: (error: E) => E2): Result.Err; +function mapError(this: Result, f: (error: E) => E2): Result; +function mapError(this: Result, f: (error: E) => E2): Result { + if (this.isOk) return this; + else return Result.err(f(this.error)); } function tap(this: Result.Err, f: (error: E) => void): Result.Err; function tap(this: Result.Ok, f: (value: T) => void): Result.Ok; function tap(this: Result, f: (value: T) => void): Result; function tap(this: Result, f: (value: T) => void): Result { - if (this.isOk) f(this.value); - return this; + if (this.isOk) f(this.value); + return this; } -function flatMap( - this: Result.Ok, - f: (value: T) => Result.Ok, -): Result.Ok; -function flatMap( - this: Result.Ok, - f: (value: T) => Result.Err, -): Result.Err; -function flatMap( - this: Result.Ok, - f: (value: T) => Result, -): Result; -function flatMap( - this: Result.Err, - f: (value: T) => Result, -): Result.Err; -function flatMap( - this: Result, - f: (value: T) => Result.Ok, -): Result; +function flatMap(this: Result.Ok, f: (value: T) => Result.Ok): Result.Ok; +function flatMap(this: Result.Ok, f: (value: T) => Result.Err): Result.Err; +function flatMap(this: Result.Ok, f: (value: T) => Result): Result; +function flatMap(this: Result.Err, f: (value: T) => Result): Result.Err; +function flatMap(this: Result, f: (value: T) => Result.Ok): Result; function flatMap( - this: Result, - f: (value: T) => Result.Err, + this: Result, + f: (value: T) => Result.Err, ): Result.Err; function flatMap( - this: Result, - f: (value: T) => Result, + this: Result, + f: (value: T) => Result, ): Result; -function flatMap( - this: Result, - f: (value: T) => Result, -) { - if (this.isErr) return this; - else return f(this.value); +function flatMap(this: Result, f: (value: T) => Result) { + if (this.isErr) return this; + else return f(this.value); } function flatten(this: Result.Err): Result.Err; @@ -304,31 +254,31 @@ function flatten(this: Result, E>): Result; function flatten(this: Result, E2>): Result.Err; function flatten(this: Result, E2>): Result; function flatten(this: Result, E2>): Result { - if (this.isErr) return this; - else return this.value; + if (this.isErr) return this; + else return this.value; } function assertErrorInstanceOf any>( - this: Result.Ok, - constructor: C, + this: Result.Ok, + constructor: C, ): Result.Ok; function assertErrorInstanceOf any>( - this: Result.Err, - constructor: C, + this: Result.Err, + constructor: C, ): Result.Err>; function assertErrorInstanceOf any>( - this: Result, - constructor: C, + this: Result, + constructor: C, ): Result>; function assertErrorInstanceOf any>( - this: Result, - constructor: C, + this: Result, + constructor: C, ): Result> { - if (this.isOk) return this; + if (this.isOk) return this; - if (this.error instanceof constructor) return this as any; + if (this.error instanceof constructor) return this as any; - throw new TypeError( - `Assertion failed: Expected error to be an instance of ${constructor.name}.`, - ); + throw new TypeError( + `Assertion failed: Expected error to be an instance of ${constructor.name}.`, + ); } diff --git a/lib/src/transaction.ts b/lib/src/transaction.ts index 8cf2e5b..92e209f 100644 --- a/lib/src/transaction.ts +++ b/lib/src/transaction.ts @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - type OplogIndex, - getOplogIndex, - setOplogIndex, -} from "./bindgen/bindgen"; +import { type OplogIndex, getOplogIndex, setOplogIndex } from "./bindgen/bindgen"; import { executeWithDrop, markAtomicOperation } from "./guard"; import { Result } from "./result"; @@ -27,21 +23,21 @@ import { Result } from "./result"; * Operations can also be constructed from closures using `operation`. */ export interface Operation { - /** - * The action to execute. - * @param input - The input to the operation. - * @returns The result of the operation. - */ - execute(input: In): Result; - - /** - * Compensation to perform in case of failure. - * Compensations should not throw errors. - * @param input - The input to the operation. - * @param result - The result of the operation. - * @returns The result of the compensation. - */ - compensate(input: In, result: Out): Result; + /** + * The action to execute. + * @param input - The input to the operation. + * @returns The result of the operation. + */ + execute(input: In): Result; + + /** + * Compensation to perform in case of failure. + * Compensations should not throw errors. + * @param input - The input to the operation. + * @param result - The result of the operation. + * @returns The result of the compensation. + */ + compensate(input: In, result: Out): Result; } /** @@ -51,114 +47,114 @@ export interface Operation { * @returns The created Operation. */ export function operation( - execute: (input: In) => Result, - compensate: (input: In, result: Out) => Result, + execute: (input: In) => Result, + compensate: (input: In, result: Out) => Result, ): Operation { - return new OperationImpl(execute, compensate); + return new OperationImpl(execute, compensate); } class OperationImpl implements Operation { - constructor( - public readonly execute: (input: In) => Result, - public readonly compensate: (input: In, result: Out) => Result, - ) {} + constructor( + public readonly execute: (input: In) => Result, + public readonly compensate: (input: In, result: Out) => Result, + ) {} } class InfallibleTransaction { - private compensations: (() => void)[] = []; - - constructor(private readonly beginOplogIndex: OplogIndex) {} - - /** - * Executes an operation within the infallible transaction. - * @param operation - The operation to execute. - * @param input - The input to the operation. - * @returns The result of the operation. - */ - execute(operation: Operation, input: In): Out { - const result = operation.execute(input); - if (result.isOk) { - this.compensations.push( - // Compensations cannot fail in infallible transactions. - () => { - const compensationResult = operation.compensate(input, result.value); - if (compensationResult.isErr) { - throw new Error("Compensation action failed"); - } - }, - ); - return result.value; - } else { - this.retry(); - throw new Error("Unreachable code"); - } - } - - private retry(): void { - // Rollback all the compensations in reverse order - for (let i = this.compensations.length - 1; i >= 0; i--) { - this.compensations[i](); - } - setOplogIndex(this.beginOplogIndex); - } + private compensations: (() => void)[] = []; + + constructor(private readonly beginOplogIndex: OplogIndex) {} + + /** + * Executes an operation within the infallible transaction. + * @param operation - The operation to execute. + * @param input - The input to the operation. + * @returns The result of the operation. + */ + execute(operation: Operation, input: In): Out { + const result = operation.execute(input); + if (result.isOk) { + this.compensations.push( + // Compensations cannot fail in infallible transactions. + () => { + const compensationResult = operation.compensate(input, result.value); + if (compensationResult.isErr) { + throw new Error("Compensation action failed"); + } + }, + ); + return result.value; + } else { + this.retry(); + throw new Error("Unreachable code"); + } + } + + private retry(): void { + // Rollback all the compensations in reverse order + for (let i = this.compensations.length - 1; i >= 0; i--) { + this.compensations[i](); + } + setOplogIndex(this.beginOplogIndex); + } } class FallibleTransaction { - private compensations: (() => Result)[] = []; - - /** - * Executes an operation within the fallible transaction. - * @param operation - The operation to execute. - * @param input - The input to the operation. - * @returns The result of the operation. - */ - execute( - operation: Operation, - input: In, - ): Result { - const result = operation.execute(input); - if (result.isOk) { - this.compensations.push(() => { - return operation.compensate(input, result.value); - }); - return result; - } else { - return result; - } - } - - /** - * Handles the failure of the fallible transaction. - * @param error - The error that caused the failure. - * @returns The transaction failure result. - */ - onFailure(error: Err): TransactionFailure { - for (let i = this.compensations.length - 1; i >= 0; i--) { - const compensationResult = this.compensations[i](); - if (compensationResult.isErr) { - return { - type: "FailedAndRolledBackPartially", - error, - compensationFailure: compensationResult.error, - }; - } - } - return { - type: "FailedAndRolledBackCompletely", - error, - }; - } + private compensations: (() => Result)[] = []; + + /** + * Executes an operation within the fallible transaction. + * @param operation - The operation to execute. + * @param input - The input to the operation. + * @returns The result of the operation. + */ + execute( + operation: Operation, + input: In, + ): Result { + const result = operation.execute(input); + if (result.isOk) { + this.compensations.push(() => { + return operation.compensate(input, result.value); + }); + return result; + } else { + return result; + } + } + + /** + * Handles the failure of the fallible transaction. + * @param error - The error that caused the failure. + * @returns The transaction failure result. + */ + onFailure(error: Err): TransactionFailure { + for (let i = this.compensations.length - 1; i >= 0; i--) { + const compensationResult = this.compensations[i](); + if (compensationResult.isErr) { + return { + type: "FailedAndRolledBackPartially", + error, + compensationFailure: compensationResult.error, + }; + } + } + return { + type: "FailedAndRolledBackCompletely", + error, + }; + } } export type TransactionResult = Result>; export type TransactionFailure = - | { type: "FailedAndRolledBackCompletely"; error: Err } - | { - type: "FailedAndRolledBackPartially"; - error: Err; - compensationFailure: Err; - }; + | { type: "FailedAndRolledBackCompletely"; error: Err } + | { + type: "FailedAndRolledBackPartially"; + error: Err; + compensationFailure: Err; + }; /** * Executes an infallible transaction. @@ -177,13 +173,11 @@ export type TransactionFailure = * @param f - The function that defines the transaction. * @returns The result of the transaction. */ -export function infallibleTransaction( - f: (tx: InfallibleTransaction) => Out, -): Out { - const guard = markAtomicOperation(); - const beginOplogIndex = getOplogIndex(); - const tx = new InfallibleTransaction(beginOplogIndex); - return executeWithDrop([guard], () => f(tx)); +export function infallibleTransaction(f: (tx: InfallibleTransaction) => Out): Out { + const guard = markAtomicOperation(); + const beginOplogIndex = getOplogIndex(); + const tx = new InfallibleTransaction(beginOplogIndex); + return executeWithDrop([guard], () => f(tx)); } /** @@ -200,19 +194,19 @@ export function infallibleTransaction( * @returns The result of the transaction. */ export function fallibleTransaction( - f: (tx: FallibleTransaction) => Result, + f: (tx: FallibleTransaction) => Result, ): TransactionResult { - const guard = markAtomicOperation(); - const tx = new FallibleTransaction(); - const execute = () => { - const result = f(tx); - if (result.isOk) { - return Result.ok(result.value); - } else { - return Result.err(tx.onFailure(result.error)); - } - }; - return executeWithDrop([guard], execute); + const guard = markAtomicOperation(); + const tx = new FallibleTransaction(); + const execute = () => { + const result = f(tx); + if (result.isOk) { + return Result.ok(result.value); + } else { + return Result.err(tx.onFailure(result.error)); + } + }; + return executeWithDrop([guard], execute); } /** @@ -237,5 +231,5 @@ export function fallibleTransaction( * */ export type OperationErrors[]> = { - [K in keyof T]: T[K] extends Operation ? Err : never; + [K in keyof T]: T[K] extends Operation ? Err : never; }[number];