"use strict"; // Simple data type to represent a query path and corresponding schema path tied together, because these are basically always used together, and it would bloat up the implementation code otherwise function createInstance({ queryPath, schemaPath, queryObject, schemaObject, parent }) { let self; // eslint-disable-next-line no-return-assign return self = { queryPath: queryPath, schemaPath: schemaPath, query: queryObject, schema: schemaObject, child: function (queryKey) { let newQueryPath = (queryKey != null) ? queryPath.concat([ queryKey ]) : queryPath; let newQueryObject = (queryKey != null) ? queryObject[queryKey] : queryObject; // TODO: Is this correct even when the queryKey is null, and so we remain at the same object? // $key is used for handling aliases let effectiveSchemaKey = (newQueryObject?.$key != null) ? newQueryObject.$key : queryKey; let newSchemaPath = (effectiveSchemaKey != null) ? schemaPath.concat([ effectiveSchemaKey ]) : schemaPath; let newSchemaObject = (effectiveSchemaKey != null) ? schemaObject[effectiveSchemaKey] : schemaObject; return createInstance({ queryPath: newQueryPath, schemaPath: newSchemaPath, queryObject: newQueryObject, schemaObject: newSchemaObject, parent: self }); }, parent: parent, override: function ({ query, schema }) { return createInstance({ queryPath: queryPath, schemaPath: schemaPath, queryObject: query ?? queryObject, schemaObject: schema ?? schemaObject, // An override doesn't change the path, so the parent shouldn't change either parent: self.parent }); }, toPathString: function () { return queryPath .map((segment, i) => { if (segment === schemaPath[i]) { return segment; } else { // This is used for representing aliases, showing the original schema key in brackets return `${segment} [${schemaPath[i]}]`; } }) .join(" -> "); } }; } module.exports = function createCursor({ query, schema }) { return createInstance({ queryPath: [], schemaPath: [], queryObject: query, schemaObject: schema, parent: undefined }); };