Files
renovate[bot] 9d74625d93 build(deps): update dependency prettier to v3.9.6 (main) (#45815)
* build(deps): update dependency prettier to v3.9.6

| datasource | package  | from  | to    |
| ---------- | -------- | ----- | ----- |
| npm        | prettier | 3.8.1 | 3.9.6 |

* chore: prettier fixes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@mend.io>
2026-09-09 13:50:03 +00:00

889 lines
24 KiB
TypeScript

import { isBoolean, isUndefined } from '@sindresorhus/is';
import type { ZodError, output as ZodOutput, ZodType } from 'zod/v4';
import { NEVER } from 'zod/v4';
import { logger } from '../logger/index.ts';
import type { Nullish } from '../types/index.ts';
type Val = NonNullable<unknown>;
// ZodSafeParseResult is not exported from zod/v4 public API, derive from safeParse return type
type ZodSafeParseResult<T> = ReturnType<ZodType<T>['safeParse']>;
interface Ok<T extends Val> {
readonly ok: true;
readonly val: T;
readonly err?: never;
}
interface Err<E extends Val> {
readonly ok: false;
readonly err: E;
readonly val?: never;
/**
* Internal flag to indicate that the error was thrown during `.transform()`
* and will be re-thrown on `.unwrap()`.
*/
readonly _uncaught?: true;
}
type Res<T extends Val, E extends Val> = Ok<T> | Err<E>;
function isZodResult<Output extends Val>(
input: unknown,
): input is ZodSafeParseResult<Output> {
if (
// oxlint-disable-next-line renovate/prefer-is-object -- guards arbitrary callback results; isObject() matches functions, which must never be classified as zod results
typeof input !== 'object' ||
input === null ||
Object.keys(input).length !== 2 ||
!('success' in input) ||
!isBoolean(input.success)
) {
return false;
}
if (input.success) {
return 'data' in input && !isUndefined(input.data) && input.data !== null;
}
return 'error' in input;
}
function fromZodResult<ZodOutput extends Val>(
input: ZodSafeParseResult<ZodOutput>,
): Result<ZodOutput, ZodError> {
return input.success ? Result.ok(input.data) : Result.err(input.error);
}
/**
* All non-nullable values that also are not Promises nor Zod results.
* It's useful for restricting Zod results to not return `null` or `undefined`.
*/
type RawValue<T extends Val> = Exclude<
T,
ZodSafeParseResult<T> | Promise<unknown>
>;
function fromNullable<
T extends Val,
ErrForNull extends Val,
ErrForUndefined extends Val,
>(
input: Nullish<T>,
errForNull: ErrForNull,
errForUndefined: ErrForUndefined,
): Result<T, ErrForNull | ErrForUndefined> {
if (input === null) {
return Result.err(errForNull);
}
if (input === undefined) {
return Result.err(errForUndefined);
}
return Result.ok(input);
}
/**
* Class for representing a result that can fail.
*
* The mental model:
* - `.wrap()` and `.wrapNullable()` are sinks
* - `.transform()` are pipes which can be chained
* - `.unwrap()` is the point of consumption
*/
export class Result<T extends Val, E extends Val = Error> {
private readonly res: Res<T, E>;
private constructor(res: Res<T, E>) {
this.res = res;
}
static ok<T extends Val>(val: T): Result<T, never> {
return new Result({ ok: true, val });
}
static err<E extends Val>(err: E): Result<never, E> {
return new Result({ ok: false, err });
}
static _uncaught<E extends Val>(err: E): Result<never, E> {
return new Result({ ok: false, err, _uncaught: true });
}
/**
* Wrap a callback or promise in a Result in such a way that any thrown errors
* are caught and wrapped with `Result.err()` (and hence never re-thrown).
*
* In case of a promise, the `AsyncResult` is returned.
* Use `.unwrap()` to get the `Promise<Result<T, E>>` from `AsyncResult`.
*
* ```ts
*
* // SYNC
* const parse = (json: string) => Result.wrap(() => JSON.parse(json));
*
* const { val, err } = parse('{"foo": "bar"}').unwrap();
* expect(val).toEqual({ foo: 'bar' });
* expect(err).toBeUndefined();
*
* const { val, err } = parse('!!!').unwrap();
* expect(val).toBeUndefined();
* expect(err).toBeInstanceOf(SyntaxError);
*
* // ASYNC
* const request = (url: string) => Result.wrap(http.get(url));
*
* const { val, err } = await request('https://example.com').unwrap();
* expect(val).toBeString();
* expect(err).toBeUndefined();
*
* ```
*/
static wrap<T extends Val>(
zodResult: ZodSafeParseResult<T>,
): Result<T, ZodError>;
static wrap<T extends Val, E extends Val = Error>(
callback: () => RawValue<T>,
): Result<T, E>;
static wrap<T extends Val, E extends Val = Error>(
callback: () => Promise<RawValue<T>>,
): AsyncResult<T, E>;
static wrap<T extends Val, E extends Val = Error, EE extends Val = never>(
promise: Promise<Result<T, EE>>,
): AsyncResult<T, E | EE>;
static wrap<T extends Val, E extends Val = Error>(
promise: Promise<RawValue<T>>,
): AsyncResult<T, E>;
static wrap<T extends Val, E extends Val = Error, EE extends Val = never>(
input:
| ZodSafeParseResult<T>
| (() => RawValue<T>)
| (() => Promise<RawValue<T>>)
| Promise<Result<T, EE>>
| Promise<RawValue<T>>,
): Result<T, ZodError> | Result<T, E | EE> | AsyncResult<T, E | EE> {
if (isZodResult<T>(input)) {
return fromZodResult<T>(input);
}
if (input instanceof Promise) {
return AsyncResult.wrap(input);
}
try {
const result = input();
if (result instanceof Promise) {
return AsyncResult.wrap(result);
}
return Result.ok(result);
} catch (error) {
return Result.err(error);
}
}
/**
* Similar to `Result.wrap()`, but helps to undo the billion dollar mistake by
* replacing `null` or `undefined` with an error of provided type.
*
* Errors thrown inside the callback or promise are caught and wrapped with `Result.err()`,
* hence never re-thrown.
*
* Since functions and promises returning nullable can't be wrapped with `Result.wrap()`
* because `val` is constrained by being `NonNullable`, `null` and `undefined`
* must be converted to some sort of `err` value.
*
* This method does exactly this, i.g. it is the feature-rich shorthand for:
*
* ```ts
* const { val, err } = Result.wrap(() => {
* const result = callback();
* return result === null || result === undefined
* ? Result.err('oops')
* : Result.ok(result);
* }).unwrap();
* ```
*
* In case of a promise, the `AsyncResult` is returned.
*
* ```ts
*
* // SYNC
* const getHostname = (url: string) =>
* Result.wrapNullable(
* () => parseUrl(url)?.hostname,
* 'invalid-url' as const
* );
* const { val, err } = getHostname('foobar').unwrap();
* expect(val).toBeUndefined();
* expect(err).toBe('invalid-url');
*
* // ASYNC
* const { val, err } = await Result.wrapNullable(
* readLocalFile('yarn.lock'),
* 'file-read-error' as const
* ).unwrap();
*
* ```
*/
static wrapNullable<
T extends Val,
E extends Val = Error,
ErrForNullable extends Val = Error,
>(
callback: () => Nullish<T>,
errForNullable: ErrForNullable,
): Result<T, E | ErrForNullable>;
static wrapNullable<
T extends Val,
E extends Val = Error,
ErrForNull extends Val = Error,
ErrForUndefined extends Val = Error,
>(
callback: () => Nullish<T>,
errForNull: ErrForNull,
errForUndefined: ErrForUndefined,
): Result<T, E | ErrForNull | ErrForUndefined>;
static wrapNullable<
T extends Val,
E extends Val = Error,
ErrForNullable extends Val = Error,
>(
promise: Promise<Nullish<T>>,
errForNullable: ErrForNullable,
): AsyncResult<T, E | ErrForNullable>;
static wrapNullable<
T extends Val,
E extends Val = Error,
ErrForNull extends Val = Error,
ErrForUndefined extends Val = Error,
>(
promise: Promise<Nullish<T>>,
errForNull: ErrForNull,
errForUndefined: ErrForUndefined,
): AsyncResult<T, E | ErrForNull | ErrForUndefined>;
static wrapNullable<
T extends Val,
E extends Val = Error,
ErrForNullable extends Val = Error,
>(
value: Nullish<T>,
errForNullable: ErrForNullable,
): Result<T, E | ErrForNullable>;
static wrapNullable<
T extends Val,
E extends Val = Error,
ErrForNull extends Val = Error,
ErrForUndefined extends Val = Error,
>(
value: Nullish<T>,
errForNull: ErrForNull,
errForUndefined: ErrForUndefined,
): Result<T, E | ErrForNull | ErrForUndefined>;
static wrapNullable<
T extends Val,
E extends Val = Error,
ErrForNull extends Val = Error,
ErrForUndefined extends Val = Error,
>(
input: (() => Nullish<T>) | Promise<Nullish<T>> | Nullish<T>,
arg2: ErrForNull,
arg3?: ErrForUndefined,
):
| Result<T, E | ErrForNull | ErrForUndefined>
| AsyncResult<T, E | ErrForNull | ErrForUndefined> {
const errForNull = arg2;
const errForUndefined = arg3 ?? arg2;
if (input instanceof Promise) {
return AsyncResult.wrapNullable(input, errForNull, errForUndefined);
}
if (input instanceof Function) {
try {
const result = input();
return fromNullable(result, errForNull, errForUndefined);
} catch (error) {
return Result.err(error);
}
}
return fromNullable(input, errForNull, errForUndefined);
}
/**
* Returns a discriminated union for type-safe consumption of the result.
* When error was uncaught during transformation, it's being re-thrown here.
*
* ```ts
*
* const { val, err } = Result.ok('foo').unwrap();
* expect(val).toBe('foo');
* expect(err).toBeUndefined();
*
* ```
*/
unwrap(): Res<T, E> {
if (this.res.ok) {
return this.res;
}
if (this.res._uncaught) {
// TODO: fix, should only allow `Error` type
// oxlint-disable-next-line typescript/only-throw-error
throw this.res.err;
}
return this.res;
}
/**
* Returns a success value or a fallback value.
* When error was uncaught during transformation, it's being re-thrown here.
*
* ```ts
*
* const value = Result.err('bar').unwrapOr('foo');
* expect(val).toBe('foo');
*
* ```
*/
unwrapOr(fallback: T): T {
if (this.res.ok) {
return this.res.val;
}
if (this.res._uncaught) {
// TODO: fix, should only allow `Error` type
// oxlint-disable-next-line typescript/only-throw-error
throw this.res.err;
}
return fallback;
}
/**
* Returns the ok-value or throw the error.
*/
unwrapOrThrow(): T {
if (this.res.ok) {
return this.res.val;
}
// TODO: fix, should only allow `Error` type
// oxlint-disable-next-line typescript/only-throw-error
throw this.res.err;
}
/**
* Returns the ok-value or `null`.
* When error was uncaught during transformation, it's being re-thrown here.
*/
unwrapOrNull(): T | null {
if (this.res.ok) {
return this.res.val;
}
if (this.res._uncaught) {
// TODO: fix, should only allow `Error` type
// oxlint-disable-next-line typescript/only-throw-error
throw this.res.err;
}
return null;
}
/**
* Transforms the ok-value, sync or async way.
*
* Transform functions SHOULD NOT throw.
* Uncaught errors are logged and wrapped to `Result._uncaught()`,
* which leads to re-throwing them in `unwrap()`.
*
* Zod `.safeParse()` results are converted automatically.
*
* ```ts
*
* // SYNC
* const { val, err } = Result.ok('foo')
* .transform((x) => x.length)
* .unwrap();
* expect(val).toBe(3);
*
* // ASYNC
* const { val, err } = await Result.wrap(
* http.getJson('https://api.example.com/data.json')
* )
* .transform(({ body }) => body)
* .unwrap();
*
* ```
*/
transform<U extends Val, EE extends Val>(
fn: (value: T) => Result<U, E | EE>,
): Result<U, E | EE>;
transform<U extends Val, EE extends Val>(
fn: (value: T) => AsyncResult<U, E | EE>,
): AsyncResult<U, E | EE>;
transform<U extends Val>(
fn: (value: T) => ZodSafeParseResult<NonNullable<U>>,
): Result<U, E | ZodError>;
transform<U extends Val>(
fn: (value: T) => Promise<ZodSafeParseResult<NonNullable<U>>>,
): AsyncResult<U, E | ZodError>;
transform<U extends Val, EE extends Val>(
fn: (value: T) => Promise<Result<U, E | EE>>,
): AsyncResult<U, E | EE>;
transform<U extends Val>(
fn: (value: T) => Promise<RawValue<U>>,
): AsyncResult<U, E>;
transform<U extends Val>(fn: (value: T) => RawValue<U>): Result<U, E>;
transform<U extends Val, EE extends Val>(
fn: (
value: T,
) =>
| Result<U, E | EE>
| AsyncResult<U, E | EE>
| ZodSafeParseResult<NonNullable<U>>
| Promise<ZodSafeParseResult<NonNullable<U>>>
| Promise<Result<U, E | EE>>
| Promise<RawValue<U>>
| RawValue<U>,
): Result<U, E | EE | ZodError> | AsyncResult<U, E | EE | ZodError> {
if (!this.res.ok) {
return Result.err(this.res.err);
}
try {
const result = fn(this.res.val);
if (result instanceof Result) {
return result;
}
if (result instanceof AsyncResult) {
return result;
}
if (isZodResult<U>(result)) {
return fromZodResult<U>(result);
}
if (result instanceof Promise) {
return AsyncResult.wrap(result, (err) => {
logger.warn({ err }, 'Result: unhandled async transform error');
return Result._uncaught(err);
});
}
return Result.ok(result);
} catch (err) {
logger.warn({ err }, 'Result: unhandled transform error');
return Result._uncaught(err);
}
}
catch<U extends Val = T, EE extends Val = E>(
fn: (err: E) => Result<U, EE>,
): Result<T | U, EE>;
catch<U extends Val = T, EE extends Val = E>(
fn: (err: E) => AsyncResult<U, EE>,
): AsyncResult<T | U, EE>;
catch<U extends Val = T, EE extends Val = E>(
fn: (err: E) => Promise<Result<U, EE>>,
): AsyncResult<T | U, EE>;
catch<U extends Val = T, EE extends Val = E>(
fn: (err: E) => Result<U, EE> | AsyncResult<U, EE> | Promise<Result<U, EE>>,
): Result<T | U, EE> | AsyncResult<T | U, EE> {
if (this.res.ok) {
return this as never;
}
if (this.res._uncaught) {
return this as never;
}
try {
const result = fn(this.res.err);
if (result instanceof Promise) {
return AsyncResult.wrap(result, (err) => {
logger.warn(
{ err },
'Result: unexpected error in async catch handler',
);
return Result._uncaught(err);
});
}
return result;
} catch (err) {
logger.warn({ err }, 'Result: unexpected error in catch handler');
return Result._uncaught(err);
}
}
/**
* Given a `schema` and `input`, returns a `Result` with `val` being the parsed value.
* Additionally, `null` and `undefined` values are converted into Zod error.
*/
static parse<Schema extends ZodType<any, any, any>>(
input: unknown,
schema: Schema,
): Result<NonNullable<ZodOutput<Schema>>, ZodError> {
const parseResult = schema
.transform((result, ctx): NonNullable<ZodOutput<Schema>> => {
if (result === undefined) {
ctx.addIssue({
code: 'custom',
message: `Result can't accept nullish values, but input was parsed by Zod schema to undefined`,
});
return NEVER;
}
if (result === null) {
ctx.addIssue({
code: 'custom',
message: `Result can't accept nullish values, but input was parsed by Zod schema to null`,
});
return NEVER;
}
return result;
})
.safeParse(input);
return fromZodResult<NonNullable<ZodOutput<Schema>>>(parseResult);
}
/**
* Given a `schema`, returns a `Result` with `val` being the parsed value.
* Additionally, `null` and `undefined` values are converted into Zod error.
*/
parse<Schema extends ZodType<any, any, any>>(
schema: Schema,
): Result<NonNullable<ZodOutput<Schema>>, E | ZodError> {
if (this.res.ok) {
return Result.parse(this.res.val, schema);
}
const err = this.res.err;
if (this.res._uncaught) {
return Result._uncaught(err);
}
return Result.err(err);
}
/**
* Call `fn` on the `val` if the result is ok.
*/
onValue(fn: (value: T) => void): Result<T, E> {
if (this.res.ok) {
try {
fn(this.res.val);
} catch (err) {
return Result._uncaught(err);
}
}
return this;
}
/**
* Call `fn` on the `err` if the result is err.
*/
onError(fn: (err: E) => void): Result<T, E> {
if (!this.res.ok) {
try {
fn(this.res.err);
} catch (err) {
return Result._uncaught(err);
}
}
return this;
}
}
/**
* This class is being used when `Result` methods encounter async code.
* It isn't meant to be used directly, but exported for usage in type annotations.
*
* All the methods resemble `Result` methods, but work asynchronously.
*/
export class AsyncResult<T extends Val, E extends Val> implements PromiseLike<
Result<T, E>
> {
private asyncResult: Promise<Result<T, E>>;
private constructor(asyncResult: Promise<Result<T, E>>) {
this.asyncResult = asyncResult;
}
// oxlint-disable-next-line unicorn/no-thenable -- required to implement PromiseLike
then<TResult1 = Result<T, E>>(
onfulfilled?:
((value: Result<T, E>) => TResult1 | PromiseLike<TResult1>) | null,
): PromiseLike<TResult1> {
return this.asyncResult.then(onfulfilled);
}
static ok<T extends Val>(val: T): AsyncResult<T, never> {
return new AsyncResult(Promise.resolve(Result.ok(val)));
}
static err<E extends Val>(err: NonNullable<E>): AsyncResult<never, E> {
return new AsyncResult(Promise.resolve(Result.err(err)));
}
static wrap<T extends Val, E extends Val = Error, EE extends Val = never>(
promise:
| Promise<ZodSafeParseResult<T>>
| Promise<Result<T, EE>>
| Promise<RawValue<T>>,
onErr?: (err: NonNullable<E>) => Result<T, E>,
): AsyncResult<T, E | EE> {
return new AsyncResult(
promise
.then((value) => {
if (value instanceof Result) {
return value;
}
if (isZodResult<T>(value)) {
return fromZodResult<T>(value);
}
return Result.ok(value);
})
.catch((err) => {
if (onErr) {
return onErr(err);
}
return Result.err(err);
}),
);
}
static wrapNullable<
T extends Val,
E extends Val,
ErrForNull extends Val,
ErrForUndefined extends Val,
>(
promise: Promise<Nullish<T>>,
errForNull: NonNullable<ErrForNull>,
errForUndefined: NonNullable<ErrForUndefined>,
): AsyncResult<T, E | ErrForNull | ErrForUndefined> {
return new AsyncResult(
promise
.then((value) => fromNullable(value, errForNull, errForUndefined))
.catch((err) => Result.err(err)),
);
}
/**
* Returns a discriminated union for type-safe consumption of the result.
*
* ```ts
*
* const { val, err } = await Result.wrap(readFile('foo.txt')).unwrap();
* expect(val).toBe('foo');
* expect(err).toBeUndefined();
*
* ```
*/
unwrap(): Promise<Res<T, E>> {
return this.asyncResult.then<Res<T, E>>((res) => res.unwrap());
}
/**
* Returns a success value or a fallback value.
*
* ```ts
*
* const val = await Result.wrap(readFile('foo.txt')).unwrapOr('bar');
* expect(val).toBe('bar');
* expect(err).toBeUndefined();
*
* ```
*/
unwrapOr(fallback: T): Promise<T> {
return this.asyncResult.then<T>((res) => res.unwrapOr(fallback));
}
/**
* Returns the ok-value or throw the error.
*/
async unwrapOrThrow(): Promise<T> {
const result = await this.asyncResult;
return result.unwrapOrThrow();
}
/**
* Returns the ok-value or `null`.
*/
unwrapOrNull(): Promise<T | null> {
return this.asyncResult.then<T | null>((res) => res.unwrapOrNull());
}
/**
* Transforms the ok-value, sync or async way.
*
* Transform functions SHOULD NOT throw.
* Uncaught errors are logged and wrapped to `Result._uncaught()`,
* which leads to re-throwing them in `unwrap()`.
*
* Zod `.safeParse()` results are converted automatically.
*
* ```ts
*
* const { val, err } = await Result.wrap(
* http.getJson('https://api.example.com/data.json')
* )
* .transform(({ body }) => body)
* .unwrap();
*
* ```
*/
transform<U extends Val, EE extends Val>(
fn: (value: T) => Result<U, E | EE>,
): AsyncResult<U, E | EE>;
transform<U extends Val, EE extends Val>(
fn: (value: T) => AsyncResult<U, E | EE>,
): AsyncResult<U, E | EE>;
transform<U extends Val>(
fn: (value: T) => ZodSafeParseResult<NonNullable<U>>,
): AsyncResult<U, E | ZodError>;
transform<U extends Val>(
fn: (value: T) => Promise<ZodSafeParseResult<NonNullable<U>>>,
): AsyncResult<U, E | ZodError>;
transform<U extends Val, EE extends Val>(
fn: (value: T) => Promise<Result<U, E | EE>>,
): AsyncResult<U, E | EE>;
transform<U extends Val>(
fn: (value: T) => Promise<RawValue<U>>,
): AsyncResult<U, E>;
transform<U extends Val>(fn: (value: T) => RawValue<U>): AsyncResult<U, E>;
transform<U extends Val, EE extends Val>(
fn: (
value: T,
) =>
| Result<U, E | EE>
| AsyncResult<U, E | EE>
| ZodSafeParseResult<NonNullable<U>>
| Promise<ZodSafeParseResult<NonNullable<U>>>
| Promise<Result<U, E | EE>>
| Promise<RawValue<U>>
| RawValue<U>,
): AsyncResult<U, E | EE | ZodError> {
return new AsyncResult(
this.asyncResult
.then((oldResult) => {
const { ok, val: value, err: error } = oldResult.unwrap();
if (!ok) {
return Result.err(error);
}
try {
const result = fn(value);
if (result instanceof Result) {
return result;
}
if (result instanceof AsyncResult) {
return result;
}
if (isZodResult<U>(result)) {
return fromZodResult<U>(result);
}
if (result instanceof Promise) {
return AsyncResult.wrap(result, (err) => {
logger.warn(
{ err },
'AsyncResult: unhandled async transform error',
);
return Result._uncaught(err);
});
}
return Result.ok(result);
} catch (err) {
logger.warn({ err }, 'AsyncResult: unhandled transform error');
return Result._uncaught(err);
}
})
.catch((err) => {
// Happens when `.unwrap()` of `oldResult` throws
return Result._uncaught(err);
}),
);
}
catch<U extends Val = T, EE extends Val = E>(
fn: (err: NonNullable<E>) => Result<U, EE>,
): AsyncResult<T | U, EE>;
catch<U extends Val = T, EE extends Val = E>(
fn: (err: NonNullable<E>) => AsyncResult<U, EE>,
): AsyncResult<T | U, EE>;
catch<U extends Val = T, EE extends Val = E>(
fn: (err: NonNullable<E>) => Promise<Result<U, EE>>,
): AsyncResult<T | U, EE>;
catch<U extends Val = T, EE extends Val = E>(
fn: (
err: NonNullable<E>,
) => Result<U, EE> | AsyncResult<U, EE> | Promise<Result<U, EE>>,
): AsyncResult<T | U, EE> {
const caughtAsyncResult: Promise<Result<T, EE>> = this.asyncResult.then(
(result) => result.catch(fn as never),
);
return AsyncResult.wrap(caughtAsyncResult);
}
/**
* Given a `schema`, returns a `Result` with `val` being the parsed value.
* Additionally, `null` and `undefined` values are converted into Zod error.
*/
parse<Schema extends ZodType<any, any, any>>(
schema: Schema,
): AsyncResult<NonNullable<ZodOutput<Schema>>, E | ZodError> {
return new AsyncResult(
this.asyncResult
.then((oldResult) => oldResult.parse(schema))
.catch(
/* istanbul ignore next: should never happen */
(err) => Result._uncaught(err),
),
);
}
onValue(fn: (value: T) => void): AsyncResult<T, E> {
return new AsyncResult(
this.asyncResult
.then((result) => result.onValue(fn))
.catch(
/* istanbul ignore next: should never happen */
(err) => Result._uncaught(err),
),
);
}
onError(fn: (err: E) => void): AsyncResult<T, E> {
return new AsyncResult(
this.asyncResult
.then((result) => result.onError(fn))
.catch(
/* istanbul ignore next: should never happen */
(err) => Result._uncaught(err),
),
);
}
}