mirror of
https://github.com/bitwarden/browser
synced 2026-01-31 08:43:54 +00:00
feat: add types v1
This commit is contained in:
77
libs/common/src/platform/services/sdk/chainable-promise.ts
Normal file
77
libs/common/src/platform/services/sdk/chainable-promise.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
type AsChainable<T> = T extends object
|
||||
? T extends Promise<T>
|
||||
? T & { await: AwaitProxy<T> }
|
||||
: Promise<T> & { await: AwaitProxy<T> }
|
||||
: Promise<T>;
|
||||
|
||||
type AwaitProxy<T> = {
|
||||
// Methods that return Promise<R> -> (...args) => Promise<R> (and if R is object, re-wrap to Chainable)
|
||||
[K in keyof T]: T[K] extends (...args: infer A) => Promise<infer R>
|
||||
? (...args: A) => AsChainable<R>
|
||||
: // Sync methods -> (...args) => Promise<R> (and re-wrap objects)
|
||||
T[K] extends (...args: infer A) => infer R
|
||||
? (...args: A) => AsChainable<R>
|
||||
: // Properties -> Promise<T[K]>
|
||||
Promise<T[K]>;
|
||||
};
|
||||
|
||||
export type ChainablePromise<T> = T extends object
|
||||
? T extends Promise<T>
|
||||
? T & { await: AwaitProxy<T> }
|
||||
: Promise<T> & { await: AwaitProxy<T> }
|
||||
: Promise<T>;
|
||||
|
||||
export function chain<T extends object>(p: Promise<T>): ChainablePromise<T> {
|
||||
const promise: any = p;
|
||||
|
||||
if (!promise.await) {
|
||||
const wrapIfObject = <U>(x: U): any =>
|
||||
typeof x === "object" && x !== null ? chain(Promise.resolve(x as any)) : x;
|
||||
|
||||
promise.await = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_t, prop: string | symbol) {
|
||||
return (...args: any[]) =>
|
||||
Promise.resolve(p).then(async (obj) => {
|
||||
const member = (obj as any)[prop];
|
||||
if (typeof member === "function") {
|
||||
const result = await member.apply(obj, args);
|
||||
return wrapIfObject(result);
|
||||
}
|
||||
// property access
|
||||
return wrapIfObject(member);
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
class A {
|
||||
method(): ChainablePromise<B> {
|
||||
return chain(B.createB());
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
static async createB(): Promise<B> {
|
||||
return new B();
|
||||
}
|
||||
|
||||
async asyncMethod(): Promise<B> {
|
||||
return new B();
|
||||
}
|
||||
|
||||
syncMethod(): number {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async function testChainable() {
|
||||
const objectA = new A();
|
||||
return objectA.method().await.asyncMethod().await.syncMethod();
|
||||
}
|
||||
11
libs/common/src/platform/services/sdk/remote-sdk.service.ts
Normal file
11
libs/common/src/platform/services/sdk/remote-sdk.service.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { BitwardenClient, FolderView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { Remote } from "./remote";
|
||||
|
||||
export type RemoteSdk = Remote<BitwardenClient>;
|
||||
|
||||
const remoteClient: RemoteSdk = {} as RemoteSdk;
|
||||
|
||||
export async function test(): Promise<FolderView[]> {
|
||||
return await remoteClient.vault().await.folders().await.list();
|
||||
}
|
||||
21
libs/common/src/platform/services/sdk/remote.ts
Normal file
21
libs/common/src/platform/services/sdk/remote.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ChainablePromise } from "./chainable-promise";
|
||||
|
||||
export type Remote<T> = {
|
||||
[K in keyof T]: RemoteProperty<T[K]>;
|
||||
};
|
||||
|
||||
export type RemoteProperty<T> = T extends (...args: any[]) => any
|
||||
? RemoteFunction<T>
|
||||
: RemoteValue<T>;
|
||||
|
||||
export type RemoteReference<T> = Remote<T>;
|
||||
|
||||
export type RemoteValue<T> = T extends { free(): void }
|
||||
? ChainablePromise<RemoteReference<T>>
|
||||
: T extends Promise<infer R>
|
||||
? Promise<R>
|
||||
: Promise<T>;
|
||||
|
||||
export type RemoteFunction<T extends (...args: any[]) => any> = (
|
||||
...args: Parameters<T>
|
||||
) => RemoteValue<ReturnType<T>>;
|
||||
Reference in New Issue
Block a user