@controlplane/cli
Version:
Control Plane Corporation CLI
273 lines • 9.35 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.completePath = void 0;
class StepManager {
constructor() {
this.step = new Step();
}
addEntry(item) {
let owningStep = this.step;
for (let partIndex in item.parts.slice(0, -1)) {
const part = item.parts[partIndex];
const hasEntry = owningStep.entries.find((i) => i.name === part);
if (hasEntry) {
if (!hasEntry.nextStep) {
hasEntry.addStep();
}
owningStep = hasEntry.nextStep;
continue;
}
const newEntry = new Entry(part, [], true);
owningStep.entries.push(newEntry);
owningStep = newEntry.nextStep;
}
const entry = new Entry(item.parts.pop(), item.methods);
owningStep.entries.push(entry);
return;
}
getPathsForEntry(userInput, entry, method) {
let step = this.step;
const inputs = userInput.split('/').filter(Boolean);
if (userInput.endsWith('/')) {
inputs.push('/');
}
let path = '/';
for (let input of inputs.slice(0, -1)) {
let currentEntry = step.entries.find(e => e.name === input);
if (!currentEntry && step.hasParameter) {
currentEntry = step.parameter;
path += input;
}
else if (currentEntry) {
path += currentEntry.name;
}
path += '/';
if (!currentEntry || !currentEntry.nextStep) {
throw new Error('Invalid path for entry, path was: ' + path);
}
step = currentEntry.nextStep;
}
const lastPart = inputs.pop();
if (lastPart === entry.name) {
if (entry.isEndpoint) {
return [];
}
if (entry.hasFollowingPath) {
if (entry.nextStep.hasParameter) {
return [];
}
if (entry.nextStep.entries.length === 1) {
return [
userInput + '/' + entry.nextStep.entries[0].name,
userInput + '/' + entry.nextStep.entries[0].name + '/'
];
}
return [];
}
}
const result = [];
if (entry.isEndpoint) {
if (entry.methods.includes(method)) {
result.push(path + entry.name);
}
if (entry.hasFollowingPath) {
result.push(path + entry.name + '/');
}
return result;
}
if (entry.hasFollowingPath) {
if (entry.nextStep.guessable !== 'yes') {
result.push(path + entry.name + '/');
return result;
}
for (let nextEntry of entry.nextStep.entries) {
if (nextEntry.isEndpoint) {
result.push(path + entry.name + '/' + nextEntry.name);
}
if (nextEntry.hasFollowingPath) {
result.push(path + entry.name + '/' + nextEntry.name + '/');
}
}
}
return result;
}
}
class Step {
constructor() {
this.entries = [];
}
get guessable() {
const hasParam = this.entries.some((i) => i.isParameter);
const hasNonParam = this.entries.some((i) => !i.isParameter);
if (hasParam && hasNonParam) {
return 'maybe';
}
if (hasParam) {
return 'no';
}
return 'yes';
}
get hasParameter() {
return this.entries.some(e => e.isParameter);
}
get parameter() {
return this.entries.find(e => e.isParameter);
}
validEntriesForMethod(method) {
return this.entries.filter(e => e.validForMethod(method));
}
}
class Entry {
constructor(name, methods, addStep) {
this.name = name;
this.methods = methods;
if (addStep) {
this.addStep();
}
}
get hasFollowingPath() {
return !!this.nextStep;
}
get isParameter() {
return this.name.startsWith(':');
}
get isEndpoint() {
return this.methods.length > 0;
}
addStep() {
this.nextStep = new Step();
}
validForMethod(method) {
if (!this.nextStep) {
return this.methods.includes(method);
}
if (this.methods.includes(method)) {
return true;
}
for (let entry of this.nextStep.entries) {
const entryIsValid = entry.validForMethod(method);
if (entryIsValid) {
return true;
}
}
return false;
}
}
function generateResourceMap(rawMap) {
const stepManager = new StepManager();
const mapItems = rawMap
.map((i) => ({ parts: i.path.split('/').filter(Boolean), methods: i.supports }))
.sort((a, b) => a.parts.length - b.parts.length);
for (let item of mapItems) {
stepManager.addEntry(item);
}
return stepManager;
}
function completePath(input, method, rawMap) {
if (!input.startsWith('/')) {
return [];
}
const inputs = input.split('/').filter(Boolean);
if (input.endsWith('/')) {
inputs.push('');
}
let stepManager = generateResourceMap(rawMap);
let step = stepManager.step;
// If user inputs only / character, split returns empty array,
// so we return each possibility
if (inputs.length < 1) {
const completions = [];
step.validEntriesForMethod(method)
.forEach((e) => {
if (e.isEndpoint && e.methods.includes(method)) {
completions.push(`/${e.name}`);
}
if (e.hasFollowingPath) {
completions.push(`/${e.name}/`);
}
});
if (completions.length === 1) {
if (completions[0].endsWith('/')) {
completions.push(completions[0].slice(0, -1));
}
else {
completions.push(completions[0] + '/');
}
}
return completions;
}
const completionEntries = [];
// We go through each part of user input
for (let inputIndex in inputs) {
const inputForStep = inputs[inputIndex];
// if user input is not valid within the resource map, return | (A)
if (!step) {
return [];
}
// if not at last part of input - ex: /org/coke - coke is the last part
// Lower step
if (parseInt(inputIndex) < inputs.length - 1) {
let lowerStepEntry = step.entries.find((i) => i.name === inputForStep);
// if not found with input, and this step has a parameter entry
if (!lowerStepEntry && step.hasParameter) {
lowerStepEntry = step.entries.find((i) => i.isParameter);
}
// if no parameter entry found, then it means input is invalid, so we return empty
if (!lowerStepEntry) {
return [];
}
// We made all the check with this part of input, we go one step up in the hierarchy and finish the iteration
step = lowerStepEntry.nextStep;
continue;
}
// Current step of input
else if (parseInt(inputIndex) === inputs.length - 1) {
let currentStepEntry = step.entries.find((i) => i.name === inputForStep);
if (currentStepEntry) {
if (currentStepEntry.isEndpoint) {
return [];
}
if (currentStepEntry.hasFollowingPath) {
return stepManager.getPathsForEntry(input, currentStepEntry, method).flat();
}
return [];
}
if (step.guessable === 'no') {
return [];
}
if (step.guessable === 'yes') {
step.validEntriesForMethod(method)
.filter(e => e.name.startsWith(inputForStep))
.forEach(e => {
completionEntries.push(e);
});
break;
}
// step.guessable is 'maybe'
if (!inputForStep.startsWith('-')) {
return [];
}
step.validEntriesForMethod(method)
.filter((i) => i.name.startsWith(inputForStep))
.forEach((e) => {
completionEntries.push(e);
});
break;
}
}
if (completionEntries.length === 1) {
const singleResultArray = [];
const singleResult = completionEntries.map(e => stepManager.getPathsForEntry(input, e, method)).flat()[0];
singleResultArray.push(singleResult);
if (singleResult.endsWith('/')) {
singleResultArray.push(singleResult.slice(0, -1));
}
else {
singleResultArray.push(singleResult + '/');
}
return singleResultArray;
}
return completionEntries.map(e => stepManager.getPathsForEntry(input, e, method)).flat();
}
exports.completePath = completePath;
//# sourceMappingURL=resourceMapGenerator.js.map