Skip to content

Commit

Permalink
refactor: move query logic to core
Browse files Browse the repository at this point in the history
  • Loading branch information
tien committed Jun 11, 2024
1 parent 558f246 commit b4961ac
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 84 deletions.
81 changes: 59 additions & 22 deletions packages/core/src/QueryBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { ReDotDescriptor } from "@reactive-dot/types";
import type { ChainDefinition, TypedApi } from "polkadot-api";
import { Observable } from "rxjs";
import type { Observable } from "rxjs";

type InferPapiStorageEntry<T> = T extends {
watchValue: (...args: infer Args) => Observable<infer Payload>;
watchValue: (...args: infer Args) => infer Response;
}
? { args: Args; payload: Payload }
: { args: unknown[]; payload: unknown };
? { args: Args; response: Response }
: { args: unknown[]; response: unknown };

type InferPapiStorageEntryWithKeys<T> = T extends {
getEntries: (...args: infer Args) => Promise<infer Payload>;
getEntries: (...args: infer Args) => infer Response;
}
? { args: Args; payload: Payload }
: { args: unknown[]; payload: unknown };
? { args: Args; response: Response }
: { args: unknown[]; response: unknown };

type InferPapiRuntimeCall<T> = T extends (
...args: infer Args
) => Promise<infer Payload>
? { args: Args; payload: Payload }
: { args: unknown[]; payload: unknown };
type InferPapiRuntimeCall<T> = T extends (...args: infer Args) => infer Response
? { args: Args; response: Response }
: { args: unknown[]; response: unknown };

type InferPapiConstantEntry<T> = T extends {
(): Promise<infer Payload>;
(runtime: infer _): infer Payload;
}
? Payload
? Promise<Payload>
: unknown;

type BaseInstruction<T extends string> = {
Expand Down Expand Up @@ -109,7 +107,7 @@ type StorageReadPayload<
TDescriptor extends ChainDefinition = ReDotDescriptor,
> = InferPapiStorageEntry<
TypedApi<TDescriptor>["query"][TInstruction["pallet"]][TInstruction["storage"]]
>["payload"];
>["response"];

type StorageEntriesReadPayload<
TInstruction extends StorageEntriesReadInstruction<
Expand All @@ -121,7 +119,7 @@ type StorageEntriesReadPayload<
TDescriptor extends ChainDefinition = ReDotDescriptor,
> = InferPapiStorageEntryWithKeys<
TypedApi<TDescriptor>["query"][TInstruction["pallet"]][TInstruction["storage"]]
>["payload"];
>["response"];

type ApiCallPayload<
TInstruction extends
Expand All @@ -130,9 +128,9 @@ type ApiCallPayload<
TDescriptor extends ChainDefinition = ReDotDescriptor,
> = InferPapiRuntimeCall<
TypedApi<TDescriptor>["apis"][TInstruction["pallet"]][TInstruction["api"]]
>["payload"];
>["response"];

export type InferInstruction<
export type InferInstructionResponse<
TInstruction extends QueryInstruction,
TDescriptor extends ChainDefinition = ReDotDescriptor,
> =
Expand Down Expand Up @@ -164,22 +162,61 @@ export type InferInstruction<
? ApiCallPayload<TInstruction, TDescriptor>
: never;

export type InferInstructions<
export type InferInstructionPayload<
TInstruction extends QueryInstruction,
TDescriptor extends ChainDefinition = ReDotDescriptor,
> =
InferInstructionResponse<TInstruction, TDescriptor> extends Promise<
infer Payload
>
? Payload
: InferInstructionResponse<TInstruction, TDescriptor> extends Observable<
infer Payload
>
? Payload
: InferInstructionResponse<TInstruction, TDescriptor>;

export type InferInstructionsResponse<
TInstructions extends QueryInstruction[],
TDescriptor extends ChainDefinition = ReDotDescriptor,
> = {
[P in keyof TInstructions]: InferInstructionResponse<
TInstructions[P],
TDescriptor
>;
};

export type InferInstructionsPayload<
TInstructions extends QueryInstruction[],
TDescriptor extends ChainDefinition = ReDotDescriptor,
> = {
[P in keyof TInstructions]: InferInstruction<TInstructions[P], TDescriptor>;
[P in keyof TInstructions]: InferInstructionPayload<
TInstructions[P],
TDescriptor
>;
};

export type InferQueryBuilder<T extends QueryBuilder> =
export type InferQueryBuilderResponse<T extends QueryBuilder> =
T extends QueryBuilder<infer Instructions, infer Descriptor>
? InferInstructions<Instructions, Descriptor>
? InferInstructionsResponse<Instructions, Descriptor>
: never;

export type InferQueryBuilderPayload<T extends QueryBuilder> =
T extends QueryBuilder<infer Instructions, infer Descriptor>
? InferInstructionsPayload<Instructions, Descriptor>
: never;

export type Query<
TInstructions extends QueryInstruction[] = QueryInstruction[],
> = {
readonly instructions: TInstructions;
};

export default class QueryBuilder<
const TInstructions extends QueryInstruction[] = QueryInstruction[],
TDescriptor extends ChainDefinition = ReDotDescriptor,
> {
> implements Query<TInstructions>
{
#instructions: TInstructions;

constructor(instructions: TInstructions) {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export {
default as QueryBuilder,
type InferQueryBuilder,
type InferQueryBuilderPayload,
type InferQueryBuilderResponse,
type MultiInstruction,
type QueryInstruction,
} from "./QueryBuilder.js";
export * from "./errors.js";
export { default as query } from "./query.js";
export * from "./symbols.js";
45 changes: 45 additions & 0 deletions packages/core/src/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type {
InferInstructionResponse,
QueryInstruction,
} from "./QueryBuilder.js";
import type { ReDotDescriptor } from "@reactive-dot/types";
import type { ChainDefinition, TypedApi } from "polkadot-api";

const query = <
TInstruction extends QueryInstruction,
TDescriptor extends ChainDefinition = ReDotDescriptor,
>(
api: TypedApi<TDescriptor>,
instruction: TInstruction,
options?: { signal?: AbortSignal },
): InferInstructionResponse<TInstruction> => {
switch (instruction.instruction) {
case "fetch-constant":
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (api as any).constants[instruction.pallet][
instruction.constant
]() as InferInstructionResponse<TInstruction>;
case "call-api":
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (api as any).apis[instruction.pallet][instruction.api](
...instruction.args,
{ signal: options?.signal },
) as InferInstructionResponse<TInstruction>;
case "read-storage":
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (api as any).query[instruction.pallet][
instruction.storage
].watchValue(
...instruction.args,
) as InferInstructionResponse<TInstruction>;
case "read-storage-entries":
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (api as any).query[instruction.pallet][
instruction.storage
].getEntries(...instruction.args, {
signal: options?.signal,
}) as InferInstructionResponse<TInstruction>;
}
};

export default query;
6 changes: 4 additions & 2 deletions packages/react/src/hooks/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { Falsy, FalsyGuard, FlatHead } from "../types.js";
import { flatHead, stringify } from "../utils.js";
import type { ChainHookOptions } from "./types.js";
import {
InferQueryBuilder,
QueryBuilder,
QueryError,
QueryInstruction,
type InferQueryBuilderPayload,
} from "@reactive-dot/core";
import type { ChainId, Chains, ReDotDescriptor } from "@reactive-dot/types";
import { atom, useAtomValue } from "jotai";
Expand Down Expand Up @@ -38,7 +38,9 @@ export const useQuery = <
: FalsyGuard<
ReturnType<Exclude<TQuery, Falsy>>,
FlatHead<
InferQueryBuilder<Exclude<ReturnType<Exclude<TQuery, Falsy>>, Falsy>>
InferQueryBuilderPayload<
Exclude<ReturnType<Exclude<TQuery, Falsy>>, Falsy>
>
>
> => {
const contextChainId = useContext(ChainIdContext);
Expand Down
73 changes: 14 additions & 59 deletions packages/react/src/stores/query.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { stringify } from "../utils.js";
import { typedApiAtomFamily } from "./client.js";
import {
QueryInstruction,
MultiInstruction,
QueryError,
QueryInstruction,
query,
} from "@reactive-dot/core";
import type { ChainId } from "@reactive-dot/types";
import { atom } from "jotai";
import { atomFamily, atomWithObservable } from "jotai/utils";
import { from, type Observable } from "rxjs";
import { switchMap } from "rxjs/operators";
import { from, switchMap, type Observable } from "rxjs";

const _queryAtomFamily = atomFamily(
(param: {
Expand All @@ -20,62 +20,17 @@ const _queryAtomFamily = atomFamily(
// eslint-disable-next-line @typescript-eslint/ban-types
{}>
>;
}) => {
switch (param.instruction.instruction) {
case "fetch-constant": {
const { pallet, constant } = param.instruction;

return atom(async (get) => {
const api = await get(typedApiAtomFamily(param.chainId));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (api as any).constants[pallet][constant]() as Promise<unknown>;
});
}

case "read-storage": {
const { pallet, storage, args } = param.instruction;

return atomWithObservable((get) =>
from(get(typedApiAtomFamily(param.chainId))).pipe(
switchMap(
(api) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(api as any).query[pallet][storage].watchValue(
...args,
) as Observable<unknown>,
),
),
);
}

case "read-storage-entries": {
const { pallet, storage, args } = param.instruction;

return atom(async (get, { signal }) => {
const api = await get(typedApiAtomFamily(param.chainId));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (api as any).query[pallet][storage].getEntries(...args, {
signal,
});
});
}

case "call-api": {
const { pallet, api, args } = param.instruction;

return atom(async (get, { signal }) => {
const typedApi = await get(typedApiAtomFamily(param.chainId));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (typedApi as any).apis[pallet][api](...args, {
signal,
}) as Promise<unknown>;
});
}
}
},
}) =>
atomWithObservable((get) =>
from(get(typedApiAtomFamily(param.chainId))).pipe(
switchMap(
(api) =>
query(api, param.instruction) as
| Promise<unknown>
| Observable<unknown>,
),
),
),
(a, b) => stringify(a) === stringify(b),
);

Expand Down

0 comments on commit b4961ac

Please sign in to comment.