@reqquest/ui 1.0.0
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 +3 -0
- package/dist/api.d.ts +595 -0
- package/dist/api.js +618 -0
- package/dist/components/ButtonLoadingIcon.svelte +27 -0
- package/dist/components/ButtonLoadingIcon.svelte.d.ts +18 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/registry.d.ts +138 -0
- package/dist/registry.js +42 -0
- package/dist/stores/IStateStore.d.ts +5 -0
- package/dist/stores/IStateStore.js +1 -0
- package/dist/typed-client/index.d.ts +25 -0
- package/dist/typed-client/index.js +23 -0
- package/dist/typed-client/runtime/batcher.d.ts +105 -0
- package/dist/typed-client/runtime/batcher.js +203 -0
- package/dist/typed-client/runtime/createClient.d.ts +17 -0
- package/dist/typed-client/runtime/createClient.js +24 -0
- package/dist/typed-client/runtime/error.d.ts +18 -0
- package/dist/typed-client/runtime/error.js +19 -0
- package/dist/typed-client/runtime/fetcher.d.ts +10 -0
- package/dist/typed-client/runtime/fetcher.js +65 -0
- package/dist/typed-client/runtime/generateGraphqlOperation.d.ts +30 -0
- package/dist/typed-client/runtime/generateGraphqlOperation.js +128 -0
- package/dist/typed-client/runtime/index.d.ts +11 -0
- package/dist/typed-client/runtime/index.js +10 -0
- package/dist/typed-client/runtime/linkTypeMap.d.ts +9 -0
- package/dist/typed-client/runtime/linkTypeMap.js +95 -0
- package/dist/typed-client/runtime/typeSelection.d.ts +28 -0
- package/dist/typed-client/runtime/typeSelection.js +3 -0
- package/dist/typed-client/runtime/types.d.ts +55 -0
- package/dist/typed-client/runtime/types.js +2 -0
- package/dist/typed-client/schema.d.ts +1483 -0
- package/dist/typed-client/schema.graphql +1217 -0
- package/dist/typed-client/schema.js +313 -0
- package/dist/typed-client/types.d.ts +540 -0
- package/dist/typed-client/types.js +1368 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.js +3 -0
- package/package.json +56 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { LayoutStructureNode, LayoutStructureNodeRoot } from '@txstate-mws/carbon-svelte';
|
|
2
|
+
import type { SvelteComponent } from 'svelte';
|
|
3
|
+
export interface ProgramDefinition {
|
|
4
|
+
/**
|
|
5
|
+
* The key for the program. Allows us to match up API programs with
|
|
6
|
+
* the appropriate definition.
|
|
7
|
+
*/
|
|
8
|
+
key: string;
|
|
9
|
+
/**
|
|
10
|
+
* An icon to represent this program in the navigation.
|
|
11
|
+
*/
|
|
12
|
+
icon?: typeof SvelteComponent<any>;
|
|
13
|
+
}
|
|
14
|
+
export interface RequirementDefinition {
|
|
15
|
+
/**
|
|
16
|
+
* The key for the requirement. Allows us to match up API requirements with
|
|
17
|
+
* the appropriate definition.
|
|
18
|
+
*/
|
|
19
|
+
key: string;
|
|
20
|
+
/**
|
|
21
|
+
* An icon to represent this requirement in the navigation. Requirements do not appear
|
|
22
|
+
* in the navigation in the applicant's view.
|
|
23
|
+
*/
|
|
24
|
+
icon?: typeof SvelteComponent<any>;
|
|
25
|
+
/**
|
|
26
|
+
* A component that will be used to render the form for this prompt's configuration, if applicable.
|
|
27
|
+
*/
|
|
28
|
+
configureComponent?: typeof SvelteComponent<any>;
|
|
29
|
+
}
|
|
30
|
+
export interface PromptDefinition {
|
|
31
|
+
key: string;
|
|
32
|
+
/**
|
|
33
|
+
* The component that will be used to render the form for this prompt.
|
|
34
|
+
*/
|
|
35
|
+
formComponent: typeof SvelteComponent<any>;
|
|
36
|
+
/**
|
|
37
|
+
* A component that displays the data collected from this prompt instead of collecting
|
|
38
|
+
* it. Should be as compact as possible, as it will be displayed in a big list of prompts.
|
|
39
|
+
*/
|
|
40
|
+
displayComponent: typeof SvelteComponent<any>;
|
|
41
|
+
/**
|
|
42
|
+
* A component that will be used to render the form for this prompt's configuration, if applicable.
|
|
43
|
+
*/
|
|
44
|
+
configureComponent?: typeof SvelteComponent<any>;
|
|
45
|
+
/**
|
|
46
|
+
* An icon for the navigation.
|
|
47
|
+
*/
|
|
48
|
+
icon?: typeof SvelteComponent<any>;
|
|
49
|
+
}
|
|
50
|
+
export interface Terminologies {
|
|
51
|
+
/**
|
|
52
|
+
* The name of the container for all the applications/programes.
|
|
53
|
+
*
|
|
54
|
+
* Defaults to "App Request", but can be changed to something like "Request" or "Application".
|
|
55
|
+
* "Application" works well if the project only has one program.
|
|
56
|
+
*/
|
|
57
|
+
appRequest?: string;
|
|
58
|
+
/**
|
|
59
|
+
* What to call the login for each user.
|
|
60
|
+
*
|
|
61
|
+
* Defaults to "Login", but can be changed to something like "Username" or "Email" or something
|
|
62
|
+
* unique to the organization like "NetID".
|
|
63
|
+
*/
|
|
64
|
+
login?: string;
|
|
65
|
+
/**
|
|
66
|
+
* The name of the time periods that applications are placed inside.
|
|
67
|
+
*
|
|
68
|
+
* Defaults to "Period", but can be changed to something like "Year" or "Term" or "Application
|
|
69
|
+
* Window".
|
|
70
|
+
*/
|
|
71
|
+
period?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A type for the config object that should be exported from a CMS instance's admin/local/index.js
|
|
75
|
+
* to configure how that instance should work.
|
|
76
|
+
*/
|
|
77
|
+
export interface UIConfig {
|
|
78
|
+
programs: ProgramDefinition[];
|
|
79
|
+
requirements: RequirementDefinition[];
|
|
80
|
+
prompts: PromptDefinition[];
|
|
81
|
+
appName: string;
|
|
82
|
+
applicantDashboardTitle?: string;
|
|
83
|
+
applicantDashboardNavTitle?: string;
|
|
84
|
+
extraNavItems?: LayoutStructureNodeRoot<LayoutStructureNode>[];
|
|
85
|
+
/**
|
|
86
|
+
* These options give you the ability to customize the terminology used in the UI.
|
|
87
|
+
*
|
|
88
|
+
* This is useful for changing the wording to better fit your project's
|
|
89
|
+
* context.
|
|
90
|
+
*/
|
|
91
|
+
terminology?: Terminologies & {
|
|
92
|
+
/**
|
|
93
|
+
* Optionally, provide plural forms for each of the above. By default we will use a pluralization
|
|
94
|
+
* library.
|
|
95
|
+
*/
|
|
96
|
+
plural?: Terminologies;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Several spots in the UI allow you to provide custom components to add extra data or functionality.
|
|
100
|
+
*
|
|
101
|
+
* Specify the components here.
|
|
102
|
+
*/
|
|
103
|
+
slots?: {
|
|
104
|
+
/**
|
|
105
|
+
* This will be placed inside the card on the reviewer sidebar.
|
|
106
|
+
*
|
|
107
|
+
* It will be given the `basicRequestData` prop, which contains the basic information about the
|
|
108
|
+
* request, such as the name of the applicant and the period. Any custom user information returned
|
|
109
|
+
* by the userLookup function you provide will also be included at `basicRequestData.applicant.otherInfo`.
|
|
110
|
+
*/
|
|
111
|
+
reviewerSidebarCard?: typeof SvelteComponent<any>;
|
|
112
|
+
/**
|
|
113
|
+
* This will be placed below the request details in the sidebar.
|
|
114
|
+
*
|
|
115
|
+
* It will only receive the `basicRequestData` prop, but you may use the `api` object from
|
|
116
|
+
* @reqquest/ui to fetch additional data.
|
|
117
|
+
*/
|
|
118
|
+
reviewerSidebar?: typeof SvelteComponent<any>;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export interface UIConfigWithDefaults extends UIConfig {
|
|
122
|
+
terminology: Required<Terminologies>;
|
|
123
|
+
plural: Required<Terminologies>;
|
|
124
|
+
}
|
|
125
|
+
export declare class UIRegistry {
|
|
126
|
+
config: UIConfig;
|
|
127
|
+
protected promptMap: Record<string, PromptDefinition>;
|
|
128
|
+
protected requirementMap: Record<string, RequirementDefinition>;
|
|
129
|
+
protected programMap: Record<string, ProgramDefinition>;
|
|
130
|
+
protected lang: Required<Terminologies>;
|
|
131
|
+
protected plural: Required<Terminologies>;
|
|
132
|
+
constructor(config: UIConfig);
|
|
133
|
+
getWord(key: keyof Terminologies, count?: number, inclusive?: boolean): string;
|
|
134
|
+
getPlural(key: keyof Terminologies): string;
|
|
135
|
+
getPrompt(key: string): PromptDefinition;
|
|
136
|
+
getRequirement(key: string): RequirementDefinition;
|
|
137
|
+
getProgram(key: string): ProgramDefinition;
|
|
138
|
+
}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { plural } from 'txstate-utils';
|
|
2
|
+
export class UIRegistry {
|
|
3
|
+
config;
|
|
4
|
+
promptMap = {};
|
|
5
|
+
requirementMap = {};
|
|
6
|
+
programMap = {};
|
|
7
|
+
lang;
|
|
8
|
+
plural;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
for (const prompt of config.prompts)
|
|
12
|
+
this.promptMap[prompt.key] = prompt;
|
|
13
|
+
for (const requirement of config.requirements)
|
|
14
|
+
this.requirementMap[requirement.key] = requirement;
|
|
15
|
+
for (const program of config.programs)
|
|
16
|
+
this.programMap[program.key] = program;
|
|
17
|
+
this.lang = {
|
|
18
|
+
appRequest: config.terminology?.appRequest ?? config.programs.length > 1 ? 'App Request' : 'Application',
|
|
19
|
+
login: config.terminology?.login ?? 'Login',
|
|
20
|
+
period: config.terminology?.period ?? 'Period'
|
|
21
|
+
};
|
|
22
|
+
this.plural = {};
|
|
23
|
+
for (const key of Object.keys(this.lang)) {
|
|
24
|
+
this.plural[key] = config.terminology?.plural?.[key] ?? plural(this.lang[key]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
getWord(key, count = 1, inclusive = false) {
|
|
28
|
+
return (inclusive ? count + ' ' : '') + (count !== 1 ? this.plural[key] : this.lang[key]);
|
|
29
|
+
}
|
|
30
|
+
getPlural(key) {
|
|
31
|
+
return this.plural[key];
|
|
32
|
+
}
|
|
33
|
+
getPrompt(key) {
|
|
34
|
+
return this.promptMap[key];
|
|
35
|
+
}
|
|
36
|
+
getRequirement(key) {
|
|
37
|
+
return this.requirementMap[key];
|
|
38
|
+
}
|
|
39
|
+
getProgram(key) {
|
|
40
|
+
return this.programMap[key];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { QueryGenqlSelection, Query, MutationGenqlSelection, Mutation } from './schema';
|
|
2
|
+
import { type FieldsSelection, type GraphqlOperation, type ClientOptions, GenqlError } from './runtime';
|
|
3
|
+
export type { FieldsSelection } from './runtime';
|
|
4
|
+
export { GenqlError };
|
|
5
|
+
export * from './schema';
|
|
6
|
+
export interface Client {
|
|
7
|
+
query<R extends QueryGenqlSelection>(request: R & {
|
|
8
|
+
__name?: string;
|
|
9
|
+
}): Promise<FieldsSelection<Query, R>>;
|
|
10
|
+
mutation<R extends MutationGenqlSelection>(request: R & {
|
|
11
|
+
__name?: string;
|
|
12
|
+
}): Promise<FieldsSelection<Mutation, R>>;
|
|
13
|
+
}
|
|
14
|
+
export declare const createClient: (options?: ClientOptions) => Client;
|
|
15
|
+
export declare const everything: {
|
|
16
|
+
__scalar: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type QueryResult<fields extends QueryGenqlSelection> = FieldsSelection<Query, fields>;
|
|
19
|
+
export declare const generateQueryOp: (fields: QueryGenqlSelection & {
|
|
20
|
+
__name?: string;
|
|
21
|
+
}) => GraphqlOperation;
|
|
22
|
+
export type MutationResult<fields extends MutationGenqlSelection> = FieldsSelection<Mutation, fields>;
|
|
23
|
+
export declare const generateMutationOp: (fields: MutationGenqlSelection & {
|
|
24
|
+
__name?: string;
|
|
25
|
+
}) => GraphqlOperation;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { linkTypeMap, createClient as createClientOriginal, generateGraphqlOperation, GenqlError, } from './runtime';
|
|
2
|
+
export { GenqlError };
|
|
3
|
+
import types from './types';
|
|
4
|
+
export * from './schema';
|
|
5
|
+
const typeMap = linkTypeMap(types);
|
|
6
|
+
export const createClient = function (options) {
|
|
7
|
+
return createClientOriginal({
|
|
8
|
+
url: 'http://localhost:81/graphql',
|
|
9
|
+
...options,
|
|
10
|
+
queryRoot: typeMap.Query,
|
|
11
|
+
mutationRoot: typeMap.Mutation,
|
|
12
|
+
subscriptionRoot: typeMap.Subscription,
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
export const everything = {
|
|
16
|
+
__scalar: true,
|
|
17
|
+
};
|
|
18
|
+
export const generateQueryOp = function (fields) {
|
|
19
|
+
return generateGraphqlOperation('query', typeMap.Query, fields);
|
|
20
|
+
};
|
|
21
|
+
export const generateMutationOp = function (fields) {
|
|
22
|
+
return generateGraphqlOperation('mutation', typeMap.Mutation, fields);
|
|
23
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { GraphqlOperation } from './generateGraphqlOperation';
|
|
2
|
+
type Variables = Record<string, any>;
|
|
3
|
+
type QueryError = Error & {
|
|
4
|
+
message: string;
|
|
5
|
+
locations?: Array<{
|
|
6
|
+
line: number;
|
|
7
|
+
column: number;
|
|
8
|
+
}>;
|
|
9
|
+
path?: any;
|
|
10
|
+
rid: string;
|
|
11
|
+
details?: Record<string, any>;
|
|
12
|
+
};
|
|
13
|
+
type Result = {
|
|
14
|
+
data: Record<string, any>;
|
|
15
|
+
errors: Array<QueryError>;
|
|
16
|
+
};
|
|
17
|
+
type Fetcher = (batchedQuery: GraphqlOperation | Array<GraphqlOperation>) => Promise<Array<Result>>;
|
|
18
|
+
type Options = {
|
|
19
|
+
batchInterval?: number;
|
|
20
|
+
shouldBatch?: boolean;
|
|
21
|
+
maxBatchSize?: number;
|
|
22
|
+
};
|
|
23
|
+
type Queue = Array<{
|
|
24
|
+
request: GraphqlOperation;
|
|
25
|
+
resolve: (...args: Array<any>) => any;
|
|
26
|
+
reject: (...args: Array<any>) => any;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Create a batcher client.
|
|
30
|
+
* @param {Fetcher} fetcher - A function that can handle the network requests to graphql endpoint
|
|
31
|
+
* @param {Options} options - the options to be used by client
|
|
32
|
+
* @param {boolean} options.shouldBatch - should the client batch requests. (default true)
|
|
33
|
+
* @param {integer} options.batchInterval - duration (in MS) of each batch window. (default 6)
|
|
34
|
+
* @param {integer} options.maxBatchSize - max number of requests in a batch. (default 0)
|
|
35
|
+
* @param {boolean} options.defaultHeaders - default headers to include with every request
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const fetcher = batchedQuery => fetch('path/to/graphql', {
|
|
39
|
+
* method: 'post',
|
|
40
|
+
* headers: {
|
|
41
|
+
* Accept: 'application/json',
|
|
42
|
+
* 'Content-Type': 'application/json',
|
|
43
|
+
* },
|
|
44
|
+
* body: JSON.stringify(batchedQuery),
|
|
45
|
+
* credentials: 'include',
|
|
46
|
+
* })
|
|
47
|
+
* .then(response => response.json())
|
|
48
|
+
*
|
|
49
|
+
* const client = new QueryBatcher(fetcher, { maxBatchSize: 10 })
|
|
50
|
+
*/
|
|
51
|
+
export declare class QueryBatcher {
|
|
52
|
+
fetcher: Fetcher;
|
|
53
|
+
_options: Options;
|
|
54
|
+
_queue: Queue;
|
|
55
|
+
constructor(fetcher: Fetcher, { batchInterval, shouldBatch, maxBatchSize, }?: Options);
|
|
56
|
+
/**
|
|
57
|
+
* Fetch will send a graphql request and return the parsed json.
|
|
58
|
+
* @param {string} query - the graphql query.
|
|
59
|
+
* @param {Variables} variables - any variables you wish to inject as key/value pairs.
|
|
60
|
+
* @param {[string]} operationName - the graphql operationName.
|
|
61
|
+
* @param {Options} overrides - the client options overrides.
|
|
62
|
+
*
|
|
63
|
+
* @return {promise} resolves to parsed json of server response
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* client.fetch(`
|
|
67
|
+
* query getHuman($id: ID!) {
|
|
68
|
+
* human(id: $id) {
|
|
69
|
+
* name
|
|
70
|
+
* height
|
|
71
|
+
* }
|
|
72
|
+
* }
|
|
73
|
+
* `, { id: "1001" }, 'getHuman')
|
|
74
|
+
* .then(human => {
|
|
75
|
+
* // do something with human
|
|
76
|
+
* console.log(human);
|
|
77
|
+
* });
|
|
78
|
+
*/
|
|
79
|
+
fetch(query: string, variables?: Variables, operationName?: string, overrides?: Options): Promise<Result>;
|
|
80
|
+
/**
|
|
81
|
+
* Fetch will send a graphql request and return the parsed json.
|
|
82
|
+
* @param {string} query - the graphql query.
|
|
83
|
+
* @param {Variables} variables - any variables you wish to inject as key/value pairs.
|
|
84
|
+
* @param {[string]} operationName - the graphql operationName.
|
|
85
|
+
* @param {Options} overrides - the client options overrides.
|
|
86
|
+
*
|
|
87
|
+
* @return {Promise<Array<Result>>} resolves to parsed json of server response
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* client.forceFetch(`
|
|
91
|
+
* query getHuman($id: ID!) {
|
|
92
|
+
* human(id: $id) {
|
|
93
|
+
* name
|
|
94
|
+
* height
|
|
95
|
+
* }
|
|
96
|
+
* }
|
|
97
|
+
* `, { id: "1001" }, 'getHuman')
|
|
98
|
+
* .then(human => {
|
|
99
|
+
* // do something with human
|
|
100
|
+
* console.log(human);
|
|
101
|
+
* });
|
|
102
|
+
*/
|
|
103
|
+
forceFetch(query: string, variables?: Variables, operationName?: string, overrides?: Options): Promise<Result>;
|
|
104
|
+
}
|
|
105
|
+
export {};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { GenqlError } from './error';
|
|
2
|
+
/**
|
|
3
|
+
* takes a list of requests (queue) and batches them into a single server request.
|
|
4
|
+
* It will then resolve each individual requests promise with the appropriate data.
|
|
5
|
+
* @private
|
|
6
|
+
* @param {QueryBatcher} client - the client to use
|
|
7
|
+
* @param {Queue} queue - the list of requests to batch
|
|
8
|
+
*/
|
|
9
|
+
function dispatchQueueBatch(client, queue) {
|
|
10
|
+
let batchedQuery = queue.map((item) => item.request);
|
|
11
|
+
if (batchedQuery.length === 1) {
|
|
12
|
+
batchedQuery = batchedQuery[0];
|
|
13
|
+
}
|
|
14
|
+
(() => {
|
|
15
|
+
try {
|
|
16
|
+
return client.fetcher(batchedQuery);
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
return Promise.reject(e);
|
|
20
|
+
}
|
|
21
|
+
})().then((responses) => {
|
|
22
|
+
if (queue.length === 1 && !Array.isArray(responses)) {
|
|
23
|
+
if (responses.errors && responses.errors.length) {
|
|
24
|
+
queue[0].reject(new GenqlError(responses.errors, responses.data));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
queue[0].resolve(responses);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
else if (responses.length !== queue.length) {
|
|
31
|
+
throw new Error('response length did not match query length');
|
|
32
|
+
}
|
|
33
|
+
for (let i = 0; i < queue.length; i++) {
|
|
34
|
+
if (responses[i].errors && responses[i].errors.length) {
|
|
35
|
+
queue[i].reject(new GenqlError(responses[i].errors, responses[i].data));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
queue[i].resolve(responses[i]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
.catch((e) => {
|
|
43
|
+
for (let i = 0; i < queue.length; i++) {
|
|
44
|
+
queue[i].reject(e);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* creates a list of requests to batch according to max batch size.
|
|
50
|
+
* @private
|
|
51
|
+
* @param {QueryBatcher} client - the client to create list of requests from from
|
|
52
|
+
* @param {Options} options - the options for the batch
|
|
53
|
+
*/
|
|
54
|
+
function dispatchQueue(client, options) {
|
|
55
|
+
const queue = client._queue;
|
|
56
|
+
const maxBatchSize = options.maxBatchSize || 0;
|
|
57
|
+
client._queue = [];
|
|
58
|
+
if (maxBatchSize > 0 && maxBatchSize < queue.length) {
|
|
59
|
+
for (let i = 0; i < queue.length / maxBatchSize; i++) {
|
|
60
|
+
dispatchQueueBatch(client, queue.slice(i * maxBatchSize, (i + 1) * maxBatchSize));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
dispatchQueueBatch(client, queue);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a batcher client.
|
|
69
|
+
* @param {Fetcher} fetcher - A function that can handle the network requests to graphql endpoint
|
|
70
|
+
* @param {Options} options - the options to be used by client
|
|
71
|
+
* @param {boolean} options.shouldBatch - should the client batch requests. (default true)
|
|
72
|
+
* @param {integer} options.batchInterval - duration (in MS) of each batch window. (default 6)
|
|
73
|
+
* @param {integer} options.maxBatchSize - max number of requests in a batch. (default 0)
|
|
74
|
+
* @param {boolean} options.defaultHeaders - default headers to include with every request
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const fetcher = batchedQuery => fetch('path/to/graphql', {
|
|
78
|
+
* method: 'post',
|
|
79
|
+
* headers: {
|
|
80
|
+
* Accept: 'application/json',
|
|
81
|
+
* 'Content-Type': 'application/json',
|
|
82
|
+
* },
|
|
83
|
+
* body: JSON.stringify(batchedQuery),
|
|
84
|
+
* credentials: 'include',
|
|
85
|
+
* })
|
|
86
|
+
* .then(response => response.json())
|
|
87
|
+
*
|
|
88
|
+
* const client = new QueryBatcher(fetcher, { maxBatchSize: 10 })
|
|
89
|
+
*/
|
|
90
|
+
export class QueryBatcher {
|
|
91
|
+
fetcher;
|
|
92
|
+
_options;
|
|
93
|
+
_queue;
|
|
94
|
+
constructor(fetcher, { batchInterval = 6, shouldBatch = true, maxBatchSize = 0, } = {}) {
|
|
95
|
+
this.fetcher = fetcher;
|
|
96
|
+
this._options = {
|
|
97
|
+
batchInterval,
|
|
98
|
+
shouldBatch,
|
|
99
|
+
maxBatchSize,
|
|
100
|
+
};
|
|
101
|
+
this._queue = [];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Fetch will send a graphql request and return the parsed json.
|
|
105
|
+
* @param {string} query - the graphql query.
|
|
106
|
+
* @param {Variables} variables - any variables you wish to inject as key/value pairs.
|
|
107
|
+
* @param {[string]} operationName - the graphql operationName.
|
|
108
|
+
* @param {Options} overrides - the client options overrides.
|
|
109
|
+
*
|
|
110
|
+
* @return {promise} resolves to parsed json of server response
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* client.fetch(`
|
|
114
|
+
* query getHuman($id: ID!) {
|
|
115
|
+
* human(id: $id) {
|
|
116
|
+
* name
|
|
117
|
+
* height
|
|
118
|
+
* }
|
|
119
|
+
* }
|
|
120
|
+
* `, { id: "1001" }, 'getHuman')
|
|
121
|
+
* .then(human => {
|
|
122
|
+
* // do something with human
|
|
123
|
+
* console.log(human);
|
|
124
|
+
* });
|
|
125
|
+
*/
|
|
126
|
+
fetch(query, variables, operationName, overrides = {}) {
|
|
127
|
+
const request = {
|
|
128
|
+
query,
|
|
129
|
+
};
|
|
130
|
+
const options = Object.assign({}, this._options, overrides);
|
|
131
|
+
if (variables) {
|
|
132
|
+
request.variables = variables;
|
|
133
|
+
}
|
|
134
|
+
if (operationName) {
|
|
135
|
+
request.operationName = operationName;
|
|
136
|
+
}
|
|
137
|
+
const promise = new Promise((resolve, reject) => {
|
|
138
|
+
this._queue.push({
|
|
139
|
+
request,
|
|
140
|
+
resolve,
|
|
141
|
+
reject,
|
|
142
|
+
});
|
|
143
|
+
if (this._queue.length === 1) {
|
|
144
|
+
if (options.shouldBatch) {
|
|
145
|
+
setTimeout(() => dispatchQueue(this, options), options.batchInterval);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
dispatchQueue(this, options);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return promise;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Fetch will send a graphql request and return the parsed json.
|
|
156
|
+
* @param {string} query - the graphql query.
|
|
157
|
+
* @param {Variables} variables - any variables you wish to inject as key/value pairs.
|
|
158
|
+
* @param {[string]} operationName - the graphql operationName.
|
|
159
|
+
* @param {Options} overrides - the client options overrides.
|
|
160
|
+
*
|
|
161
|
+
* @return {Promise<Array<Result>>} resolves to parsed json of server response
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* client.forceFetch(`
|
|
165
|
+
* query getHuman($id: ID!) {
|
|
166
|
+
* human(id: $id) {
|
|
167
|
+
* name
|
|
168
|
+
* height
|
|
169
|
+
* }
|
|
170
|
+
* }
|
|
171
|
+
* `, { id: "1001" }, 'getHuman')
|
|
172
|
+
* .then(human => {
|
|
173
|
+
* // do something with human
|
|
174
|
+
* console.log(human);
|
|
175
|
+
* });
|
|
176
|
+
*/
|
|
177
|
+
forceFetch(query, variables, operationName, overrides = {}) {
|
|
178
|
+
const request = {
|
|
179
|
+
query,
|
|
180
|
+
};
|
|
181
|
+
const options = Object.assign({}, this._options, overrides, {
|
|
182
|
+
shouldBatch: false,
|
|
183
|
+
});
|
|
184
|
+
if (variables) {
|
|
185
|
+
request.variables = variables;
|
|
186
|
+
}
|
|
187
|
+
if (operationName) {
|
|
188
|
+
request.operationName = operationName;
|
|
189
|
+
}
|
|
190
|
+
const promise = new Promise((resolve, reject) => {
|
|
191
|
+
const client = new QueryBatcher(this.fetcher, this._options);
|
|
192
|
+
client._queue = [
|
|
193
|
+
{
|
|
194
|
+
request,
|
|
195
|
+
resolve,
|
|
196
|
+
reject,
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
dispatchQueue(client, options);
|
|
200
|
+
});
|
|
201
|
+
return promise;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type BatchOptions } from './fetcher';
|
|
2
|
+
import type { ExecutionResult, LinkedType } from './types';
|
|
3
|
+
import { type GraphqlOperation } from './generateGraphqlOperation';
|
|
4
|
+
export type Headers = HeadersInit | (() => HeadersInit) | (() => Promise<HeadersInit>);
|
|
5
|
+
export type BaseFetcher = (operation: GraphqlOperation | GraphqlOperation[]) => Promise<ExecutionResult | ExecutionResult[]>;
|
|
6
|
+
export type ClientOptions = Omit<RequestInit, 'body' | 'headers'> & {
|
|
7
|
+
url?: string;
|
|
8
|
+
batch?: BatchOptions | boolean;
|
|
9
|
+
fetcher?: BaseFetcher;
|
|
10
|
+
fetch?: Function;
|
|
11
|
+
headers?: Headers;
|
|
12
|
+
};
|
|
13
|
+
export declare const createClient: ({ queryRoot, mutationRoot, subscriptionRoot, ...options }: ClientOptions & {
|
|
14
|
+
queryRoot?: LinkedType;
|
|
15
|
+
mutationRoot?: LinkedType;
|
|
16
|
+
subscriptionRoot?: LinkedType;
|
|
17
|
+
}) => any;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { createFetcher } from './fetcher';
|
|
3
|
+
import { generateGraphqlOperation, } from './generateGraphqlOperation';
|
|
4
|
+
export const createClient = ({ queryRoot, mutationRoot, subscriptionRoot, ...options }) => {
|
|
5
|
+
const fetcher = createFetcher(options);
|
|
6
|
+
const client = {};
|
|
7
|
+
if (queryRoot) {
|
|
8
|
+
client.query = (request) => {
|
|
9
|
+
if (!queryRoot)
|
|
10
|
+
throw new Error('queryRoot argument is missing');
|
|
11
|
+
const resultPromise = fetcher(generateGraphqlOperation('query', queryRoot, request));
|
|
12
|
+
return resultPromise;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (mutationRoot) {
|
|
16
|
+
client.mutation = (request) => {
|
|
17
|
+
if (!mutationRoot)
|
|
18
|
+
throw new Error('mutationRoot argument is missing');
|
|
19
|
+
const resultPromise = fetcher(generateGraphqlOperation('mutation', mutationRoot, request));
|
|
20
|
+
return resultPromise;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return client;
|
|
24
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class GenqlError extends Error {
|
|
2
|
+
errors: Array<GraphqlError>;
|
|
3
|
+
/**
|
|
4
|
+
* Partial data returned by the server
|
|
5
|
+
*/
|
|
6
|
+
data?: any;
|
|
7
|
+
constructor(errors: any[], data: any);
|
|
8
|
+
}
|
|
9
|
+
interface GraphqlError {
|
|
10
|
+
message: string;
|
|
11
|
+
locations?: Array<{
|
|
12
|
+
line: number;
|
|
13
|
+
column: number;
|
|
14
|
+
}>;
|
|
15
|
+
path?: string[];
|
|
16
|
+
extensions?: Record<string, any>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
export class GenqlError extends Error {
|
|
3
|
+
errors = [];
|
|
4
|
+
/**
|
|
5
|
+
* Partial data returned by the server
|
|
6
|
+
*/
|
|
7
|
+
data;
|
|
8
|
+
constructor(errors, data) {
|
|
9
|
+
let message = Array.isArray(errors)
|
|
10
|
+
? errors.map((x) => x?.message || '').join('\n')
|
|
11
|
+
: '';
|
|
12
|
+
if (!message) {
|
|
13
|
+
message = 'GraphQL error';
|
|
14
|
+
}
|
|
15
|
+
super(message);
|
|
16
|
+
this.errors = errors;
|
|
17
|
+
this.data = data;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClientOptions } from './createClient';
|
|
2
|
+
import type { GraphqlOperation } from './generateGraphqlOperation';
|
|
3
|
+
export interface Fetcher {
|
|
4
|
+
(gql: GraphqlOperation): Promise<any>;
|
|
5
|
+
}
|
|
6
|
+
export type BatchOptions = {
|
|
7
|
+
batchInterval?: number;
|
|
8
|
+
maxBatchSize?: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const createFetcher: ({ url, headers, fetcher, fetch: _fetch, batch, ...rest }: ClientOptions) => Fetcher;
|