@gqlts/runtime
Version:
Gqlts runtime client
156 lines • 6.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateGraphqlOperation = generateGraphqlOperation;
const getFieldFromPath_1 = require("./getFieldFromPath");
const selectionMetaFields = new Set(['__alias', '__name', '__scalar']);
function parseAliasRequest(aliasName, aliasRequest, ctx, path, opt) {
if (!aliasRequest || typeof aliasRequest !== 'object' || Array.isArray(aliasRequest)) {
throw new Error(`Alias \`${aliasName}\` should select exactly one field`);
}
const aliasFields = aliasRequest;
const selectedFieldNames = Object.keys(aliasFields).filter((fieldName) => Boolean(aliasFields[fieldName]));
if (selectedFieldNames.length !== 1) {
throw new Error(`Alias \`${aliasName}\` should select exactly one field`);
}
const fieldName = selectedFieldNames[0];
const parsed = parseRequest(aliasFields[fieldName], ctx, [...path, fieldName], opt);
return `${aliasName}:${fieldName}${parsed}`;
}
function parseAliases(aliases, ctx, path, opt) {
if (!aliases) {
return [];
}
if (typeof aliases !== 'object' || Array.isArray(aliases)) {
throw new Error('`__alias` should be an object');
}
return Object.keys(aliases)
.filter((aliasName) => Boolean(aliases[aliasName]))
.map((aliasName) => parseAliasRequest(aliasName, aliases[aliasName], ctx, path, opt));
}
function parseRequest(request, ctx, path, opt) {
if (Array.isArray(request)) {
const [args, fields] = request;
const argNames = Object.keys(args);
if (argNames.length === 0) {
return parseRequest(fields, ctx, path, opt);
}
const field = (0, getFieldFromPath_1.getFieldFromPath)(ctx.root, path);
return `(${argNames.map((argName) => {
ctx.varCounter++;
const varName = `v${ctx.varCounter}`;
const typing = field.args && field.args[argName];
if (!typing && !opt?.skipTypingCheck) {
throw new Error(`No typing defined for argument \`${argName}\` in path \`${path.join('.')}\``);
}
let varType = typing;
const value = args?.[argName];
let inferredTypeFromValue;
// Check if the value is not undefined and if the type is not defined
if (opt?.skipTypingCheck && !typing) {
const valueType = typeof value;
switch (valueType) {
case 'string':
inferredTypeFromValue = 'String';
break;
case 'number':
inferredTypeFromValue = 'Int';
break;
case 'boolean':
inferredTypeFromValue = 'Boolean';
break;
case 'object':
if (value === null) {
inferredTypeFromValue = 'Null';
}
else if (Array.isArray(value)) {
inferredTypeFromValue = '[String]'; // Assuming array of strings for simplicity
}
else {
inferredTypeFromValue = 'Object';
}
break;
}
if (!inferredTypeFromValue) {
throw new Error(`No typing defined for argument \`${argName}\` in path \`${path.join('.')}\``);
}
varType = varType || [{ name: inferredTypeFromValue, scalar: [], fields: {} }, inferredTypeFromValue];
console.warn(`Infer type for argument \`${argName}\` in path \`${path.join('.')}\` as \`${inferredTypeFromValue}\` - consider adding typing and updating the schema`);
}
ctx.variables[varName] = {
value: value,
typing: varType,
};
return `${argName}:$${varName}`;
})})${parseRequest(fields, ctx, path, opt)}`;
}
else if (typeof request === 'object' && request !== null) {
const fields = request;
const fieldNames = Object.keys(fields).filter((k) => Boolean(fields[k]));
if (fieldNames.length === 0) {
throw new Error('Field selection should not be empty');
}
const type = path.length > 0 ? (0, getFieldFromPath_1.getFieldFromPath)(ctx.root, path).type : ctx.root;
const scalarFields = type.scalar;
let scalarFieldsFragment;
if (fieldNames.includes('__scalar')) {
const falsyFieldNames = new Set(Object.keys(fields).filter((k) => !Boolean(fields[k])));
if (scalarFields?.length) {
ctx.fragmentCounter++;
scalarFieldsFragment = `f${ctx.fragmentCounter}`;
ctx.fragments.push(`fragment ${scalarFieldsFragment} on ${type.name}{${scalarFields
.filter((f) => !falsyFieldNames.has(f))
.join(',')}}`);
}
}
const fieldsSelection = fieldNames
.filter((f) => !selectionMetaFields.has(f))
.map((f) => {
const parsed = parseRequest(fields[f], ctx, [...path, f], opt);
if (f.startsWith('on_')) {
ctx.fragmentCounter++;
const implementationFragment = `f${ctx.fragmentCounter}`;
const typeMatch = f.match(/^on_(.+)/);
if (!typeMatch || !typeMatch[1])
throw new Error(`Invalid fragment type in field: ${f}`);
ctx.fragments.push(`fragment ${implementationFragment} on ${typeMatch[1]}${parsed}`);
return `...${implementationFragment}`;
}
else {
return `${f}${parsed}`;
}
})
.concat(parseAliases(fields.__alias, ctx, path, opt))
.concat(scalarFieldsFragment ? [`...${scalarFieldsFragment}`] : [])
.join(',');
return `{${fieldsSelection}}`;
}
else {
return '';
}
}
function generateGraphqlOperation(operation, root, fields, opt) {
const ctx = {
root,
varCounter: 0,
variables: {},
fragmentCounter: 0,
fragments: [],
};
const result = parseRequest(fields, ctx, [], opt);
const varNames = Object.keys(ctx.variables);
const varsString = varNames.length > 0
? `(${varNames.map((v) => {
const variableType = ctx.variables[v].typing[1];
return `$${v}:${variableType}`;
})})`
: '';
const operationName = fields.__name || '';
return {
query: [`${operation} ${operationName}${varsString}${result}`, ...ctx.fragments].join(','),
variables: Object.keys(ctx.variables).reduce((r, v) => {
r[v] = ctx.variables[v].value;
return r;
}, {}),
};
}
//# sourceMappingURL=generateGraphqlOperation.js.map