@midnight-ntwrk/wallet-sdk-indexer-client 1.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/effect/ConnectionHelper.d.ts +4 -0
- package/dist/effect/ConnectionHelper.js +29 -0
- package/dist/effect/HttpQueryClient.d.ts +4 -0
- package/dist/effect/HttpQueryClient.js +38 -0
- package/dist/effect/Query.d.ts +46 -0
- package/dist/effect/Query.js +35 -0
- package/dist/effect/QueryClient.d.ts +15 -0
- package/dist/effect/QueryClient.js +3 -0
- package/dist/effect/Subscription.d.ts +21 -0
- package/dist/effect/Subscription.js +28 -0
- package/dist/effect/SubscriptionClient.d.ts +15 -0
- package/dist/effect/SubscriptionClient.js +3 -0
- package/dist/effect/WsSubscriptionClient.d.ts +4 -0
- package/dist/effect/WsSubscriptionClient.js +32 -0
- package/dist/effect/index.d.ts +7 -0
- package/dist/effect/index.js +7 -0
- package/dist/effect/test/httpQueryClient.spied.test.d.ts +1 -0
- package/dist/effect/test/httpQueryClient.spied.test.js +25 -0
- package/dist/effect/test/httpQueryClient.test.d.ts +1 -0
- package/dist/effect/test/httpQueryClient.test.js +20 -0
- package/dist/effect/test/wsSubscriptionClient.spied.test.d.ts +1 -0
- package/dist/effect/test/wsSubscriptionClient.spied.test.js +25 -0
- package/dist/effect/test/wsSubscriptionClient.test.d.ts +1 -0
- package/dist/effect/test/wsSubscriptionClient.test.js +20 -0
- package/dist/graphql/generated/fragment-masking.d.ts +19 -0
- package/dist/graphql/generated/fragment-masking.js +16 -0
- package/dist/graphql/generated/gql.d.ts +66 -0
- package/dist/graphql/generated/gql.js +14 -0
- package/dist/graphql/generated/graphql.d.ts +656 -0
- package/dist/graphql/generated/graphql.js +7 -0
- package/dist/graphql/generated/index.d.ts +2 -0
- package/dist/graphql/generated/index.js +2 -0
- package/dist/graphql/queries/BlockHash.d.ts +6 -0
- package/dist/graphql/queries/BlockHash.js +11 -0
- package/dist/graphql/queries/Connect.d.ts +6 -0
- package/dist/graphql/queries/Connect.js +7 -0
- package/dist/graphql/queries/Disconnect.d.ts +6 -0
- package/dist/graphql/queries/Disconnect.js +7 -0
- package/dist/graphql/queries/index.d.ts +3 -0
- package/dist/graphql/queries/index.js +3 -0
- package/dist/graphql/queries/test/BlockHash.test.d.ts +1 -0
- package/dist/graphql/queries/test/BlockHash.test.js +69 -0
- package/dist/graphql/subscriptions/DustLedgerEvents.d.ts +6 -0
- package/dist/graphql/subscriptions/DustLedgerEvents.js +12 -0
- package/dist/graphql/subscriptions/ShieldedTransactions.d.ts +8 -0
- package/dist/graphql/subscriptions/ShieldedTransactions.js +42 -0
- package/dist/graphql/subscriptions/UnshieldedTransactions.d.ts +8 -0
- package/dist/graphql/subscriptions/UnshieldedTransactions.js +49 -0
- package/dist/graphql/subscriptions/ZswapEvents.d.ts +6 -0
- package/dist/graphql/subscriptions/ZswapEvents.js +11 -0
- package/dist/graphql/subscriptions/index.d.ts +4 -0
- package/dist/graphql/subscriptions/index.js +4 -0
- package/dist/graphql/subscriptions/test/ShieldedTransactions.test.d.ts +1 -0
- package/dist/graphql/subscriptions/test/ShieldedTransactions.test.js +41 -0
- package/dist/graphql/subscriptions/test/UnshieldedTransactions.test.d.ts +1 -0
- package/dist/graphql/subscriptions/test/UnshieldedTransactions.test.js +38 -0
- package/dist/graphql/subscriptions/test/ZswapEvents.test.d.ts +1 -0
- package/dist/graphql/subscriptions/test/ZswapEvents.test.js +36 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Wallet SDK Unshielded Token Indexer Client
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import * as Either from 'effect/Either';
|
|
2
|
+
import { URLError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
3
|
+
export declare const deriveWebSocketUrl: (url: URL | string) => Either.Either<string, URLError>;
|
|
4
|
+
export declare const createWebSocketUrl: (httpUrl: URL | string, wsUrl?: string) => Either.Either<string, URLError>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as Either from 'effect/Either';
|
|
2
|
+
import { FailedToDeriveWebSocketUrlError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
3
|
+
export const deriveWebSocketUrl = (url) => {
|
|
4
|
+
const httpUrl = typeof url === 'string' ? new URL(url) : url;
|
|
5
|
+
try {
|
|
6
|
+
const wsUrl = new URL(httpUrl);
|
|
7
|
+
// Convert protocol
|
|
8
|
+
wsUrl.protocol = httpUrl.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
9
|
+
// Ensure pathname ends with '/ws'
|
|
10
|
+
if (!wsUrl.pathname.endsWith('/')) {
|
|
11
|
+
wsUrl.pathname += '/';
|
|
12
|
+
}
|
|
13
|
+
wsUrl.pathname += 'ws';
|
|
14
|
+
return Either.right(wsUrl.toString());
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
18
|
+
return Either.left(new FailedToDeriveWebSocketUrlError({
|
|
19
|
+
message: `Failed to derive WebSocket URL from ${httpUrl.toString()}`,
|
|
20
|
+
cause: error,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
export const createWebSocketUrl = (httpUrl, wsUrl) => {
|
|
25
|
+
if (wsUrl) {
|
|
26
|
+
return Either.right(wsUrl);
|
|
27
|
+
}
|
|
28
|
+
return deriveWebSocketUrl(httpUrl);
|
|
29
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Layer, Scope } from 'effect';
|
|
2
|
+
import { QueryClient } from './QueryClient.js';
|
|
3
|
+
import { InvalidProtocolSchemeError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
4
|
+
export declare const layer: (config: QueryClient.ServerConfig) => Layer.Layer<QueryClient, InvalidProtocolSchemeError, Scope.Scope>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Effect, Layer } from 'effect';
|
|
2
|
+
import { print } from 'graphql';
|
|
3
|
+
import { createClient } from 'graphql-http';
|
|
4
|
+
import { QueryClient } from './QueryClient.js';
|
|
5
|
+
import { ClientError, ServerError, HttpURL, } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
6
|
+
export const layer = (config) => Layer.effect(QueryClient, HttpURL.make(config.url).pipe(Effect.flatMap((url) => Effect.acquireRelease(Effect.sync(() => createClient({
|
|
7
|
+
url: url.toString(),
|
|
8
|
+
shouldRetry: (error, retries) => {
|
|
9
|
+
const statusCode = error.response?.status ?? 500;
|
|
10
|
+
return Promise.resolve(retries < 3 && statusCode >= 502 && statusCode <= 504);
|
|
11
|
+
},
|
|
12
|
+
})), (client) => Effect.sync(() => client.dispose()))), Effect.map((client) => new HttpQueryClientImpl(client))));
|
|
13
|
+
class HttpQueryClientImpl {
|
|
14
|
+
constructor(client) {
|
|
15
|
+
this.client = client;
|
|
16
|
+
}
|
|
17
|
+
client;
|
|
18
|
+
query(document, variables) {
|
|
19
|
+
return Effect.async((resume) => {
|
|
20
|
+
let result;
|
|
21
|
+
const dispose = this.client.subscribe({ query: print(document), variables: variables }, {
|
|
22
|
+
next: (data) => (result = data),
|
|
23
|
+
error: (error) => {
|
|
24
|
+
const statusCode = error.response?.status ?? 500;
|
|
25
|
+
const message = error.response?.statusText ?? 'An unknown error occurred';
|
|
26
|
+
resume(Effect.fail(statusCode >= 400 && statusCode < 500
|
|
27
|
+
? new ClientError({ message, cause: error })
|
|
28
|
+
: new ServerError({ message })));
|
|
29
|
+
},
|
|
30
|
+
complete: () => resume(result.errors
|
|
31
|
+
? Effect.fail(new ClientError({ message: result.errors[0].message, cause: result.errors }))
|
|
32
|
+
: Effect.succeed(result.data)),
|
|
33
|
+
});
|
|
34
|
+
// Ensure we dispose of the query if the running Fiber is terminated.
|
|
35
|
+
return Effect.sync(dispose);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Effect, Context } from 'effect';
|
|
2
|
+
import { ClientError, ServerError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
3
|
+
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
4
|
+
import { QueryClient } from './QueryClient.js';
|
|
5
|
+
/**
|
|
6
|
+
* Describes an invocable GraphQL query.
|
|
7
|
+
*/
|
|
8
|
+
export interface Query<R, V, F extends Query.QueryFn<R, V> = Query.QueryFn<R, V>> extends Effect.Effect<F> {
|
|
9
|
+
readonly tag: Context.Tag<Query<R, V>, F>;
|
|
10
|
+
readonly run: F;
|
|
11
|
+
}
|
|
12
|
+
export declare namespace Query {
|
|
13
|
+
/**
|
|
14
|
+
* A GraphQL query (that may be parameterized with variables), that returns a typed document.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam R The type returned by the GraphQL query.
|
|
17
|
+
* @typeParam V A type that describes the variables present in the GraphQL query.
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* `Document` is a simple type alias for `TypedDocumentNode`.
|
|
21
|
+
*/
|
|
22
|
+
type Document<R, V> = TypedDocumentNode<R, V>;
|
|
23
|
+
/**
|
|
24
|
+
* The variables of a {@link Document}.
|
|
25
|
+
*/
|
|
26
|
+
type Variables<T> = T extends Document<any, infer V> ? V : never;
|
|
27
|
+
/**
|
|
28
|
+
* The expected result of executing a {@link Document}.
|
|
29
|
+
*/
|
|
30
|
+
type Result<T> = T extends Document<infer R, any> ? R : never;
|
|
31
|
+
/**
|
|
32
|
+
* Describes a function that executes a GraphQL query over some given variables.
|
|
33
|
+
*
|
|
34
|
+
* @param variables The variables to be used in the GraphQL query.
|
|
35
|
+
* @returns An `Effect` that will yield the result of the GraphQL query.
|
|
36
|
+
*/
|
|
37
|
+
type QueryFn<R, V> = (variables: V) => Effect.Effect<R, ClientError | ServerError, QueryClient>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Constructs a new invocable GraphQL query.
|
|
41
|
+
*
|
|
42
|
+
* @param name The name of the tag to be associated with this query.
|
|
43
|
+
* @param document A parsed GraphQL query document that represents the query.
|
|
44
|
+
* @returns A {@link Query}.
|
|
45
|
+
*/
|
|
46
|
+
export declare const make: <Name extends string, T extends Query.Document<R, V>, R = Query.Result<T>, V = Query.Variables<T>>(name: Name, document: T) => Query<R, V>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Effect, Context, Effectable, Option, identity } from 'effect';
|
|
2
|
+
import { QueryClient } from './QueryClient.js';
|
|
3
|
+
/**
|
|
4
|
+
* Constructs a new invocable GraphQL query.
|
|
5
|
+
*
|
|
6
|
+
* @param name The name of the tag to be associated with this query.
|
|
7
|
+
* @param document A parsed GraphQL query document that represents the query.
|
|
8
|
+
* @returns A {@link Query}.
|
|
9
|
+
*/
|
|
10
|
+
export const make = (name, document) => new QueryImpl(`${name}Query`, document);
|
|
11
|
+
class QueryImpl extends Effectable.Class {
|
|
12
|
+
name;
|
|
13
|
+
document;
|
|
14
|
+
constructor(name, document) {
|
|
15
|
+
super();
|
|
16
|
+
this.document = document;
|
|
17
|
+
this.name = name;
|
|
18
|
+
this.tag = Context.GenericTag(name);
|
|
19
|
+
this.run = ((variables) => Effect.flatMap(this, (f) => f(variables)));
|
|
20
|
+
}
|
|
21
|
+
tag;
|
|
22
|
+
run;
|
|
23
|
+
commit() {
|
|
24
|
+
const self = this; // eslint-disable-line @typescript-eslint/no-this-alias
|
|
25
|
+
return Effect.gen(function* () {
|
|
26
|
+
return Option.match(yield* Effect.serviceOption(self.tag), {
|
|
27
|
+
onSome: identity,
|
|
28
|
+
onNone: () => self.defaultFn.bind(self),
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
defaultFn(variables) {
|
|
33
|
+
return QueryClient.pipe(Effect.flatMap((client) => client.query(this.document, variables)));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect, Context } from 'effect';
|
|
2
|
+
import { ClientError, ServerError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
3
|
+
import type { Query } from './Query.js';
|
|
4
|
+
declare const QueryClient_base: Context.TagClass<QueryClient, "@midnight-ntwrk/indexer-client#QueryClient", QueryClient.Service>;
|
|
5
|
+
export declare class QueryClient extends QueryClient_base {
|
|
6
|
+
}
|
|
7
|
+
export declare namespace QueryClient {
|
|
8
|
+
interface ServerConfig {
|
|
9
|
+
readonly url: URL | string;
|
|
10
|
+
}
|
|
11
|
+
interface Service {
|
|
12
|
+
query<R, V, T extends Query.Document<R, V> = Query.Document<R, V>>(document: T, variables: V): Effect.Effect<Query.Result<T>, ClientError | ServerError>;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Effect, Stream, Context } from 'effect';
|
|
2
|
+
import { ClientError, ServerError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
3
|
+
import { SubscriptionClient } from './SubscriptionClient.js';
|
|
4
|
+
import type { Query } from './Query.js';
|
|
5
|
+
/**
|
|
6
|
+
* Describes a subscription of elements from an invocable GraphQL query.
|
|
7
|
+
*/
|
|
8
|
+
export interface Subscription<R, V, F extends Subscription.SubscriptionFn<R, V> = Subscription.SubscriptionFn<R, V>> extends Effect.Effect<F> {
|
|
9
|
+
readonly tag: Context.Tag<Subscription<R, V>, F>;
|
|
10
|
+
readonly run: F;
|
|
11
|
+
}
|
|
12
|
+
export declare namespace Subscription {
|
|
13
|
+
/**
|
|
14
|
+
* Describes a function that streams a GraphQL subscription for some given variables.
|
|
15
|
+
*
|
|
16
|
+
* @param variables The variables to be used in the GraphQL query.
|
|
17
|
+
* @returns A `Stream` that will yield the elements of the GraphQL subscription.
|
|
18
|
+
*/
|
|
19
|
+
type SubscriptionFn<R, V> = (variables: V) => Stream.Stream<R, ClientError | ServerError, SubscriptionClient>;
|
|
20
|
+
}
|
|
21
|
+
export declare const make: <Name extends string, T extends Query.Document<R, V>, R = Query.Result<T>, V = Query.Variables<T>>(name: Name, document: T) => Subscription<R, V>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Effect, Stream, Context, Effectable, Option, identity } from 'effect';
|
|
2
|
+
import { SubscriptionClient } from './SubscriptionClient.js';
|
|
3
|
+
export const make = (name, document) => new SubscriptionImpl(`${name}Subscription`, document);
|
|
4
|
+
class SubscriptionImpl extends Effectable.Class {
|
|
5
|
+
name;
|
|
6
|
+
document;
|
|
7
|
+
constructor(name, document) {
|
|
8
|
+
super();
|
|
9
|
+
this.document = document;
|
|
10
|
+
this.name = name;
|
|
11
|
+
this.tag = Context.GenericTag(name);
|
|
12
|
+
this.run = ((variables) => Stream.flatMap(this, (f) => f(variables)));
|
|
13
|
+
}
|
|
14
|
+
tag;
|
|
15
|
+
run;
|
|
16
|
+
commit() {
|
|
17
|
+
const self = this; // eslint-disable-line @typescript-eslint/no-this-alias
|
|
18
|
+
return Effect.gen(function* () {
|
|
19
|
+
return Option.match(yield* Effect.serviceOption(self.tag), {
|
|
20
|
+
onSome: identity,
|
|
21
|
+
onNone: () => self.defaultFn.bind(self),
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
defaultFn(variables) {
|
|
26
|
+
return SubscriptionClient.pipe(Stream.flatMap((client) => client.subscribe(this.document, variables)));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Stream, Context } from 'effect';
|
|
2
|
+
import type { Query } from './Query.js';
|
|
3
|
+
import { ClientError, ServerError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
4
|
+
declare const SubscriptionClient_base: Context.TagClass<SubscriptionClient, "@midnight-ntwrk/indexer-client#SubscriptionClient", SubscriptionClient.Service>;
|
|
5
|
+
export declare class SubscriptionClient extends SubscriptionClient_base {
|
|
6
|
+
}
|
|
7
|
+
export declare namespace SubscriptionClient {
|
|
8
|
+
interface ServerConfig {
|
|
9
|
+
readonly url: URL | string;
|
|
10
|
+
}
|
|
11
|
+
interface Service {
|
|
12
|
+
subscribe<R, V, T extends Query.Document<R, V> = Query.Document<R, V>>(document: T, variables: V): Stream.Stream<Query.Result<T>, ClientError | ServerError>;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Layer, Scope } from 'effect';
|
|
2
|
+
import { SubscriptionClient } from './SubscriptionClient.js';
|
|
3
|
+
import { InvalidProtocolSchemeError } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
4
|
+
export declare const layer: (config: SubscriptionClient.ServerConfig) => Layer.Layer<SubscriptionClient, InvalidProtocolSchemeError, Scope.Scope>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect, Stream, Layer } from 'effect';
|
|
2
|
+
import { createClient } from 'graphql-ws';
|
|
3
|
+
import { print } from 'graphql';
|
|
4
|
+
import { SubscriptionClient } from './SubscriptionClient.js';
|
|
5
|
+
import { WsURL, ClientError, ServerError, } from '@midnight-ntwrk/wallet-sdk-utilities/networking';
|
|
6
|
+
export const layer = (config) => Layer.effect(SubscriptionClient, WsURL.make(config.url).pipe(Effect.flatMap((url) => Effect.acquireRelease(Effect.sync(() => createClient({ url: url.toString(), shouldRetry: () => true, retryAttempts: 100 })), (client) => Effect.sync(() => client.dispose()))), Effect.map((client) => new WebSocketSubscriptionClientImpl(client))));
|
|
7
|
+
class WebSocketSubscriptionClientImpl {
|
|
8
|
+
constructor(client) {
|
|
9
|
+
this.client = client;
|
|
10
|
+
}
|
|
11
|
+
client;
|
|
12
|
+
subscribe(document, variables) {
|
|
13
|
+
return Stream.async((emit) => {
|
|
14
|
+
const dispose = this.client.subscribe({ query: print(document), variables: variables }, {
|
|
15
|
+
next: (data) => {
|
|
16
|
+
if (data.errors) {
|
|
17
|
+
return void emit.fail(new ClientError({ message: data.errors[0].message, cause: data.errors }));
|
|
18
|
+
}
|
|
19
|
+
void emit.single(data.data);
|
|
20
|
+
},
|
|
21
|
+
error: (err) => {
|
|
22
|
+
void emit.fail(Array.isArray(err)
|
|
23
|
+
? new ClientError({ message: err[0].message })
|
|
24
|
+
: new ServerError({ message: String(err) }));
|
|
25
|
+
},
|
|
26
|
+
complete: () => void emit.end(),
|
|
27
|
+
});
|
|
28
|
+
// Ensure we dispose of the query if the running Fiber is terminated.
|
|
29
|
+
return Effect.sync(dispose);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as Query from './Query.js';
|
|
2
|
+
export * as Subscription from './Subscription.js';
|
|
3
|
+
export * from './QueryClient.js';
|
|
4
|
+
export * as HttpQueryClient from './HttpQueryClient.js';
|
|
5
|
+
export * from './SubscriptionClient.js';
|
|
6
|
+
export * as WsSubscriptionClient from './WsSubscriptionClient.js';
|
|
7
|
+
export * as ConnectionHelper from './ConnectionHelper.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as Query from './Query.js';
|
|
2
|
+
export * as Subscription from './Subscription.js';
|
|
3
|
+
export * from './QueryClient.js';
|
|
4
|
+
export * as HttpQueryClient from './HttpQueryClient.js';
|
|
5
|
+
export * from './SubscriptionClient.js';
|
|
6
|
+
export * as WsSubscriptionClient from './WsSubscriptionClient.js';
|
|
7
|
+
export * as ConnectionHelper from './ConnectionHelper.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import * as QueryClient from '../QueryClient.js';
|
|
4
|
+
vi.mock('graphql-http', () => ({
|
|
5
|
+
createClient: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
describe('HttpQueryClient', () => {
|
|
8
|
+
describe('layer', () => {
|
|
9
|
+
it('disposes of underlying scoped client', async () => {
|
|
10
|
+
const graphqlHTTP = await import('graphql-http');
|
|
11
|
+
const SpiedHttpQueryClient = await import('../HttpQueryClient.js');
|
|
12
|
+
const dispose = vi.fn();
|
|
13
|
+
const spiedCreateClient = vi
|
|
14
|
+
.spyOn(graphqlHTTP, 'createClient')
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
|
|
16
|
+
.mockImplementationOnce(() => ({ dispose }));
|
|
17
|
+
await Effect.gen(function* () {
|
|
18
|
+
const client = yield* QueryClient.QueryClient;
|
|
19
|
+
expect(client).toBeDefined();
|
|
20
|
+
}).pipe(Effect.provide(SpiedHttpQueryClient.layer({ url: 'http://localhost.com' })), Effect.scoped, Effect.runPromise);
|
|
21
|
+
expect(spiedCreateClient).toHaveBeenCalled();
|
|
22
|
+
expect(dispose).toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { describe, it } from 'vitest';
|
|
3
|
+
import * as QueryClient from '../QueryClient.js';
|
|
4
|
+
import * as HttpQueryClient from '../HttpQueryClient.js';
|
|
5
|
+
describe('HttpQueryClient', () => {
|
|
6
|
+
describe('layer', () => {
|
|
7
|
+
// Ensures that we cannot construct a layer for HttpQueryClient when we use common incorrect URI schemes.
|
|
8
|
+
it.each(['ftp:', 'mailto:', 'ws:', 'wss:', 'file:'])('should fail when constructed with %s as the URI scheme', async (scheme) => {
|
|
9
|
+
await Effect.gen(function* () {
|
|
10
|
+
// We should never be able to resolve a QueryClient since the configuration used to create the
|
|
11
|
+
// associated HttpQueryClient layer is invalid with the protocol schemes being used.
|
|
12
|
+
return yield* QueryClient.QueryClient;
|
|
13
|
+
}).pipe(Effect.flatMap((_) => Effect.fail('Unexpectedly resolved a QueryClient')), Effect.provide(HttpQueryClient.layer({ url: `${scheme}//localhost.com` })),
|
|
14
|
+
// Ensure the reported invalid protocol scheme is the one used.
|
|
15
|
+
Effect.catchTag('InvalidProtocolSchemeError', (err) => err.invalidScheme !== scheme
|
|
16
|
+
? Effect.fail(`Expected '${scheme}' but received '${err.invalidScheme}'`)
|
|
17
|
+
: Effect.succeed(void 0)), Effect.scoped, Effect.runPromise);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import * as SubscriptionClient from '../SubscriptionClient.js';
|
|
4
|
+
vi.mock('graphql-ws', () => ({
|
|
5
|
+
createClient: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
describe('WsSubscriptionClient', () => {
|
|
8
|
+
describe('layer', () => {
|
|
9
|
+
it('disposes of underlying scoped client', async () => {
|
|
10
|
+
const graphqlWS = await import('graphql-ws');
|
|
11
|
+
const SpiedWsSubscriptionClient = await import('../WsSubscriptionClient.js');
|
|
12
|
+
const dispose = vi.fn();
|
|
13
|
+
const spiedCreateClient = vi
|
|
14
|
+
.spyOn(graphqlWS, 'createClient')
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
|
|
16
|
+
.mockImplementationOnce(() => ({ dispose }));
|
|
17
|
+
await Effect.gen(function* () {
|
|
18
|
+
const client = yield* SubscriptionClient.SubscriptionClient;
|
|
19
|
+
expect(client).toBeDefined();
|
|
20
|
+
}).pipe(Effect.provide(SpiedWsSubscriptionClient.layer({ url: 'ws://localhost.com' })), Effect.scoped, Effect.runPromise);
|
|
21
|
+
expect(spiedCreateClient).toHaveBeenCalled();
|
|
22
|
+
expect(dispose).toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { describe, it } from 'vitest';
|
|
3
|
+
import * as SubscriptionClient from '../SubscriptionClient.js';
|
|
4
|
+
import * as WsSubscriptionClient from '../WsSubscriptionClient.js';
|
|
5
|
+
describe('WsSubscriptionClient', () => {
|
|
6
|
+
describe('layer', () => {
|
|
7
|
+
// Ensures that we cannot construct a layer for WsSubscriptionClient when we use common incorrect URI schemes.
|
|
8
|
+
it.each(['ftp:', 'mailto:', 'http:', 'https:', 'file:'])('should fail when constructed with %s as the URI scheme', async (scheme) => {
|
|
9
|
+
await Effect.gen(function* () {
|
|
10
|
+
// We should never be able to resolve a SubscriptionClient since the configuration used to create the
|
|
11
|
+
// associated WsSubscriptionClient layer is invalid with the protocol schemes being used.
|
|
12
|
+
return yield* SubscriptionClient.SubscriptionClient;
|
|
13
|
+
}).pipe(Effect.flatMap((_) => Effect.fail('Unexpectedly resolved a SubscriptionClient')), Effect.provide(WsSubscriptionClient.layer({ url: `${scheme}//localhost.com` })),
|
|
14
|
+
// Ensure the reported invalid protocol scheme is the one used.
|
|
15
|
+
Effect.catchTag('InvalidProtocolSchemeError', (err) => err.invalidScheme !== scheme
|
|
16
|
+
? Effect.fail(`Expected '${scheme}' but received '${err.invalidScheme}'`)
|
|
17
|
+
: Effect.succeed(void 0)), Effect.scoped, Effect.runPromise);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
2
|
+
import type { Incremental } from './graphql.js';
|
|
3
|
+
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<infer TType, any> ? [TType] extends [{
|
|
4
|
+
' $fragmentName'?: infer TKey;
|
|
5
|
+
}] ? TKey extends string ? {
|
|
6
|
+
' $fragmentRefs'?: {
|
|
7
|
+
[key in TKey]: TType;
|
|
8
|
+
};
|
|
9
|
+
} : never : never : never;
|
|
10
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>): TType;
|
|
11
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined): TType | undefined;
|
|
12
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null): TType | null;
|
|
13
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined): TType | null | undefined;
|
|
14
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>): Array<TType>;
|
|
15
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined): Array<TType> | null | undefined;
|
|
16
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>): ReadonlyArray<TType>;
|
|
17
|
+
export declare function useFragment<TType>(_documentNode: DocumentTypeDecoration<TType, any>, fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined): ReadonlyArray<TType> | null | undefined;
|
|
18
|
+
export declare function makeFragmentData<F extends DocumentTypeDecoration<any, any>, FT extends ResultOf<F>>(data: FT, _fragment: F): FragmentType<F>;
|
|
19
|
+
export declare function isFragmentReady<TQuery, TFrag>(queryNode: DocumentTypeDecoration<TQuery, any>, fragmentNode: TypedDocumentNode<TFrag>, data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined): data is FragmentType<typeof fragmentNode>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function useFragment(_documentNode, fragmentType) {
|
|
2
|
+
return fragmentType;
|
|
3
|
+
}
|
|
4
|
+
export function makeFragmentData(data, _fragment) {
|
|
5
|
+
return data;
|
|
6
|
+
}
|
|
7
|
+
export function isFragmentReady(queryNode, fragmentNode, data) {
|
|
8
|
+
const deferredFields = queryNode.__meta__
|
|
9
|
+
?.deferredFields;
|
|
10
|
+
if (!deferredFields)
|
|
11
|
+
return true;
|
|
12
|
+
const fragDef = fragmentNode.definitions[0];
|
|
13
|
+
const fragName = fragDef?.name?.value;
|
|
14
|
+
const fields = (fragName && deferredFields[fragName]) || [];
|
|
15
|
+
return fields.length > 0 && fields.every((field) => data && field in data);
|
|
16
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as types from './graphql.js';
|
|
2
|
+
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
3
|
+
/**
|
|
4
|
+
* Map of all GraphQL operations in the project.
|
|
5
|
+
*
|
|
6
|
+
* This map has several performance disadvantages:
|
|
7
|
+
* 1. It is not tree-shakeable, so it will include all operations in the project.
|
|
8
|
+
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
|
|
9
|
+
* 3. It does not support dead code elimination, so it will add unused operations.
|
|
10
|
+
*
|
|
11
|
+
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
|
12
|
+
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
|
|
13
|
+
*/
|
|
14
|
+
type Documents = {
|
|
15
|
+
'\n query BlockHash($offset: BlockOffset) {\n block(offset: $offset) {\n height\n hash\n ledgerParameters\n }\n }\n ': typeof types.BlockHashDocument;
|
|
16
|
+
'\n mutation Connect($viewingKey: ViewingKey!) {\n connect(viewingKey: $viewingKey)\n }\n ': typeof types.ConnectDocument;
|
|
17
|
+
'\n mutation Disconnect($sessionId: HexEncoded!) {\n disconnect(sessionId: $sessionId)\n }\n ': typeof types.DisconnectDocument;
|
|
18
|
+
'\n subscription DustLedgerEvents($id: Int) {\n dustLedgerEvents(id: $id) {\n type: __typename\n id\n raw\n maxId\n }\n }\n ': typeof types.DustLedgerEventsDocument;
|
|
19
|
+
'\n subscription ShieldedTransactions($sessionId: HexEncoded!, $index: Int) {\n shieldedTransactions(sessionId: $sessionId, index: $index) {\n __typename\n ... on ShieldedTransactionsProgress {\n highestEndIndex\n highestCheckedEndIndex\n highestRelevantEndIndex\n }\n ... on RelevantTransaction {\n transaction {\n id\n raw\n hash\n protocolVersion\n identifiers\n startIndex\n endIndex\n fees {\n paidFees\n estimatedFees\n }\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n collapsedMerkleTree {\n startIndex\n endIndex\n update\n protocolVersion\n }\n }\n }\n }\n ': typeof types.ShieldedTransactionsDocument;
|
|
20
|
+
'\n subscription UnshieldedTransactions($address: UnshieldedAddress!, $transactionId: Int) {\n unshieldedTransactions(address: $address, transactionId: $transactionId) {\n ... on UnshieldedTransaction {\n type: __typename\n transaction {\n type: __typename\n id\n hash\n protocolVersion\n ... on RegularTransaction {\n identifiers\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n }\n createdUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n spentUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n }\n ... on UnshieldedTransactionsProgress {\n type: __typename\n highestTransactionId\n }\n }\n }\n ': typeof types.UnshieldedTransactionsDocument;
|
|
21
|
+
'\n subscription ZswapEvents($id: Int) {\n zswapLedgerEvents(id: $id) {\n id\n raw\n maxId\n }\n }\n ': typeof types.ZswapEventsDocument;
|
|
22
|
+
};
|
|
23
|
+
declare const documents: Documents;
|
|
24
|
+
/**
|
|
25
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
26
|
+
*
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const query = gql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* The query argument is unknown!
|
|
34
|
+
* Please regenerate the types.
|
|
35
|
+
*/
|
|
36
|
+
export declare function gql(source: string): unknown;
|
|
37
|
+
/**
|
|
38
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
39
|
+
*/
|
|
40
|
+
export declare function gql(source: '\n query BlockHash($offset: BlockOffset) {\n block(offset: $offset) {\n height\n hash\n ledgerParameters\n }\n }\n '): (typeof documents)['\n query BlockHash($offset: BlockOffset) {\n block(offset: $offset) {\n height\n hash\n ledgerParameters\n }\n }\n '];
|
|
41
|
+
/**
|
|
42
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
43
|
+
*/
|
|
44
|
+
export declare function gql(source: '\n mutation Connect($viewingKey: ViewingKey!) {\n connect(viewingKey: $viewingKey)\n }\n '): (typeof documents)['\n mutation Connect($viewingKey: ViewingKey!) {\n connect(viewingKey: $viewingKey)\n }\n '];
|
|
45
|
+
/**
|
|
46
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
47
|
+
*/
|
|
48
|
+
export declare function gql(source: '\n mutation Disconnect($sessionId: HexEncoded!) {\n disconnect(sessionId: $sessionId)\n }\n '): (typeof documents)['\n mutation Disconnect($sessionId: HexEncoded!) {\n disconnect(sessionId: $sessionId)\n }\n '];
|
|
49
|
+
/**
|
|
50
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
51
|
+
*/
|
|
52
|
+
export declare function gql(source: '\n subscription DustLedgerEvents($id: Int) {\n dustLedgerEvents(id: $id) {\n type: __typename\n id\n raw\n maxId\n }\n }\n '): (typeof documents)['\n subscription DustLedgerEvents($id: Int) {\n dustLedgerEvents(id: $id) {\n type: __typename\n id\n raw\n maxId\n }\n }\n '];
|
|
53
|
+
/**
|
|
54
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
55
|
+
*/
|
|
56
|
+
export declare function gql(source: '\n subscription ShieldedTransactions($sessionId: HexEncoded!, $index: Int) {\n shieldedTransactions(sessionId: $sessionId, index: $index) {\n __typename\n ... on ShieldedTransactionsProgress {\n highestEndIndex\n highestCheckedEndIndex\n highestRelevantEndIndex\n }\n ... on RelevantTransaction {\n transaction {\n id\n raw\n hash\n protocolVersion\n identifiers\n startIndex\n endIndex\n fees {\n paidFees\n estimatedFees\n }\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n collapsedMerkleTree {\n startIndex\n endIndex\n update\n protocolVersion\n }\n }\n }\n }\n '): (typeof documents)['\n subscription ShieldedTransactions($sessionId: HexEncoded!, $index: Int) {\n shieldedTransactions(sessionId: $sessionId, index: $index) {\n __typename\n ... on ShieldedTransactionsProgress {\n highestEndIndex\n highestCheckedEndIndex\n highestRelevantEndIndex\n }\n ... on RelevantTransaction {\n transaction {\n id\n raw\n hash\n protocolVersion\n identifiers\n startIndex\n endIndex\n fees {\n paidFees\n estimatedFees\n }\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n collapsedMerkleTree {\n startIndex\n endIndex\n update\n protocolVersion\n }\n }\n }\n }\n '];
|
|
57
|
+
/**
|
|
58
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
59
|
+
*/
|
|
60
|
+
export declare function gql(source: '\n subscription UnshieldedTransactions($address: UnshieldedAddress!, $transactionId: Int) {\n unshieldedTransactions(address: $address, transactionId: $transactionId) {\n ... on UnshieldedTransaction {\n type: __typename\n transaction {\n type: __typename\n id\n hash\n protocolVersion\n ... on RegularTransaction {\n identifiers\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n }\n createdUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n spentUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n }\n ... on UnshieldedTransactionsProgress {\n type: __typename\n highestTransactionId\n }\n }\n }\n '): (typeof documents)['\n subscription UnshieldedTransactions($address: UnshieldedAddress!, $transactionId: Int) {\n unshieldedTransactions(address: $address, transactionId: $transactionId) {\n ... on UnshieldedTransaction {\n type: __typename\n transaction {\n type: __typename\n id\n hash\n protocolVersion\n ... on RegularTransaction {\n identifiers\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n }\n createdUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n spentUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n }\n ... on UnshieldedTransactionsProgress {\n type: __typename\n highestTransactionId\n }\n }\n }\n '];
|
|
61
|
+
/**
|
|
62
|
+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
63
|
+
*/
|
|
64
|
+
export declare function gql(source: '\n subscription ZswapEvents($id: Int) {\n zswapLedgerEvents(id: $id) {\n id\n raw\n maxId\n }\n }\n '): (typeof documents)['\n subscription ZswapEvents($id: Int) {\n zswapLedgerEvents(id: $id) {\n id\n raw\n maxId\n }\n }\n '];
|
|
65
|
+
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode<infer TType, any> ? TType : never;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as types from './graphql.js';
|
|
3
|
+
const documents = {
|
|
4
|
+
'\n query BlockHash($offset: BlockOffset) {\n block(offset: $offset) {\n height\n hash\n ledgerParameters\n }\n }\n ': types.BlockHashDocument,
|
|
5
|
+
'\n mutation Connect($viewingKey: ViewingKey!) {\n connect(viewingKey: $viewingKey)\n }\n ': types.ConnectDocument,
|
|
6
|
+
'\n mutation Disconnect($sessionId: HexEncoded!) {\n disconnect(sessionId: $sessionId)\n }\n ': types.DisconnectDocument,
|
|
7
|
+
'\n subscription DustLedgerEvents($id: Int) {\n dustLedgerEvents(id: $id) {\n type: __typename\n id\n raw\n maxId\n }\n }\n ': types.DustLedgerEventsDocument,
|
|
8
|
+
'\n subscription ShieldedTransactions($sessionId: HexEncoded!, $index: Int) {\n shieldedTransactions(sessionId: $sessionId, index: $index) {\n __typename\n ... on ShieldedTransactionsProgress {\n highestEndIndex\n highestCheckedEndIndex\n highestRelevantEndIndex\n }\n ... on RelevantTransaction {\n transaction {\n id\n raw\n hash\n protocolVersion\n identifiers\n startIndex\n endIndex\n fees {\n paidFees\n estimatedFees\n }\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n collapsedMerkleTree {\n startIndex\n endIndex\n update\n protocolVersion\n }\n }\n }\n }\n ': types.ShieldedTransactionsDocument,
|
|
9
|
+
'\n subscription UnshieldedTransactions($address: UnshieldedAddress!, $transactionId: Int) {\n unshieldedTransactions(address: $address, transactionId: $transactionId) {\n ... on UnshieldedTransaction {\n type: __typename\n transaction {\n type: __typename\n id\n hash\n protocolVersion\n ... on RegularTransaction {\n identifiers\n transactionResult {\n status\n segments {\n id\n success\n }\n }\n }\n }\n createdUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n spentUtxos {\n owner\n tokenType\n value\n outputIndex\n intentHash\n ctime\n registeredForDustGeneration\n }\n }\n ... on UnshieldedTransactionsProgress {\n type: __typename\n highestTransactionId\n }\n }\n }\n ': types.UnshieldedTransactionsDocument,
|
|
10
|
+
'\n subscription ZswapEvents($id: Int) {\n zswapLedgerEvents(id: $id) {\n id\n raw\n maxId\n }\n }\n ': types.ZswapEventsDocument,
|
|
11
|
+
};
|
|
12
|
+
export function gql(source) {
|
|
13
|
+
return documents[source] ?? {};
|
|
14
|
+
}
|