@tzurp/openapi-skills 1.2.9 → 1.2.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/README.md +63 -16
- package/dist/cli.min.js +51 -0
- package/package.json +60 -64
- package/bin/openapi-skills +0 -4
- package/dist/cli.d.ts +0 -37
- package/dist/cli.js +0 -1646
- package/dist/cli.js.map +0 -1
- package/dist/client-schema-builder.d.ts +0 -35
- package/dist/client-schema-builder.js +0 -268
- package/dist/client-schema-builder.js.map +0 -1
- package/dist/helper/base64url-json.d.ts +0 -3
- package/dist/helper/base64url-json.js +0 -28
- package/dist/helper/base64url-json.js.map +0 -1
- package/dist/helper/dotNotation.d.ts +0 -3
- package/dist/helper/dotNotation.js +0 -34
- package/dist/helper/dotNotation.js.map +0 -1
- package/dist/helper/endpoint-filter.d.ts +0 -16
- package/dist/helper/endpoint-filter.js +0 -165
- package/dist/helper/endpoint-filter.js.map +0 -1
- package/dist/helper/endpoint-utils.d.ts +0 -2
- package/dist/helper/endpoint-utils.js +0 -10
- package/dist/helper/endpoint-utils.js.map +0 -1
- package/dist/helper/error-codes.d.ts +0 -27
- package/dist/helper/error-codes.js +0 -116
- package/dist/helper/error-codes.js.map +0 -1
- package/dist/helper/error-formatter.d.ts +0 -46
- package/dist/helper/error-formatter.js +0 -72
- package/dist/helper/error-formatter.js.map +0 -1
- package/dist/helper/get-operation-filter.d.ts +0 -17
- package/dist/helper/get-operation-filter.js +0 -26
- package/dist/helper/get-operation-filter.js.map +0 -1
- package/dist/helper/graphql.d.ts +0 -40
- package/dist/helper/graphql.js +0 -967
- package/dist/helper/graphql.js.map +0 -1
- package/dist/helper/http-client.d.ts +0 -28
- package/dist/helper/http-client.js +0 -90
- package/dist/helper/http-client.js.map +0 -1
- package/dist/helper/json-updater.d.ts +0 -17
- package/dist/helper/json-updater.js +0 -195
- package/dist/helper/json-updater.js.map +0 -1
- package/dist/helper/logger.d.ts +0 -13
- package/dist/helper/logger.js +0 -56
- package/dist/helper/logger.js.map +0 -1
- package/dist/helper/output-size.d.ts +0 -5
- package/dist/helper/output-size.js +0 -7
- package/dist/helper/output-size.js.map +0 -1
- package/dist/helper/parameter-schema.d.ts +0 -14
- package/dist/helper/parameter-schema.js +0 -36
- package/dist/helper/parameter-schema.js.map +0 -1
- package/dist/helper/paths.d.ts +0 -11
- package/dist/helper/paths.js +0 -47
- package/dist/helper/paths.js.map +0 -1
- package/dist/helper/prompt-delete.d.ts +0 -1
- package/dist/helper/prompt-delete.js +0 -13
- package/dist/helper/prompt-delete.js.map +0 -1
- package/dist/helper/request-artifacts.d.ts +0 -14
- package/dist/helper/request-artifacts.js +0 -60
- package/dist/helper/request-artifacts.js.map +0 -1
- package/dist/helper/request-preparation.d.ts +0 -23
- package/dist/helper/request-preparation.js +0 -85
- package/dist/helper/request-preparation.js.map +0 -1
- package/dist/helper/sanitizer.d.ts +0 -1
- package/dist/helper/sanitizer.js +0 -17
- package/dist/helper/sanitizer.js.map +0 -1
- package/dist/helper/update-check.d.ts +0 -1
- package/dist/helper/update-check.js +0 -36
- package/dist/helper/update-check.js.map +0 -1
- package/dist/index.d.ts +0 -50
- package/dist/index.js +0 -163
- package/dist/index.js.map +0 -1
- package/dist/install-skill.d.ts +0 -5
- package/dist/install-skill.js +0 -139
- package/dist/install-skill.js.map +0 -1
- package/dist/parser.d.ts +0 -12
- package/dist/parser.js +0 -393
- package/dist/parser.js.map +0 -1
- package/dist/validate-response.d.ts +0 -47
- package/dist/validate-response.js +0 -1017
- package/dist/validate-response.js.map +0 -1
package/dist/helper/graphql.js
DELETED
|
@@ -1,967 +0,0 @@
|
|
|
1
|
-
import fs from "fs-extra";
|
|
2
|
-
import os from "os";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { createRequire } from "module";
|
|
5
|
-
import { pathToFileURL } from "url";
|
|
6
|
-
import { buildSchema, isEnumType, isInputObjectType, isListType, isNonNullType, isObjectType, isScalarType, } from "graphql";
|
|
7
|
-
import { sanitizeOperationPath } from "./sanitizer.js";
|
|
8
|
-
const scalarNames = new Set(["String", "Int", "Float", "Boolean", "ID"]);
|
|
9
|
-
export const typescriptInstallCommand = "npm install typescript";
|
|
10
|
-
const typescriptAstModuleSpecifiers = [
|
|
11
|
-
"typescript/ast",
|
|
12
|
-
"typescript",
|
|
13
|
-
"typescript/unstable/ast",
|
|
14
|
-
];
|
|
15
|
-
const typescriptSyncModuleSpecifiers = [
|
|
16
|
-
"typescript/sync",
|
|
17
|
-
"typescript",
|
|
18
|
-
"typescript/unstable/sync",
|
|
19
|
-
];
|
|
20
|
-
let cachedTypeScriptModule = null;
|
|
21
|
-
let cachedTypeScriptApi = null;
|
|
22
|
-
function collectErrorMessages(error) {
|
|
23
|
-
if (!(error instanceof Error)) {
|
|
24
|
-
return [String(error)];
|
|
25
|
-
}
|
|
26
|
-
const messages = [error.message];
|
|
27
|
-
const cause = error.cause;
|
|
28
|
-
if (cause instanceof Error) {
|
|
29
|
-
messages.push(...collectErrorMessages(cause));
|
|
30
|
-
}
|
|
31
|
-
else if (cause !== undefined && cause !== null) {
|
|
32
|
-
messages.push(String(cause));
|
|
33
|
-
}
|
|
34
|
-
return messages;
|
|
35
|
-
}
|
|
36
|
-
function isMissingTypeScriptModuleMessage(message) {
|
|
37
|
-
if (!/typescript/i.test(message)) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
if (!/(?:Cannot find (?:package|module)|ERR_MODULE_NOT_FOUND|MODULE_NOT_FOUND)/i.test(message)) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
return /['"]typescript(?:\/(?:unstable\/)?(?:ast|sync))?['"]/i.test(message)
|
|
44
|
-
|| /Cannot find typescript(?:\/(?:unstable\/)?(?:ast|sync))?/i.test(message);
|
|
45
|
-
}
|
|
46
|
-
export function isTypeScriptUnavailableError(error) {
|
|
47
|
-
return collectErrorMessages(error).some(message => /TypeScript is required to analyze builder GraphQL schemas/i.test(message)
|
|
48
|
-
|| isMissingTypeScriptModuleMessage(message));
|
|
49
|
-
}
|
|
50
|
-
function resolveTypeScriptModule(specifier) {
|
|
51
|
-
const candidates = [
|
|
52
|
-
createRequire(import.meta.url),
|
|
53
|
-
createRequire(path.join(process.cwd(), "package.json")),
|
|
54
|
-
];
|
|
55
|
-
for (const require of candidates) {
|
|
56
|
-
try {
|
|
57
|
-
return pathToFileURL(require.resolve(specifier)).href;
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
throw new Error(`Cannot find ${specifier}`);
|
|
63
|
-
}
|
|
64
|
-
async function importTypeScriptModule() {
|
|
65
|
-
let lastError;
|
|
66
|
-
for (const specifier of typescriptAstModuleSpecifiers) {
|
|
67
|
-
try {
|
|
68
|
-
const module = await importTypeScriptAstSpecifier(specifier);
|
|
69
|
-
if (typeof module.isCallExpression === "function") {
|
|
70
|
-
return module;
|
|
71
|
-
}
|
|
72
|
-
lastError = new Error(`TypeScript module '${specifier}' does not export the AST API.`);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
lastError = error;
|
|
76
|
-
try {
|
|
77
|
-
const module = await import(resolveTypeScriptModule(specifier));
|
|
78
|
-
if (typeof module.isCallExpression === "function") {
|
|
79
|
-
return module;
|
|
80
|
-
}
|
|
81
|
-
lastError = new Error(`TypeScript module '${specifier}' does not export the AST API.`);
|
|
82
|
-
}
|
|
83
|
-
catch (resolvedError) {
|
|
84
|
-
lastError = resolvedError;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
throw lastError ?? new Error("TypeScript is required to analyze builder GraphQL schemas.");
|
|
89
|
-
}
|
|
90
|
-
async function importTypeScriptAstSpecifier(specifier) {
|
|
91
|
-
return await import(specifier);
|
|
92
|
-
}
|
|
93
|
-
async function importTypeScriptApi() {
|
|
94
|
-
let lastError;
|
|
95
|
-
for (const specifier of typescriptSyncModuleSpecifiers) {
|
|
96
|
-
try {
|
|
97
|
-
const module = await importTypeScriptSyncSpecifier(specifier);
|
|
98
|
-
if (typeof module.API === "function") {
|
|
99
|
-
return module.API;
|
|
100
|
-
}
|
|
101
|
-
const legacyApi = createLegacyTypeScriptApi(module);
|
|
102
|
-
if (legacyApi) {
|
|
103
|
-
return legacyApi;
|
|
104
|
-
}
|
|
105
|
-
lastError = new Error(`TypeScript module '${specifier}' does not export API.`);
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
lastError = error;
|
|
109
|
-
try {
|
|
110
|
-
const module = await import(resolveTypeScriptModule(specifier));
|
|
111
|
-
if (typeof module.API === "function") {
|
|
112
|
-
return module.API;
|
|
113
|
-
}
|
|
114
|
-
const legacyApi = createLegacyTypeScriptApi(module);
|
|
115
|
-
if (legacyApi) {
|
|
116
|
-
return legacyApi;
|
|
117
|
-
}
|
|
118
|
-
lastError = new Error(`TypeScript module '${specifier}' does not export API.`);
|
|
119
|
-
}
|
|
120
|
-
catch (resolvedError) {
|
|
121
|
-
lastError = resolvedError;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
throw lastError ?? new Error("TypeScript is required to analyze builder GraphQL schemas.");
|
|
126
|
-
}
|
|
127
|
-
function createLegacyTypeScriptApi(module) {
|
|
128
|
-
if (typeof module.createSourceFile !== "function" || !module.ScriptTarget || !module.ScriptKind) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
return class LegacyTypeScriptApi {
|
|
132
|
-
updateSnapshot(options) {
|
|
133
|
-
const fileName = options.openFiles[0];
|
|
134
|
-
const sourceText = fs.readFileSync(fileName, "utf8");
|
|
135
|
-
const sourceFile = module.createSourceFile(fileName, sourceText, module.ScriptTarget.Latest, true, module.ScriptKind.TS);
|
|
136
|
-
return {
|
|
137
|
-
getDefaultProjectForFile: () => ({
|
|
138
|
-
program: {
|
|
139
|
-
getSourceFile: () => sourceFile,
|
|
140
|
-
},
|
|
141
|
-
}),
|
|
142
|
-
dispose: () => undefined,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
close() {
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
async function importTypeScriptSyncSpecifier(specifier) {
|
|
150
|
-
return await import(specifier);
|
|
151
|
-
}
|
|
152
|
-
async function loadTsOrInstall() {
|
|
153
|
-
if (cachedTypeScriptModule && cachedTypeScriptApi) {
|
|
154
|
-
return cachedTypeScriptModule;
|
|
155
|
-
}
|
|
156
|
-
try {
|
|
157
|
-
cachedTypeScriptModule = await importTypeScriptModule();
|
|
158
|
-
cachedTypeScriptApi = await importTypeScriptApi();
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
throw new Error("TypeScript is required to analyze builder GraphQL schemas.", { cause: error });
|
|
162
|
-
}
|
|
163
|
-
return cachedTypeScriptModule;
|
|
164
|
-
}
|
|
165
|
-
function getTypeScriptModule() {
|
|
166
|
-
if (!cachedTypeScriptModule) {
|
|
167
|
-
throw new Error("TypeScript has not been loaded.");
|
|
168
|
-
}
|
|
169
|
-
return cachedTypeScriptModule;
|
|
170
|
-
}
|
|
171
|
-
async function parseSourceFile(sourceText, sourcePath) {
|
|
172
|
-
await loadTsOrInstall();
|
|
173
|
-
const extension = path.extname(sourcePath ?? "").toLowerCase();
|
|
174
|
-
const fileName = `graphql-source${extension === ".tsx" || extension === ".jsx" ? extension : ".ts"}`;
|
|
175
|
-
const tempDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "openapi-skills-graphql-"));
|
|
176
|
-
const tempFile = path.join(tempDirectory, fileName);
|
|
177
|
-
await fs.writeFile(tempFile, sourceText, "utf8");
|
|
178
|
-
if (!cachedTypeScriptApi) {
|
|
179
|
-
throw new Error("TypeScript API has not been loaded.");
|
|
180
|
-
}
|
|
181
|
-
const api = new cachedTypeScriptApi();
|
|
182
|
-
const snapshot = api.updateSnapshot({ openFiles: [tempFile] });
|
|
183
|
-
try {
|
|
184
|
-
const project = snapshot.getDefaultProjectForFile(tempFile);
|
|
185
|
-
const sourceFile = project?.program.getSourceFile(tempFile);
|
|
186
|
-
if (!sourceFile) {
|
|
187
|
-
throw new Error("TypeScript could not parse the GraphQL builder source.");
|
|
188
|
-
}
|
|
189
|
-
return sourceFile;
|
|
190
|
-
}
|
|
191
|
-
finally {
|
|
192
|
-
snapshot.dispose();
|
|
193
|
-
api.close();
|
|
194
|
-
await fs.remove(tempDirectory);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
export function looksLikeBuilderTsSchema(sourceText, sourcePath) {
|
|
198
|
-
if (sourcePath && [".ts", ".tsx", ".mts", ".cts"].includes(path.extname(sourcePath).toLowerCase())) {
|
|
199
|
-
return true;
|
|
200
|
-
}
|
|
201
|
-
return [
|
|
202
|
-
/\bbuilder\.queryType\s*\(/,
|
|
203
|
-
/\bbuilder\.mutationType\s*\(/,
|
|
204
|
-
/\bbuilder\.subscriptionType\s*\(/,
|
|
205
|
-
/\bobjectType\s*\(/,
|
|
206
|
-
/\bnew\s+SchemaBuilder\s*\(/,
|
|
207
|
-
].some(pattern => pattern.test(sourceText));
|
|
208
|
-
}
|
|
209
|
-
export async function loadSourceText(source) {
|
|
210
|
-
if (/^https?:\/\//i.test(source)) {
|
|
211
|
-
const response = await fetch(source);
|
|
212
|
-
if (!response.ok) {
|
|
213
|
-
throw new Error(`Failed to load GraphQL source from ${source}: HTTP ${response.status}`);
|
|
214
|
-
}
|
|
215
|
-
return await response.text();
|
|
216
|
-
}
|
|
217
|
-
return await fs.readFile(source, "utf8");
|
|
218
|
-
}
|
|
219
|
-
export function isGraphQL(text) {
|
|
220
|
-
return [
|
|
221
|
-
/\btype\s+Query\b/i,
|
|
222
|
-
/\btype\s+Mutation\b/i,
|
|
223
|
-
/\btype\s+Subscription\b/i,
|
|
224
|
-
/\bschema\s*\{/i,
|
|
225
|
-
/\bextend\s+type\s+Query\b/i,
|
|
226
|
-
/\bbuilder\.(queryType|mutationType|subscriptionType)\s*\(/i,
|
|
227
|
-
/\btoSchema\s*\(/i,
|
|
228
|
-
].some(pattern => pattern.test(text));
|
|
229
|
-
}
|
|
230
|
-
function firstStringLiteralValue(node) {
|
|
231
|
-
const ts = getTypeScriptModule();
|
|
232
|
-
if (!node) {
|
|
233
|
-
return undefined;
|
|
234
|
-
}
|
|
235
|
-
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
236
|
-
return node.text;
|
|
237
|
-
}
|
|
238
|
-
return undefined;
|
|
239
|
-
}
|
|
240
|
-
function getPropertyAssignment(objectLiteral, propertyName) {
|
|
241
|
-
const ts = getTypeScriptModule();
|
|
242
|
-
if (!objectLiteral) {
|
|
243
|
-
return undefined;
|
|
244
|
-
}
|
|
245
|
-
return objectLiteral.properties.find((property) => {
|
|
246
|
-
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)) {
|
|
247
|
-
const name = ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name)
|
|
248
|
-
? property.name.text
|
|
249
|
-
: property.name.getText();
|
|
250
|
-
return name === propertyName;
|
|
251
|
-
}
|
|
252
|
-
return false;
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
function getPropertyExpression(objectLiteral, propertyName) {
|
|
256
|
-
const ts = getTypeScriptModule();
|
|
257
|
-
const property = getPropertyAssignment(objectLiteral, propertyName);
|
|
258
|
-
if (!property || !ts.isPropertyAssignment(property)) {
|
|
259
|
-
return undefined;
|
|
260
|
-
}
|
|
261
|
-
return property.initializer;
|
|
262
|
-
}
|
|
263
|
-
function unwrapParenthesized(expression) {
|
|
264
|
-
const ts = getTypeScriptModule();
|
|
265
|
-
let current = expression;
|
|
266
|
-
while (current && ts.isParenthesizedExpression(current)) {
|
|
267
|
-
current = current.expression;
|
|
268
|
-
}
|
|
269
|
-
return current;
|
|
270
|
-
}
|
|
271
|
-
function getObjectLiteralFromExpression(expression) {
|
|
272
|
-
const ts = getTypeScriptModule();
|
|
273
|
-
const unwrapped = unwrapParenthesized(expression);
|
|
274
|
-
if (!unwrapped) {
|
|
275
|
-
return undefined;
|
|
276
|
-
}
|
|
277
|
-
if (ts.isObjectLiteralExpression(unwrapped)) {
|
|
278
|
-
return unwrapped;
|
|
279
|
-
}
|
|
280
|
-
if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {
|
|
281
|
-
if (ts.isBlock(unwrapped.body)) {
|
|
282
|
-
for (const statement of unwrapped.body.statements) {
|
|
283
|
-
if (ts.isReturnStatement(statement) && statement.expression) {
|
|
284
|
-
const returned = getObjectLiteralFromExpression(statement.expression);
|
|
285
|
-
if (returned) {
|
|
286
|
-
return returned;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return undefined;
|
|
291
|
-
}
|
|
292
|
-
return getObjectLiteralFromExpression(unwrapped.body);
|
|
293
|
-
}
|
|
294
|
-
return undefined;
|
|
295
|
-
}
|
|
296
|
-
function getRootTypeFromCallExpression(callExpression) {
|
|
297
|
-
const ts = getTypeScriptModule();
|
|
298
|
-
const expression = callExpression.expression;
|
|
299
|
-
if (!ts.isPropertyAccessExpression(expression)) {
|
|
300
|
-
return undefined;
|
|
301
|
-
}
|
|
302
|
-
const method = expression.name.text;
|
|
303
|
-
if (method === "queryType") {
|
|
304
|
-
return "query";
|
|
305
|
-
}
|
|
306
|
-
if (method === "mutationType") {
|
|
307
|
-
return "mutation";
|
|
308
|
-
}
|
|
309
|
-
if (method === "subscriptionType") {
|
|
310
|
-
return "subscription";
|
|
311
|
-
}
|
|
312
|
-
return undefined;
|
|
313
|
-
}
|
|
314
|
-
function getTypeNameFromExpression(expression, typeMaps) {
|
|
315
|
-
const ts = getTypeScriptModule();
|
|
316
|
-
const unwrapped = unwrapParenthesized(expression);
|
|
317
|
-
if (!unwrapped) {
|
|
318
|
-
return undefined;
|
|
319
|
-
}
|
|
320
|
-
if (ts.isStringLiteral(unwrapped) || ts.isNoSubstitutionTemplateLiteral(unwrapped)) {
|
|
321
|
-
return unwrapped.text;
|
|
322
|
-
}
|
|
323
|
-
if (ts.isIdentifier(unwrapped)) {
|
|
324
|
-
return typeMaps.objectTypeNames[unwrapped.text] ?? unwrapped.text;
|
|
325
|
-
}
|
|
326
|
-
return unwrapped.getText();
|
|
327
|
-
}
|
|
328
|
-
function inferBuilderOutputFieldTypeText(expression, typeMaps) {
|
|
329
|
-
const ts = getTypeScriptModule();
|
|
330
|
-
const unwrapped = unwrapParenthesized(expression);
|
|
331
|
-
if (!unwrapped) {
|
|
332
|
-
return undefined;
|
|
333
|
-
}
|
|
334
|
-
if (ts.isCallExpression(unwrapped)) {
|
|
335
|
-
const callee = unwrapped.expression.getText();
|
|
336
|
-
if (/\.exposeID$/i.test(callee))
|
|
337
|
-
return "ID";
|
|
338
|
-
if (/\.exposeString$/i.test(callee))
|
|
339
|
-
return "String";
|
|
340
|
-
if (/\.exposeInt$/i.test(callee))
|
|
341
|
-
return "Int";
|
|
342
|
-
if (/\.exposeFloat$/i.test(callee))
|
|
343
|
-
return "Float";
|
|
344
|
-
if (/\.exposeBoolean$/i.test(callee))
|
|
345
|
-
return "Boolean";
|
|
346
|
-
if (/\.string$/i.test(callee))
|
|
347
|
-
return "String";
|
|
348
|
-
if (/\.int$/i.test(callee))
|
|
349
|
-
return "Int";
|
|
350
|
-
if (/\.float$/i.test(callee))
|
|
351
|
-
return "Float";
|
|
352
|
-
if (/\.boolean$/i.test(callee))
|
|
353
|
-
return "Boolean";
|
|
354
|
-
if (/\.id$/i.test(callee))
|
|
355
|
-
return "ID";
|
|
356
|
-
if (/\.stringList$/i.test(callee))
|
|
357
|
-
return "[String]";
|
|
358
|
-
if (/\.intList$/i.test(callee))
|
|
359
|
-
return "[Int]";
|
|
360
|
-
if (/\.floatList$/i.test(callee))
|
|
361
|
-
return "[Float]";
|
|
362
|
-
if (/\.booleanList$/i.test(callee))
|
|
363
|
-
return "[Boolean]";
|
|
364
|
-
if (/\.idList$/i.test(callee))
|
|
365
|
-
return "[ID]";
|
|
366
|
-
const firstArg = unwrapped.arguments[0];
|
|
367
|
-
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
368
|
-
const typeExpr = getPropertyExpression(firstArg, "type");
|
|
369
|
-
if (typeExpr) {
|
|
370
|
-
const typeName = getTypeNameFromExpression(typeExpr, typeMaps);
|
|
371
|
-
if (typeName) {
|
|
372
|
-
return typeName;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return getTypeNameFromExpression(unwrapped, typeMaps);
|
|
378
|
-
}
|
|
379
|
-
function collectBuilderTypeMaps(sourceFile) {
|
|
380
|
-
const ts = getTypeScriptModule();
|
|
381
|
-
const objectTypeNames = {};
|
|
382
|
-
const rawFields = {};
|
|
383
|
-
function visit(node) {
|
|
384
|
-
if (ts.isVariableDeclaration(node) && node.initializer && ts.isCallExpression(node.initializer) && ts.isPropertyAccessExpression(node.initializer.expression)) {
|
|
385
|
-
if (node.initializer.expression.name.text === "objectRef") {
|
|
386
|
-
const firstArg = node.initializer.arguments[0];
|
|
387
|
-
const refName = node.name.getText(sourceFile);
|
|
388
|
-
if (firstArg && ts.isStringLiteral(firstArg)) {
|
|
389
|
-
objectTypeNames[refName] = firstArg.text;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "objectType" && node.arguments.length > 1) {
|
|
394
|
-
const typeArg = node.arguments[0];
|
|
395
|
-
const config = getObjectLiteralFromExpression(node.arguments[1]);
|
|
396
|
-
const typeName = getPropertyExpression(config, "name")
|
|
397
|
-
? firstStringLiteralValue(getPropertyExpression(config, "name"))
|
|
398
|
-
: typeArg && ts.isIdentifier(typeArg)
|
|
399
|
-
? objectTypeNames[typeArg.text] ?? typeArg.text
|
|
400
|
-
: typeArg && ts.isStringLiteral(typeArg)
|
|
401
|
-
? typeArg.text
|
|
402
|
-
: undefined;
|
|
403
|
-
if (typeName && config) {
|
|
404
|
-
const fieldsExpression = getPropertyExpression(config, "fields");
|
|
405
|
-
const fieldsObject = getObjectLiteralFromExpression(fieldsExpression);
|
|
406
|
-
if (fieldsObject) {
|
|
407
|
-
const fieldSpecs = {};
|
|
408
|
-
for (const property of fieldsObject.properties) {
|
|
409
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
const fieldName = ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name)
|
|
413
|
-
? property.name.text
|
|
414
|
-
: property.name.getText(sourceFile);
|
|
415
|
-
const fieldTypeText = inferBuilderOutputFieldTypeText(property.initializer, { objectTypeNames, objectFields: rawFields });
|
|
416
|
-
if (fieldTypeText) {
|
|
417
|
-
fieldSpecs[fieldName] = fieldTypeText;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
rawFields[typeName] = fieldSpecs;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
node.forEachChild(visit);
|
|
425
|
-
}
|
|
426
|
-
visit(sourceFile);
|
|
427
|
-
return { objectTypeNames, objectFields: rawFields };
|
|
428
|
-
}
|
|
429
|
-
function resolveBuilderOutputDescriptor(typeText, typeMaps, visited = new Set()) {
|
|
430
|
-
const normalized = typeText.trim();
|
|
431
|
-
if (!normalized) {
|
|
432
|
-
return { kind: "unknown", typeName: "Unknown" };
|
|
433
|
-
}
|
|
434
|
-
if (normalized.endsWith("!")) {
|
|
435
|
-
return {
|
|
436
|
-
kind: "non-null",
|
|
437
|
-
typeName: normalized,
|
|
438
|
-
required: true,
|
|
439
|
-
ofType: resolveBuilderOutputDescriptor(normalized.slice(0, -1), typeMaps, visited),
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
if (normalized.startsWith("[") && normalized.endsWith("]")) {
|
|
443
|
-
return {
|
|
444
|
-
kind: "list",
|
|
445
|
-
typeName: normalized,
|
|
446
|
-
ofType: resolveBuilderOutputDescriptor(normalized.slice(1, -1).trim(), typeMaps, visited),
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
if (scalarNames.has(normalized)) {
|
|
450
|
-
return { kind: "scalar", typeName: normalized };
|
|
451
|
-
}
|
|
452
|
-
const objectTypeName = typeMaps.objectTypeNames[normalized] ?? normalized;
|
|
453
|
-
if (visited.has(objectTypeName)) {
|
|
454
|
-
return { kind: "object", typeName: objectTypeName, circular: objectTypeName, fields: {} };
|
|
455
|
-
}
|
|
456
|
-
const fieldSpecs = typeMaps.objectFields[objectTypeName];
|
|
457
|
-
if (!fieldSpecs) {
|
|
458
|
-
return { kind: "object", typeName: objectTypeName };
|
|
459
|
-
}
|
|
460
|
-
const nextVisited = new Set(visited);
|
|
461
|
-
nextVisited.add(objectTypeName);
|
|
462
|
-
const fields = {};
|
|
463
|
-
for (const [fieldName, fieldTypeText] of Object.entries(fieldSpecs)) {
|
|
464
|
-
fields[fieldName] = resolveBuilderOutputDescriptor(fieldTypeText, typeMaps, nextVisited);
|
|
465
|
-
}
|
|
466
|
-
return {
|
|
467
|
-
kind: "object",
|
|
468
|
-
typeName: objectTypeName,
|
|
469
|
-
fields,
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
function isTypeWrapperText(typeText) {
|
|
473
|
-
return /^\[.*\]!?$/.test(typeText.trim()) || /!$/.test(typeText.trim());
|
|
474
|
-
}
|
|
475
|
-
function stripWrappers(typeText) {
|
|
476
|
-
let normalized = typeText.trim();
|
|
477
|
-
while (normalized.endsWith("!")) {
|
|
478
|
-
normalized = normalized.slice(0, -1).trim();
|
|
479
|
-
}
|
|
480
|
-
if (normalized.startsWith("[") && normalized.endsWith("]")) {
|
|
481
|
-
normalized = normalized.slice(1, -1).trim();
|
|
482
|
-
}
|
|
483
|
-
return normalized;
|
|
484
|
-
}
|
|
485
|
-
function classifyTypeName(typeText, isInput) {
|
|
486
|
-
const normalized = stripWrappers(typeText);
|
|
487
|
-
if (scalarNames.has(normalized)) {
|
|
488
|
-
return "scalar";
|
|
489
|
-
}
|
|
490
|
-
if (isInput && /input|filter|where|args?/i.test(normalized)) {
|
|
491
|
-
return "input-object";
|
|
492
|
-
}
|
|
493
|
-
return isInput ? "input-object" : "object";
|
|
494
|
-
}
|
|
495
|
-
function inferTypeDescriptorFromText(typeText, isInput) {
|
|
496
|
-
const normalized = typeText.trim();
|
|
497
|
-
if (!normalized) {
|
|
498
|
-
return { kind: "unknown", typeName: "Unknown" };
|
|
499
|
-
}
|
|
500
|
-
if (normalized.endsWith("!")) {
|
|
501
|
-
return {
|
|
502
|
-
kind: "non-null",
|
|
503
|
-
typeName: normalized,
|
|
504
|
-
required: true,
|
|
505
|
-
ofType: inferTypeDescriptorFromText(normalized.slice(0, -1), isInput),
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
if (normalized.startsWith("[") && normalized.endsWith("]")) {
|
|
509
|
-
const inner = normalized.slice(1, -1).trim();
|
|
510
|
-
return {
|
|
511
|
-
kind: "list",
|
|
512
|
-
typeName: normalized,
|
|
513
|
-
ofType: inferTypeDescriptorFromText(inner, isInput),
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
const kind = classifyTypeName(normalized, isInput);
|
|
517
|
-
return {
|
|
518
|
-
kind,
|
|
519
|
-
typeName: normalized,
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
function inferLiteralValue(expression) {
|
|
523
|
-
const ts = getTypeScriptModule();
|
|
524
|
-
const unwrapped = unwrapParenthesized(expression);
|
|
525
|
-
if (!unwrapped) {
|
|
526
|
-
return undefined;
|
|
527
|
-
}
|
|
528
|
-
if (ts.isStringLiteral(unwrapped) || ts.isNoSubstitutionTemplateLiteral(unwrapped)) {
|
|
529
|
-
return unwrapped.text;
|
|
530
|
-
}
|
|
531
|
-
if (ts.isNumericLiteral(unwrapped)) {
|
|
532
|
-
return Number(unwrapped.text);
|
|
533
|
-
}
|
|
534
|
-
if (unwrapped.kind === ts.SyntaxKind.TrueKeyword) {
|
|
535
|
-
return true;
|
|
536
|
-
}
|
|
537
|
-
if (unwrapped.kind === ts.SyntaxKind.FalseKeyword) {
|
|
538
|
-
return false;
|
|
539
|
-
}
|
|
540
|
-
if (unwrapped.kind === ts.SyntaxKind.NullKeyword) {
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
if (ts.isArrayLiteralExpression(unwrapped)) {
|
|
544
|
-
return unwrapped.elements.map((element) => inferLiteralValue(element));
|
|
545
|
-
}
|
|
546
|
-
if (ts.isObjectLiteralExpression(unwrapped)) {
|
|
547
|
-
const out = {};
|
|
548
|
-
for (const property of unwrapped.properties) {
|
|
549
|
-
if (ts.isPropertyAssignment(property)) {
|
|
550
|
-
const name = ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name)
|
|
551
|
-
? property.name.text
|
|
552
|
-
: property.name.getText();
|
|
553
|
-
out[name] = inferLiteralValue(property.initializer);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return out;
|
|
557
|
-
}
|
|
558
|
-
if (ts.isIdentifier(unwrapped) && unwrapped.text === "undefined") {
|
|
559
|
-
return undefined;
|
|
560
|
-
}
|
|
561
|
-
return undefined;
|
|
562
|
-
}
|
|
563
|
-
function inferArgDescriptorFromExpression(expression) {
|
|
564
|
-
const ts = getTypeScriptModule();
|
|
565
|
-
const unwrapped = unwrapParenthesized(expression);
|
|
566
|
-
if (!unwrapped) {
|
|
567
|
-
return { kind: "unknown", typeName: "Unknown" };
|
|
568
|
-
}
|
|
569
|
-
if (ts.isCallExpression(unwrapped)) {
|
|
570
|
-
const callee = unwrapped.expression.getText();
|
|
571
|
-
const firstArg = unwrapped.arguments[0];
|
|
572
|
-
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
573
|
-
const typeExpr = getPropertyExpression(firstArg, "type");
|
|
574
|
-
const requiredExpr = getPropertyExpression(firstArg, "required");
|
|
575
|
-
const defaultExpr = getPropertyExpression(firstArg, "defaultValue");
|
|
576
|
-
let descriptor;
|
|
577
|
-
if (typeExpr) {
|
|
578
|
-
descriptor = inferTypeDescriptorFromText(typeExpr.getText(), true);
|
|
579
|
-
}
|
|
580
|
-
else if (/\.string$/i.test(callee)) {
|
|
581
|
-
descriptor = { kind: "scalar", typeName: "String" };
|
|
582
|
-
}
|
|
583
|
-
else if (/\.int$/i.test(callee)) {
|
|
584
|
-
descriptor = { kind: "scalar", typeName: "Int" };
|
|
585
|
-
}
|
|
586
|
-
else if (/\.float$/i.test(callee)) {
|
|
587
|
-
descriptor = { kind: "scalar", typeName: "Float" };
|
|
588
|
-
}
|
|
589
|
-
else if (/\.boolean$/i.test(callee)) {
|
|
590
|
-
descriptor = { kind: "scalar", typeName: "Boolean" };
|
|
591
|
-
}
|
|
592
|
-
else if (/\.id$/i.test(callee)) {
|
|
593
|
-
descriptor = { kind: "scalar", typeName: "ID" };
|
|
594
|
-
}
|
|
595
|
-
if (descriptor) {
|
|
596
|
-
descriptor.defaultValue = inferLiteralValue(defaultExpr);
|
|
597
|
-
if (requiredExpr && inferLiteralValue(requiredExpr) === true) {
|
|
598
|
-
return {
|
|
599
|
-
kind: "non-null",
|
|
600
|
-
typeName: `${descriptor.typeName}!`,
|
|
601
|
-
required: true,
|
|
602
|
-
ofType: descriptor,
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
return descriptor;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
if (/\.string$/i.test(callee)) {
|
|
609
|
-
return { kind: "scalar", typeName: "String" };
|
|
610
|
-
}
|
|
611
|
-
if (/\.int$/i.test(callee)) {
|
|
612
|
-
return { kind: "scalar", typeName: "Int" };
|
|
613
|
-
}
|
|
614
|
-
if (/\.float$/i.test(callee)) {
|
|
615
|
-
return { kind: "scalar", typeName: "Float" };
|
|
616
|
-
}
|
|
617
|
-
if (/\.boolean$/i.test(callee)) {
|
|
618
|
-
return { kind: "scalar", typeName: "Boolean" };
|
|
619
|
-
}
|
|
620
|
-
if (/\.id$/i.test(callee)) {
|
|
621
|
-
return { kind: "scalar", typeName: "ID" };
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
return inferTypeDescriptorFromText(unwrapped.getText(), true);
|
|
625
|
-
}
|
|
626
|
-
function inferOutputDescriptorFromTypeText(typeText) {
|
|
627
|
-
return inferTypeDescriptorFromText(typeText, false);
|
|
628
|
-
}
|
|
629
|
-
function buildScalarDefault(typeName) {
|
|
630
|
-
const baseType = stripWrappers(typeName);
|
|
631
|
-
if (baseType === "Boolean") {
|
|
632
|
-
return false;
|
|
633
|
-
}
|
|
634
|
-
if (baseType === "Int" || baseType === "Float") {
|
|
635
|
-
return 0;
|
|
636
|
-
}
|
|
637
|
-
if (baseType === "ID" || baseType === "String") {
|
|
638
|
-
return "";
|
|
639
|
-
}
|
|
640
|
-
return null;
|
|
641
|
-
}
|
|
642
|
-
function buildDefaultValue(descriptor) {
|
|
643
|
-
if (descriptor.kind === "non-null" && descriptor.ofType) {
|
|
644
|
-
return buildDefaultValue(descriptor.ofType);
|
|
645
|
-
}
|
|
646
|
-
if (descriptor.kind === "list") {
|
|
647
|
-
return [];
|
|
648
|
-
}
|
|
649
|
-
if (descriptor.kind === "scalar") {
|
|
650
|
-
return buildScalarDefault(descriptor.typeName);
|
|
651
|
-
}
|
|
652
|
-
if (descriptor.kind === "enum") {
|
|
653
|
-
return descriptor.enumValues?.[0] ?? null;
|
|
654
|
-
}
|
|
655
|
-
if (descriptor.kind === "input-object") {
|
|
656
|
-
const out = {};
|
|
657
|
-
for (const [key, value] of Object.entries(descriptor.fields ?? {})) {
|
|
658
|
-
out[key] = buildDefaultValue(value);
|
|
659
|
-
}
|
|
660
|
-
return out;
|
|
661
|
-
}
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
function descriptorToTypeString(descriptor) {
|
|
665
|
-
if (descriptor.kind === "non-null" && descriptor.ofType) {
|
|
666
|
-
return `${descriptorToTypeString(descriptor.ofType)}!`;
|
|
667
|
-
}
|
|
668
|
-
if (descriptor.kind === "list" && descriptor.ofType) {
|
|
669
|
-
return `[${descriptorToTypeString(descriptor.ofType)}]`;
|
|
670
|
-
}
|
|
671
|
-
return descriptor.typeName;
|
|
672
|
-
}
|
|
673
|
-
function isLeafDescriptor(descriptor) {
|
|
674
|
-
const typeName = stripWrappers(descriptor.typeName);
|
|
675
|
-
return descriptor.kind === "scalar" || descriptor.kind === "enum" || scalarNames.has(typeName);
|
|
676
|
-
}
|
|
677
|
-
function buildSelectionSet(descriptor, seen = new Set()) {
|
|
678
|
-
if (descriptor.kind === "non-null" && descriptor.ofType) {
|
|
679
|
-
return buildSelectionSet(descriptor.ofType, seen);
|
|
680
|
-
}
|
|
681
|
-
if (descriptor.kind === "list" && descriptor.ofType) {
|
|
682
|
-
return buildSelectionSet(descriptor.ofType, seen);
|
|
683
|
-
}
|
|
684
|
-
if (isLeafDescriptor(descriptor)) {
|
|
685
|
-
return "";
|
|
686
|
-
}
|
|
687
|
-
const typeName = stripWrappers(descriptor.typeName);
|
|
688
|
-
if (seen.has(typeName)) {
|
|
689
|
-
return "__typename";
|
|
690
|
-
}
|
|
691
|
-
if (descriptor.kind === "object" || descriptor.kind === "input-object") {
|
|
692
|
-
const childSeen = new Set(seen);
|
|
693
|
-
childSeen.add(typeName);
|
|
694
|
-
const fieldEntries = Object.entries(descriptor.fields ?? {});
|
|
695
|
-
if (fieldEntries.length === 0) {
|
|
696
|
-
return "{ __typename }";
|
|
697
|
-
}
|
|
698
|
-
const childSelections = fieldEntries.map(([name, childDescriptor]) => {
|
|
699
|
-
const nestedSelection = buildSelectionSet(childDescriptor, childSeen);
|
|
700
|
-
if (!nestedSelection) {
|
|
701
|
-
return name;
|
|
702
|
-
}
|
|
703
|
-
if (childDescriptor.kind === "object" || childDescriptor.kind === "input-object" || childDescriptor.kind === "list" || childDescriptor.kind === "non-null") {
|
|
704
|
-
const wrappedSelection = nestedSelection.startsWith("{") ? nestedSelection : `{ ${nestedSelection} }`;
|
|
705
|
-
return `${name} ${wrappedSelection}`;
|
|
706
|
-
}
|
|
707
|
-
return `${name} ${nestedSelection}`;
|
|
708
|
-
});
|
|
709
|
-
return `{ ${childSelections.join(" ")} }`;
|
|
710
|
-
}
|
|
711
|
-
return "__typename";
|
|
712
|
-
}
|
|
713
|
-
function buildQueryFromEndpoint(endpoint) {
|
|
714
|
-
const variableNames = Object.keys(endpoint.args);
|
|
715
|
-
const variableDeclarations = variableNames.map(name => {
|
|
716
|
-
const descriptor = endpoint.args[name];
|
|
717
|
-
return `$${name}: ${descriptor ? descriptorToTypeString(descriptor) : "String"}`;
|
|
718
|
-
});
|
|
719
|
-
const variables = {};
|
|
720
|
-
for (const [name, descriptor] of Object.entries(endpoint.args)) {
|
|
721
|
-
variables[name] = buildDefaultValue(descriptor);
|
|
722
|
-
}
|
|
723
|
-
const callArguments = variableNames.map(name => `${name}: $${name}`).join(", ");
|
|
724
|
-
const selectionSet = buildSelectionSet(endpoint.returns);
|
|
725
|
-
const fieldSelection = selectionSet ? ` ${selectionSet}` : "";
|
|
726
|
-
const operationName = sanitizeOperationPath(endpoint.operationId) || endpoint.operationId;
|
|
727
|
-
const variableDeclarationText = variableDeclarations.length > 0 ? `(${variableDeclarations.join(", ")})` : "";
|
|
728
|
-
const callArgumentText = callArguments.length > 0 ? `(${callArguments})` : "";
|
|
729
|
-
return {
|
|
730
|
-
query: `${endpoint.rootType} ${operationName}${variableDeclarationText} { ${endpoint.name}${callArgumentText}${fieldSelection} }`,
|
|
731
|
-
variables,
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
|
-
function extractGraphQLEndpointsFromSDL(sourceText) {
|
|
735
|
-
const schema = buildSchema(sourceText);
|
|
736
|
-
const rootTypes = [
|
|
737
|
-
["query", schema.getQueryType()],
|
|
738
|
-
["mutation", schema.getMutationType()],
|
|
739
|
-
["subscription", schema.getSubscriptionType()],
|
|
740
|
-
];
|
|
741
|
-
const endpoints = [];
|
|
742
|
-
for (const [rootType, root] of rootTypes) {
|
|
743
|
-
if (!root) {
|
|
744
|
-
continue;
|
|
745
|
-
}
|
|
746
|
-
for (const field of Object.values(root.getFields())) {
|
|
747
|
-
const args = {};
|
|
748
|
-
for (const arg of field.args) {
|
|
749
|
-
const descriptor = describeInputType(arg.type);
|
|
750
|
-
if (arg.defaultValue !== undefined) {
|
|
751
|
-
descriptor.defaultValue = arg.defaultValue;
|
|
752
|
-
}
|
|
753
|
-
args[arg.name] = descriptor;
|
|
754
|
-
}
|
|
755
|
-
const endpoint = {
|
|
756
|
-
name: field.name,
|
|
757
|
-
operationId: field.name,
|
|
758
|
-
sanitizedOperationId: sanitizeOperationPath(field.name),
|
|
759
|
-
rootType,
|
|
760
|
-
args,
|
|
761
|
-
returns: describeOutputType(field.type),
|
|
762
|
-
};
|
|
763
|
-
const summary = field.description?.split("\n").map(line => line.trim()).filter(Boolean)[0];
|
|
764
|
-
if (summary) {
|
|
765
|
-
endpoint.summary = summary;
|
|
766
|
-
}
|
|
767
|
-
if (field.description) {
|
|
768
|
-
endpoint.description = field.description;
|
|
769
|
-
}
|
|
770
|
-
endpoints.push(endpoint);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
return endpoints;
|
|
774
|
-
}
|
|
775
|
-
function describeInputType(type, visited = new Set()) {
|
|
776
|
-
if (isNonNullType(type)) {
|
|
777
|
-
return {
|
|
778
|
-
kind: "non-null",
|
|
779
|
-
typeName: type.toString(),
|
|
780
|
-
required: true,
|
|
781
|
-
ofType: describeInputType(type.ofType, visited),
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
if (isListType(type)) {
|
|
785
|
-
return {
|
|
786
|
-
kind: "list",
|
|
787
|
-
typeName: type.toString(),
|
|
788
|
-
ofType: describeInputType(type.ofType, visited),
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
if (isScalarType(type)) {
|
|
792
|
-
return { kind: "scalar", typeName: type.name };
|
|
793
|
-
}
|
|
794
|
-
if (isEnumType(type)) {
|
|
795
|
-
return { kind: "enum", typeName: type.name, enumValues: type.getValues().map(value => value.name) };
|
|
796
|
-
}
|
|
797
|
-
if (isInputObjectType(type)) {
|
|
798
|
-
if (visited.has(type.name)) {
|
|
799
|
-
return { kind: "input-object", typeName: type.name, circular: type.name, fields: {} };
|
|
800
|
-
}
|
|
801
|
-
const nextVisited = new Set(visited);
|
|
802
|
-
nextVisited.add(type.name);
|
|
803
|
-
const fields = {};
|
|
804
|
-
for (const [fieldName, field] of Object.entries(type.getFields())) {
|
|
805
|
-
const descriptor = describeInputType(field.type, nextVisited);
|
|
806
|
-
if (field.defaultValue !== undefined) {
|
|
807
|
-
descriptor.defaultValue = field.defaultValue;
|
|
808
|
-
}
|
|
809
|
-
fields[fieldName] = descriptor;
|
|
810
|
-
}
|
|
811
|
-
return {
|
|
812
|
-
kind: "input-object",
|
|
813
|
-
typeName: type.name,
|
|
814
|
-
fields,
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
return { kind: "unknown", typeName: String(type) };
|
|
818
|
-
}
|
|
819
|
-
function describeOutputType(type, visited = new Set()) {
|
|
820
|
-
if (isNonNullType(type)) {
|
|
821
|
-
return {
|
|
822
|
-
kind: "non-null",
|
|
823
|
-
typeName: type.toString(),
|
|
824
|
-
required: true,
|
|
825
|
-
ofType: describeOutputType(type.ofType, visited),
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
if (isListType(type)) {
|
|
829
|
-
return {
|
|
830
|
-
kind: "list",
|
|
831
|
-
typeName: type.toString(),
|
|
832
|
-
ofType: describeOutputType(type.ofType, visited),
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
if (isScalarType(type)) {
|
|
836
|
-
return { kind: "scalar", typeName: type.name };
|
|
837
|
-
}
|
|
838
|
-
if (isEnumType(type)) {
|
|
839
|
-
return { kind: "enum", typeName: type.name, enumValues: type.getValues().map(value => value.name) };
|
|
840
|
-
}
|
|
841
|
-
if (isObjectType(type)) {
|
|
842
|
-
if (visited.has(type.name)) {
|
|
843
|
-
return { kind: "object", typeName: type.name, circular: type.name, fields: {} };
|
|
844
|
-
}
|
|
845
|
-
const nextVisited = new Set(visited);
|
|
846
|
-
nextVisited.add(type.name);
|
|
847
|
-
const fields = {};
|
|
848
|
-
for (const [fieldName, field] of Object.entries(type.getFields())) {
|
|
849
|
-
fields[fieldName] = describeOutputType(field.type, nextVisited);
|
|
850
|
-
}
|
|
851
|
-
return {
|
|
852
|
-
kind: "object",
|
|
853
|
-
typeName: type.name,
|
|
854
|
-
fields,
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
return { kind: "unknown", typeName: type.toString() };
|
|
858
|
-
}
|
|
859
|
-
async function extractGraphQLEndpointsFromBuilderSource(sourceText, sourcePath) {
|
|
860
|
-
const ts = await loadTsOrInstall();
|
|
861
|
-
const sourceFile = await parseSourceFile(sourceText, sourcePath);
|
|
862
|
-
const typeMaps = collectBuilderTypeMaps(sourceFile);
|
|
863
|
-
const endpoints = [];
|
|
864
|
-
function visit(node) {
|
|
865
|
-
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
866
|
-
const rootType = getRootTypeFromCallExpression(node);
|
|
867
|
-
if (rootType && node.arguments.length > 0) {
|
|
868
|
-
const config = getObjectLiteralFromExpression(node.arguments[0]);
|
|
869
|
-
const fieldsExpression = getPropertyExpression(config, "fields");
|
|
870
|
-
const fieldsObject = getObjectLiteralFromExpression(fieldsExpression);
|
|
871
|
-
if (fieldsObject) {
|
|
872
|
-
for (const property of fieldsObject.properties) {
|
|
873
|
-
if (!ts.isPropertyAssignment(property)) {
|
|
874
|
-
continue;
|
|
875
|
-
}
|
|
876
|
-
const rawName = ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name)
|
|
877
|
-
? property.name.text
|
|
878
|
-
: property.name.getText(sourceFile);
|
|
879
|
-
const fieldConfig = getObjectLiteralFromExpression(property.initializer) ?? getObjectLiteralFromExpression(property.initializer);
|
|
880
|
-
const maybeFieldExpression = unwrapParenthesized(property.initializer);
|
|
881
|
-
const fieldCallExpression = maybeFieldExpression && ts.isCallExpression(maybeFieldExpression) ? maybeFieldExpression : undefined;
|
|
882
|
-
const callArgumentsObject = fieldCallExpression ? getObjectLiteralFromExpression(fieldCallExpression.arguments[0]) : fieldConfig;
|
|
883
|
-
const typeExpression = getPropertyExpression(callArgumentsObject, "type");
|
|
884
|
-
const summary = firstStringLiteralValue(getPropertyExpression(callArgumentsObject, "summary"));
|
|
885
|
-
const description = firstStringLiteralValue(getPropertyExpression(callArgumentsObject, "description"));
|
|
886
|
-
const argsExpression = getPropertyExpression(callArgumentsObject, "args");
|
|
887
|
-
const argsObject = getObjectLiteralFromExpression(argsExpression);
|
|
888
|
-
const args = {};
|
|
889
|
-
if (argsObject) {
|
|
890
|
-
for (const argProperty of argsObject.properties) {
|
|
891
|
-
if (!ts.isPropertyAssignment(argProperty)) {
|
|
892
|
-
continue;
|
|
893
|
-
}
|
|
894
|
-
const argName = ts.isIdentifier(argProperty.name) || ts.isStringLiteral(argProperty.name) || ts.isNumericLiteral(argProperty.name)
|
|
895
|
-
? argProperty.name.text
|
|
896
|
-
: argProperty.name.getText(sourceFile);
|
|
897
|
-
args[argName] = inferArgDescriptorFromExpression(argProperty.initializer);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
const endpoint = {
|
|
901
|
-
name: rawName,
|
|
902
|
-
operationId: rawName,
|
|
903
|
-
sanitizedOperationId: sanitizeOperationPath(rawName),
|
|
904
|
-
rootType,
|
|
905
|
-
args,
|
|
906
|
-
returns: typeExpression ? resolveBuilderOutputDescriptor(typeExpression.getText(sourceFile), typeMaps) : { kind: "unknown", typeName: "Unknown" },
|
|
907
|
-
};
|
|
908
|
-
if (summary) {
|
|
909
|
-
endpoint.summary = summary;
|
|
910
|
-
}
|
|
911
|
-
if (description) {
|
|
912
|
-
endpoint.description = description;
|
|
913
|
-
}
|
|
914
|
-
endpoints.push(endpoint);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
node.forEachChild(visit);
|
|
920
|
-
}
|
|
921
|
-
visit(sourceFile);
|
|
922
|
-
return endpoints;
|
|
923
|
-
}
|
|
924
|
-
export async function extractGraphQLEndpoints(sourceText, sourcePath) {
|
|
925
|
-
if (looksLikeBuilderTsSchema(sourceText, sourcePath)) {
|
|
926
|
-
return await extractGraphQLEndpointsFromBuilderSource(sourceText, sourcePath);
|
|
927
|
-
}
|
|
928
|
-
return extractGraphQLEndpointsFromSDL(sourceText);
|
|
929
|
-
}
|
|
930
|
-
export async function findGraphQLEndpoint(sourceText, rootType, fieldName, sourcePath) {
|
|
931
|
-
const endpoints = await extractGraphQLEndpoints(sourceText, sourcePath);
|
|
932
|
-
const endpoint = endpoints.find(entry => entry.rootType === rootType && entry.name === fieldName);
|
|
933
|
-
if (!endpoint) {
|
|
934
|
-
throw new Error(`GraphQL field '${fieldName}' not found on root type '${rootType}'.`);
|
|
935
|
-
}
|
|
936
|
-
return endpoint;
|
|
937
|
-
}
|
|
938
|
-
export function buildGraphQLArtifact(endpoint) {
|
|
939
|
-
return buildQueryFromEndpoint(endpoint);
|
|
940
|
-
}
|
|
941
|
-
export function buildGraphQLOperationSchema(endpoint) {
|
|
942
|
-
return endpoint;
|
|
943
|
-
}
|
|
944
|
-
export function getGraphQLRootTypeFromMethod(method) {
|
|
945
|
-
const normalized = method.toLowerCase();
|
|
946
|
-
if (normalized === "query" || normalized === "mutation" || normalized === "subscription") {
|
|
947
|
-
return normalized;
|
|
948
|
-
}
|
|
949
|
-
return undefined;
|
|
950
|
-
}
|
|
951
|
-
export function isGraphQLTypeName(typeName) {
|
|
952
|
-
const normalized = stripWrappers(typeName);
|
|
953
|
-
return !scalarNames.has(normalized);
|
|
954
|
-
}
|
|
955
|
-
export function defaultValueForDescriptor(descriptor) {
|
|
956
|
-
return buildDefaultValue(descriptor);
|
|
957
|
-
}
|
|
958
|
-
export function typeStringForDescriptor(descriptor) {
|
|
959
|
-
return descriptorToTypeString(descriptor);
|
|
960
|
-
}
|
|
961
|
-
export function selectionSetForDescriptor(descriptor) {
|
|
962
|
-
return buildSelectionSet(descriptor);
|
|
963
|
-
}
|
|
964
|
-
export function descriptorIsLeaf(descriptor) {
|
|
965
|
-
return isLeafDescriptor(descriptor);
|
|
966
|
-
}
|
|
967
|
-
//# sourceMappingURL=graphql.js.map
|