@distilled.cloud/core 1.0.0-rc.1 → 1.0.0-rc.10
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/LICENSE +201 -0
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +16 -4
- package/lib/api.js.map +1 -1
- package/lib/category.d.ts +4 -4
- package/lib/category.js +4 -4
- package/lib/codegen/boolean-string-enums.d.ts +36 -0
- package/lib/codegen/boolean-string-enums.d.ts.map +1 -0
- package/lib/codegen/boolean-string-enums.js +94 -0
- package/lib/codegen/boolean-string-enums.js.map +1 -0
- package/lib/codegen/boolean-string-enums.test.d.ts +2 -0
- package/lib/codegen/boolean-string-enums.test.d.ts.map +1 -0
- package/lib/codegen/boolean-string-enums.test.js +147 -0
- package/lib/codegen/boolean-string-enums.test.js.map +1 -0
- package/lib/codegen/cli.d.ts +56 -6
- package/lib/codegen/cli.d.ts.map +1 -1
- package/lib/codegen/cli.js +59 -75
- package/lib/codegen/cli.js.map +1 -1
- package/lib/codegen/emit.d.ts +3 -7
- package/lib/codegen/emit.d.ts.map +1 -1
- package/lib/codegen/emit.js +6 -10
- package/lib/codegen/emit.js.map +1 -1
- package/lib/codegen/generator.d.ts.map +1 -1
- package/lib/codegen/generator.js +142 -20
- package/lib/codegen/generator.js.map +1 -1
- package/lib/codegen/graphql-client.d.ts +62 -0
- package/lib/codegen/graphql-client.d.ts.map +1 -0
- package/lib/codegen/graphql-client.js +294 -0
- package/lib/codegen/graphql-client.js.map +1 -0
- package/lib/codegen/graphql-client.test.d.ts +2 -0
- package/lib/codegen/graphql-client.test.d.ts.map +1 -0
- package/lib/codegen/graphql-client.test.js +311 -0
- package/lib/codegen/graphql-client.test.js.map +1 -0
- package/lib/codegen/graphql.d.ts +207 -0
- package/lib/codegen/graphql.d.ts.map +1 -0
- package/lib/codegen/graphql.js +799 -0
- package/lib/codegen/graphql.js.map +1 -0
- package/lib/codegen/openapi-cli.d.ts +17 -3
- package/lib/codegen/openapi-cli.d.ts.map +1 -1
- package/lib/codegen/openapi-cli.js +50 -48
- package/lib/codegen/openapi-cli.js.map +1 -1
- package/lib/codegen/openapi.d.ts +69 -6
- package/lib/codegen/openapi.d.ts.map +1 -1
- package/lib/codegen/openapi.js +173 -16
- package/lib/codegen/openapi.js.map +1 -1
- package/lib/codegen/patches.d.ts +65 -0
- package/lib/codegen/patches.d.ts.map +1 -0
- package/lib/codegen/patches.js +236 -0
- package/lib/codegen/patches.js.map +1 -0
- package/lib/codegen/patches.test.d.ts +2 -0
- package/lib/codegen/patches.test.d.ts.map +1 -0
- package/lib/codegen/patches.test.js +105 -0
- package/lib/codegen/patches.test.js.map +1 -0
- package/lib/codegen/proto.d.ts +121 -0
- package/lib/codegen/proto.d.ts.map +1 -0
- package/lib/codegen/proto.js +962 -0
- package/lib/codegen/proto.js.map +1 -0
- package/lib/codegen/rewrite-operation-ids.d.ts +131 -0
- package/lib/codegen/rewrite-operation-ids.d.ts.map +1 -0
- package/lib/codegen/rewrite-operation-ids.js +1079 -0
- package/lib/codegen/rewrite-operation-ids.js.map +1 -0
- package/lib/codegen/rewrite-operation-ids.test.d.ts +2 -0
- package/lib/codegen/rewrite-operation-ids.test.d.ts.map +1 -0
- package/lib/codegen/rewrite-operation-ids.test.js +533 -0
- package/lib/codegen/rewrite-operation-ids.test.js.map +1 -0
- package/lib/codegen/spec-path.d.ts +16 -0
- package/lib/codegen/spec-path.d.ts.map +1 -0
- package/lib/codegen/spec-path.js +101 -0
- package/lib/codegen/spec-path.js.map +1 -0
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +17 -13
- package/lib/errors.js.map +1 -1
- package/lib/graphql.d.ts +284 -0
- package/lib/graphql.d.ts.map +1 -0
- package/lib/graphql.fixture.d.ts +249 -0
- package/lib/graphql.fixture.d.ts.map +1 -0
- package/lib/graphql.fixture.js +240 -0
- package/lib/graphql.fixture.js.map +1 -0
- package/lib/graphql.js +718 -0
- package/lib/graphql.js.map +1 -0
- package/lib/graphql.test.d.ts +2 -0
- package/lib/graphql.test.d.ts.map +1 -0
- package/lib/graphql.test.js +780 -0
- package/lib/graphql.test.js.map +1 -0
- package/lib/graphql.types.d.ts +2 -0
- package/lib/graphql.types.d.ts.map +1 -0
- package/lib/graphql.types.js +45 -0
- package/lib/graphql.types.js.map +1 -0
- package/lib/json-patch.d.ts +18 -11
- package/lib/json-patch.d.ts.map +1 -1
- package/lib/json-patch.js +63 -25
- package/lib/json-patch.js.map +1 -1
- package/lib/pagination.d.ts +43 -3
- package/lib/pagination.d.ts.map +1 -1
- package/lib/pagination.js +91 -5
- package/lib/pagination.js.map +1 -1
- package/lib/protocol-http.d.ts.map +1 -1
- package/lib/protocol-http.js +62 -26
- package/lib/protocol-http.js.map +1 -1
- package/lib/protocol-http.test.d.ts +2 -0
- package/lib/protocol-http.test.d.ts.map +1 -0
- package/lib/protocol-http.test.js +88 -0
- package/lib/protocol-http.test.js.map +1 -0
- package/lib/protocol-rest.d.ts +12 -2
- package/lib/protocol-rest.d.ts.map +1 -1
- package/lib/protocol-rest.js +17 -3
- package/lib/protocol-rest.js.map +1 -1
- package/lib/retry.js +1 -1
- package/lib/schema.d.ts +1 -1
- package/lib/schema.d.ts.map +1 -1
- package/lib/schema.js +1 -1
- package/lib/schema.js.map +1 -1
- package/lib/trait.d.ts +27 -3
- package/lib/trait.d.ts.map +1 -1
- package/lib/trait.js +17 -1
- package/lib/trait.js.map +1 -1
- package/package.json +13 -10
- package/src/api.ts +18 -4
- package/src/category.ts +4 -4
- package/src/codegen/boolean-string-enums.test.ts +168 -0
- package/src/codegen/boolean-string-enums.ts +106 -0
- package/src/codegen/cli.ts +127 -110
- package/src/codegen/emit.ts +6 -10
- package/src/codegen/generator.ts +151 -21
- package/src/codegen/graphql-client.test.ts +386 -0
- package/src/codegen/graphql-client.ts +419 -0
- package/src/codegen/graphql.ts +1217 -0
- package/src/codegen/openapi-cli.ts +75 -59
- package/src/codegen/openapi.ts +255 -16
- package/src/codegen/patches.test.ts +130 -0
- package/src/codegen/patches.ts +291 -0
- package/src/codegen/proto.ts +1128 -0
- package/src/codegen/rewrite-operation-ids.test.ts +563 -0
- package/src/codegen/rewrite-operation-ids.ts +1206 -0
- package/src/codegen/spec-path.ts +115 -0
- package/src/errors.ts +20 -25
- package/src/graphql.fixture.ts +371 -0
- package/src/graphql.test.ts +974 -0
- package/src/graphql.ts +1321 -0
- package/src/graphql.types.ts +185 -0
- package/src/json-patch.ts +82 -25
- package/src/pagination.ts +134 -7
- package/src/protocol-http.test.ts +107 -0
- package/src/protocol-http.ts +67 -31
- package/src/protocol-rest.ts +28 -4
- package/src/retry.ts +1 -1
- package/src/schema.ts +2 -2
- package/src/trait.ts +39 -3
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/** GraphQL-native introspection compiler. No traversal limits or RPC projection. */
|
|
2
|
+
import { readIntrospection, type TypeRef } from "./graphql.ts";
|
|
3
|
+
|
|
4
|
+
export interface GraphQLArgument {
|
|
5
|
+
readonly type: string;
|
|
6
|
+
readonly description?: string;
|
|
7
|
+
readonly defaultValue?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface GraphQLField {
|
|
10
|
+
readonly type: string;
|
|
11
|
+
readonly args: Record<string, GraphQLArgument>;
|
|
12
|
+
readonly errors: readonly string[];
|
|
13
|
+
readonly description?: string;
|
|
14
|
+
readonly deprecated?: string | true;
|
|
15
|
+
}
|
|
16
|
+
export interface GraphQLType {
|
|
17
|
+
readonly kind: string;
|
|
18
|
+
readonly description?: string;
|
|
19
|
+
readonly fields?: Record<string, GraphQLField>;
|
|
20
|
+
readonly inputFields?: Record<string, GraphQLArgument>;
|
|
21
|
+
readonly enumValues?: readonly string[];
|
|
22
|
+
readonly possibleTypes?: readonly string[];
|
|
23
|
+
readonly interfaces?: readonly string[];
|
|
24
|
+
/** TypeScript scalar representation; custom JSON scalars default to unknown. */
|
|
25
|
+
readonly scalar?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface GraphQLErrorDefinition {
|
|
28
|
+
readonly description?: string;
|
|
29
|
+
readonly category?: string;
|
|
30
|
+
readonly retryable?: boolean;
|
|
31
|
+
readonly matchers: readonly {
|
|
32
|
+
readonly code?: string;
|
|
33
|
+
readonly message?: string;
|
|
34
|
+
readonly messageIncludes?: string;
|
|
35
|
+
}[];
|
|
36
|
+
}
|
|
37
|
+
export interface GraphQLModel {
|
|
38
|
+
readonly version: 1;
|
|
39
|
+
readonly queryType: string;
|
|
40
|
+
readonly mutationType?: string;
|
|
41
|
+
readonly subscriptionType?: string;
|
|
42
|
+
readonly types: Record<string, GraphQLType>;
|
|
43
|
+
readonly errors: Record<string, GraphQLErrorDefinition>;
|
|
44
|
+
readonly globalErrors: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const graphqlTypeString = (ref: TypeRef): string => {
|
|
48
|
+
if (ref.kind === "NON_NULL" || ref.kind === "LIST") {
|
|
49
|
+
if (!ref.ofType)
|
|
50
|
+
throw new Error(`Incomplete GraphQL ${ref.kind} reference`);
|
|
51
|
+
const inner = graphqlTypeString(ref.ofType);
|
|
52
|
+
return ref.kind === "NON_NULL" ? `${inner}!` : `[${inner}]`;
|
|
53
|
+
}
|
|
54
|
+
if (!ref.name) throw new Error("Unnamed GraphQL type reference");
|
|
55
|
+
return ref.name;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** Retain every schema coordinate, wrapper, default and abstract-type relationship. */
|
|
59
|
+
export const convertGraphQLClient = (
|
|
60
|
+
introspection: unknown,
|
|
61
|
+
options: { readonly scalars?: Readonly<Record<string, string>> } = {},
|
|
62
|
+
): GraphQLModel => {
|
|
63
|
+
const schema = readIntrospection(introspection);
|
|
64
|
+
const scalars = {
|
|
65
|
+
String: "string",
|
|
66
|
+
ID: "string",
|
|
67
|
+
Int: "number",
|
|
68
|
+
Float: "number",
|
|
69
|
+
Boolean: "boolean",
|
|
70
|
+
...options.scalars,
|
|
71
|
+
};
|
|
72
|
+
const argument = (value: {
|
|
73
|
+
type: TypeRef;
|
|
74
|
+
description?: string | null;
|
|
75
|
+
defaultValue?: string | null;
|
|
76
|
+
}): GraphQLArgument => ({
|
|
77
|
+
type: graphqlTypeString(value.type),
|
|
78
|
+
...(value.description ? { description: value.description } : {}),
|
|
79
|
+
...(value.defaultValue != null ? { defaultValue: value.defaultValue } : {}),
|
|
80
|
+
});
|
|
81
|
+
const types: Record<string, GraphQLType> = {};
|
|
82
|
+
for (const type of [...schema.types].sort((a, b) =>
|
|
83
|
+
a.name.localeCompare(b.name),
|
|
84
|
+
)) {
|
|
85
|
+
if (type.name.startsWith("__")) continue;
|
|
86
|
+
types[type.name] = {
|
|
87
|
+
kind: type.kind,
|
|
88
|
+
...(type.description ? { description: type.description } : {}),
|
|
89
|
+
...(type.kind === "SCALAR"
|
|
90
|
+
? { scalar: scalars[type.name as keyof typeof scalars] ?? "unknown" }
|
|
91
|
+
: {}),
|
|
92
|
+
...(type.fields
|
|
93
|
+
? {
|
|
94
|
+
fields: Object.fromEntries(
|
|
95
|
+
[...type.fields]
|
|
96
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
97
|
+
.map((field) => [
|
|
98
|
+
field.name,
|
|
99
|
+
{
|
|
100
|
+
type: graphqlTypeString(field.type),
|
|
101
|
+
args: Object.fromEntries(
|
|
102
|
+
field.args.map((arg) => [arg.name, argument(arg)]),
|
|
103
|
+
),
|
|
104
|
+
errors: [],
|
|
105
|
+
...(field.description
|
|
106
|
+
? { description: field.description }
|
|
107
|
+
: {}),
|
|
108
|
+
...(field.isDeprecated
|
|
109
|
+
? {
|
|
110
|
+
deprecated:
|
|
111
|
+
(
|
|
112
|
+
field as typeof field & {
|
|
113
|
+
deprecationReason?: string;
|
|
114
|
+
}
|
|
115
|
+
).deprecationReason || true,
|
|
116
|
+
}
|
|
117
|
+
: {}),
|
|
118
|
+
},
|
|
119
|
+
]),
|
|
120
|
+
),
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
...(type.inputFields
|
|
124
|
+
? {
|
|
125
|
+
inputFields: Object.fromEntries(
|
|
126
|
+
type.inputFields.map((field) => [field.name, argument(field)]),
|
|
127
|
+
),
|
|
128
|
+
}
|
|
129
|
+
: {}),
|
|
130
|
+
...(type.enumValues
|
|
131
|
+
? { enumValues: type.enumValues.map((value) => value.name) }
|
|
132
|
+
: {}),
|
|
133
|
+
...(type.possibleTypes
|
|
134
|
+
? { possibleTypes: type.possibleTypes.map((ref) => ref.name!) }
|
|
135
|
+
: {}),
|
|
136
|
+
...("interfaces" in type && Array.isArray(type.interfaces)
|
|
137
|
+
? {
|
|
138
|
+
interfaces: type.interfaces.map(
|
|
139
|
+
(ref: { name: string }) => ref.name,
|
|
140
|
+
),
|
|
141
|
+
}
|
|
142
|
+
: {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
version: 1,
|
|
147
|
+
queryType: schema.queryType.name,
|
|
148
|
+
...(schema.mutationType ? { mutationType: schema.mutationType.name } : {}),
|
|
149
|
+
...(schema.subscriptionType
|
|
150
|
+
? { subscriptionType: schema.subscriptionType.name }
|
|
151
|
+
: {}),
|
|
152
|
+
types,
|
|
153
|
+
errors: {},
|
|
154
|
+
globalErrors: [],
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/** Reject dangling schema/error references after patches, before generating code. */
|
|
159
|
+
export const validateGraphQLModel = (model: GraphQLModel): void => {
|
|
160
|
+
const checkType = (ref: string, coordinate: string) => {
|
|
161
|
+
const name = ref.replace(/[\[\]!]/g, "");
|
|
162
|
+
if (!model.types[name])
|
|
163
|
+
throw new Error(`${coordinate}: unknown GraphQL type ${name}`);
|
|
164
|
+
};
|
|
165
|
+
const checkError = (error: string, coordinate: string) => {
|
|
166
|
+
if (!model.errors[error])
|
|
167
|
+
throw new Error(`${coordinate}: unknown GraphQL error ${error}`);
|
|
168
|
+
};
|
|
169
|
+
for (const name of [
|
|
170
|
+
model.queryType,
|
|
171
|
+
model.mutationType,
|
|
172
|
+
model.subscriptionType,
|
|
173
|
+
]) {
|
|
174
|
+
if (name && !model.types[name])
|
|
175
|
+
throw new Error(`Unknown GraphQL root ${name}`);
|
|
176
|
+
}
|
|
177
|
+
for (const error of model.globalErrors) checkError(error, "globalErrors");
|
|
178
|
+
for (const [name, type] of Object.entries(model.types)) {
|
|
179
|
+
for (const [fieldName, field] of Object.entries(type.fields ?? {})) {
|
|
180
|
+
const coordinate = `${name}.${fieldName}`;
|
|
181
|
+
checkType(field.type, coordinate);
|
|
182
|
+
for (const [argName, arg] of Object.entries(field.args))
|
|
183
|
+
checkType(arg.type, `${coordinate}(${argName})`);
|
|
184
|
+
for (const error of field.errors) checkError(error, coordinate);
|
|
185
|
+
}
|
|
186
|
+
for (const [fieldName, field] of Object.entries(type.inputFields ?? {}))
|
|
187
|
+
checkType(field.type, `${name}.${fieldName}`);
|
|
188
|
+
for (const member of [
|
|
189
|
+
...(type.possibleTypes ?? []),
|
|
190
|
+
...(type.interfaces ?? []),
|
|
191
|
+
])
|
|
192
|
+
checkType(member, name);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const literal = (value: unknown): string => JSON.stringify(value);
|
|
197
|
+
const union = (values: readonly string[]): string =>
|
|
198
|
+
values.length ? values.join(" | ") : "never";
|
|
199
|
+
const documentation = (description?: string): string =>
|
|
200
|
+
description ? `/** ${description.replaceAll("*/", "* /")} */\n` : "";
|
|
201
|
+
|
|
202
|
+
export interface GenerateGraphQLClientOptions {
|
|
203
|
+
readonly transportImport: string;
|
|
204
|
+
readonly transportName?: string;
|
|
205
|
+
readonly requirementsType: string;
|
|
206
|
+
readonly requirementsImport: string;
|
|
207
|
+
/** Optional legacy-name aliases, e.g. createProject -> projectCreate. */
|
|
208
|
+
readonly operationAliases?: Readonly<Record<string, string>>;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Emit schema-indexed types and the matching runtime graph from one patched model. */
|
|
212
|
+
export const generateGraphQLClient = (
|
|
213
|
+
model: GraphQLModel,
|
|
214
|
+
options: GenerateGraphQLClientOptions,
|
|
215
|
+
): string => {
|
|
216
|
+
validateGraphQLModel(model);
|
|
217
|
+
const inputType = (ref: string): string => {
|
|
218
|
+
if (ref.endsWith("!")) return requiredInput(ref.slice(0, -1));
|
|
219
|
+
return `${requiredInput(ref)} | null`;
|
|
220
|
+
};
|
|
221
|
+
const requiredInput = (ref: string): string => {
|
|
222
|
+
if (ref.startsWith("["))
|
|
223
|
+
return `ReadonlyArray<${inputType(ref.slice(1, -1))}>`;
|
|
224
|
+
const type = model.types[ref]!;
|
|
225
|
+
return type.kind === "INPUT_OBJECT"
|
|
226
|
+
? `Inputs[${literal(ref)}]`
|
|
227
|
+
: `Scalars[${literal(ref)}]`;
|
|
228
|
+
};
|
|
229
|
+
const argsType = (args: Record<string, GraphQLArgument>): string =>
|
|
230
|
+
`{ ${Object.entries(args)
|
|
231
|
+
.map(
|
|
232
|
+
([name, arg]) =>
|
|
233
|
+
`${literal(name)}${arg.type.endsWith("!") && arg.defaultValue === undefined ? "" : "?"}: ${inputType(arg.type)}`,
|
|
234
|
+
)
|
|
235
|
+
.join("; ")} }`;
|
|
236
|
+
// GraphQL names are not necessarily valid or unoccupied TypeScript symbols.
|
|
237
|
+
// Schema names remain quoted map keys; exported values get stable safe names.
|
|
238
|
+
const occupied = new Set([
|
|
239
|
+
..."await break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield implements interface let package private protected public static arguments eval abstract any as asserts async bigint boolean constructor declare from get global infer intrinsic is keyof module namespace never number object of override readonly require satisfies set string symbol type undefined unique unknown using".split(
|
|
240
|
+
" ",
|
|
241
|
+
),
|
|
242
|
+
"G",
|
|
243
|
+
"S",
|
|
244
|
+
"Category",
|
|
245
|
+
"Scalars",
|
|
246
|
+
"Inputs",
|
|
247
|
+
"Errors",
|
|
248
|
+
"Types",
|
|
249
|
+
"PossibleTypes",
|
|
250
|
+
"Schema",
|
|
251
|
+
"Selection",
|
|
252
|
+
"Result",
|
|
253
|
+
"SelectedErrors",
|
|
254
|
+
"GraphQLIssue",
|
|
255
|
+
"Report",
|
|
256
|
+
"catchTags",
|
|
257
|
+
"isErrorTag",
|
|
258
|
+
"GraphQLFailure",
|
|
259
|
+
"GraphQLTransportError",
|
|
260
|
+
"GraphQLDecodeError",
|
|
261
|
+
"GraphQLRequestError",
|
|
262
|
+
"UnknownGraphQLError",
|
|
263
|
+
"query",
|
|
264
|
+
"mutation",
|
|
265
|
+
"report",
|
|
266
|
+
"client",
|
|
267
|
+
"schema",
|
|
268
|
+
"errorClasses",
|
|
269
|
+
options.transportName ?? "transport",
|
|
270
|
+
options.requirementsType,
|
|
271
|
+
]);
|
|
272
|
+
const identifier = (name: string) =>
|
|
273
|
+
name.replace(/[^A-Za-z0-9_$]/g, "_").replace(/^(?=[0-9])/, "_") ||
|
|
274
|
+
"generated";
|
|
275
|
+
const allocate = (preferred: string, fallback: string): string => {
|
|
276
|
+
let name = identifier(preferred);
|
|
277
|
+
if (occupied.has(name)) name = identifier(fallback);
|
|
278
|
+
const base = name;
|
|
279
|
+
for (let suffix = 2; occupied.has(name); suffix++)
|
|
280
|
+
name = `${base}${suffix}`;
|
|
281
|
+
occupied.add(name);
|
|
282
|
+
return name;
|
|
283
|
+
};
|
|
284
|
+
const errorNames = new Map(
|
|
285
|
+
Object.keys(model.errors).map((tag) => [
|
|
286
|
+
tag,
|
|
287
|
+
allocate(tag, `${identifier(tag)}Error`),
|
|
288
|
+
]),
|
|
289
|
+
);
|
|
290
|
+
const lines: string[] = [
|
|
291
|
+
"// Generated by @distilled.cloud/core/codegen/graphql-client. DO NOT EDIT.",
|
|
292
|
+
'import * as G from "@distilled.cloud/core/graphql";',
|
|
293
|
+
'import * as S from "effect/Schema";',
|
|
294
|
+
'import * as Category from "@distilled.cloud/core/category";',
|
|
295
|
+
`import { ${options.transportName ?? "transport"} } from ${literal(options.transportImport)};`,
|
|
296
|
+
`import type { ${options.requirementsType} } from ${literal(options.requirementsImport)};`,
|
|
297
|
+
`export type { ${options.requirementsType} } from ${literal(options.requirementsImport)};`,
|
|
298
|
+
'export { catchTags, isErrorTag, GraphQLFailure, GraphQLTransportError, GraphQLDecodeError, GraphQLRequestError, UnknownGraphQLError, type GraphQLIssue, type Report } from "@distilled.cloud/core/graphql";',
|
|
299
|
+
"",
|
|
300
|
+
"export type Scalars = {",
|
|
301
|
+
];
|
|
302
|
+
for (const [name, type] of Object.entries(model.types)) {
|
|
303
|
+
if (type.kind === "SCALAR" || type.kind === "ENUM")
|
|
304
|
+
lines.push(
|
|
305
|
+
`${literal(name)}: ${type.kind === "SCALAR" ? (type.scalar ?? "unknown") : union(type.enumValues!.map(literal))};`,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
lines.push("}", "export type Inputs = {");
|
|
309
|
+
for (const [name, type] of Object.entries(model.types)) {
|
|
310
|
+
if (type.kind === "INPUT_OBJECT")
|
|
311
|
+
lines.push(
|
|
312
|
+
documentation(type.description),
|
|
313
|
+
`${literal(name)}: ${argsType(type.inputFields ?? {})};`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
lines.push("}");
|
|
317
|
+
const categories: Record<string, string> = {
|
|
318
|
+
auth: "withAuthError",
|
|
319
|
+
badRequest: "withBadRequestError",
|
|
320
|
+
notFound: "withNotFoundError",
|
|
321
|
+
throttling: "withThrottlingError",
|
|
322
|
+
quota: "withQuotaError",
|
|
323
|
+
server: "withServerError",
|
|
324
|
+
conflict: "withConflictError",
|
|
325
|
+
};
|
|
326
|
+
for (const [tag, error] of Object.entries(model.errors)) {
|
|
327
|
+
const name = errorNames.get(tag)!;
|
|
328
|
+
const pipes = [
|
|
329
|
+
error.category
|
|
330
|
+
? categories[error.category] && `Category.${categories[error.category]}`
|
|
331
|
+
: undefined,
|
|
332
|
+
error.retryable
|
|
333
|
+
? `Category.withRetryable(${error.category === "throttling" ? "{ throttling: true }" : ""})`
|
|
334
|
+
: undefined,
|
|
335
|
+
].filter(Boolean);
|
|
336
|
+
lines.push(
|
|
337
|
+
documentation(error.description),
|
|
338
|
+
`export class ${name} extends S.TaggedError<${name}>()(${literal(tag)}, G.errorFields)${pipes.length ? `.pipe(${pipes.join(", ")})` : ""} {}`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
lines.push("export type Errors = {");
|
|
342
|
+
for (const [tag, name] of errorNames) lines.push(`${literal(tag)}: ${name};`);
|
|
343
|
+
lines.push("}", "export type Types = {");
|
|
344
|
+
for (const [name, type] of Object.entries(model.types)) {
|
|
345
|
+
if (!["OBJECT", "INTERFACE", "UNION"].includes(type.kind)) continue;
|
|
346
|
+
lines.push(documentation(type.description), `${literal(name)}: {`);
|
|
347
|
+
for (const [fieldName, field] of Object.entries(type.fields ?? {})) {
|
|
348
|
+
lines.push(
|
|
349
|
+
documentation(field.description),
|
|
350
|
+
`${literal(fieldName)}: G.Field<${argsType(field.args)}, ${literal(field.type)}, ${union(field.errors.map(literal))}>;`,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
lines.push("};");
|
|
354
|
+
}
|
|
355
|
+
lines.push("}", "export type PossibleTypes = {");
|
|
356
|
+
for (const [name, type] of Object.entries(model.types)) {
|
|
357
|
+
if (type.kind === "INTERFACE" || type.kind === "UNION")
|
|
358
|
+
lines.push(
|
|
359
|
+
`${literal(name)}: ${union((type.possibleTypes ?? []).map(literal))};`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
lines.push(
|
|
363
|
+
"}",
|
|
364
|
+
"export type Schema = {",
|
|
365
|
+
"types: Types; scalars: Scalars; errors: Errors; possibleTypes: PossibleTypes;",
|
|
366
|
+
`query: ${literal(model.queryType)};`,
|
|
367
|
+
`mutation: ${model.mutationType ? literal(model.mutationType) : "never"};`,
|
|
368
|
+
`subscription: ${model.subscriptionType ? literal(model.subscriptionType) : "never"};`,
|
|
369
|
+
`globalErrors: ${union(model.globalErrors.map(literal))};`,
|
|
370
|
+
"};",
|
|
371
|
+
);
|
|
372
|
+
lines.push(
|
|
373
|
+
"export type Selection<Name extends keyof Types> = G.Selection<Schema, Name>;",
|
|
374
|
+
"export type Result<Name extends string, Select> = G.Result<Schema, Name, Select>;",
|
|
375
|
+
"export type SelectedErrors<Name extends keyof Types, Select> = G.Errors<Schema, Name, Select>;",
|
|
376
|
+
);
|
|
377
|
+
lines.push(`export const schema: G.GraphQLModel = ${JSON.stringify(model)};`);
|
|
378
|
+
lines.push(
|
|
379
|
+
`export const errorClasses = { ${[...errorNames].map(([tag, name]) => `${literal(tag)}: ${name}`).join(", ")} };`,
|
|
380
|
+
);
|
|
381
|
+
lines.push(
|
|
382
|
+
`export const client = G.makeClient<Schema, ${options.requirementsType}>(schema, ${options.transportName ?? "transport"}, errorClasses);`,
|
|
383
|
+
"export const query = client.query;",
|
|
384
|
+
"export const mutation = client.mutation;",
|
|
385
|
+
"export const report = client.report;",
|
|
386
|
+
);
|
|
387
|
+
const operationNames = new Map<string, string>();
|
|
388
|
+
for (const [kind, name] of [
|
|
389
|
+
["query", model.queryType],
|
|
390
|
+
["mutation", model.mutationType],
|
|
391
|
+
] as const) {
|
|
392
|
+
for (const field of Object.keys(model.types[name ?? ""]?.fields ?? {})) {
|
|
393
|
+
const exportName = allocate(
|
|
394
|
+
field,
|
|
395
|
+
`${kind}${field[0]!.toUpperCase()}${field.slice(1)}`,
|
|
396
|
+
);
|
|
397
|
+
if (!operationNames.has(field)) operationNames.set(field, exportName);
|
|
398
|
+
lines.push(
|
|
399
|
+
`export const ${exportName} = client.operation(${literal(kind)}, ${literal(field)});`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
for (const [alias, target] of Object.entries(
|
|
404
|
+
options.operationAliases ?? {},
|
|
405
|
+
)) {
|
|
406
|
+
const targetName = operationNames.get(target);
|
|
407
|
+
// Legacy aliases are optional conveniences; never displace a real export.
|
|
408
|
+
if (
|
|
409
|
+
targetName &&
|
|
410
|
+
alias !== target &&
|
|
411
|
+
identifier(alias) === alias &&
|
|
412
|
+
!occupied.has(alias)
|
|
413
|
+
) {
|
|
414
|
+
occupied.add(alias);
|
|
415
|
+
lines.push(`export const ${alias} = ${targetName};`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return lines.join("\n") + "\n";
|
|
419
|
+
};
|