adapter
v1.0.0-beta.10
Published
A TypeScript utility for writing I/O agnostic programs
Downloads
1,268
Readme
🔌 adapter
write I/O agnostic utilities
What is it?
adapter
is a tiny TypeScript helper for writing I/O agnostic utilities in a standardized way. Your code could run in a headless process, an interactive commandline tool, or in a graphical program, but either way your code remains the same.
The idea is difficult to appreciate without an example. If you're comfortable with writing your own Promises and async
/await
patterns, the following example should be fairly intuitive.
Example
Let's say that you have some code that creates a remote repository via the GitHub API. It might look roughly like this:
import axios from 'axios'; // http library
const url = 'https://api.github.com/user/repos';
const headers = { Authorization: 'token <access-token>' };
const data = { name: '<repo-name>', private: true };
await axios.post(url, data, {headers});
adapter
can be used to improve this code by injecting (or "plugging in") the means of handling I/O (input
/output
), just as Promises inject the means of handling success/failure (resolve
/reject
). Let's rewrite the above code leveraging these injections:
// github-service.ts
import axios, {AxiosResponse} from 'axios';
import {makeAdapter} from 'adapter';
type Resolve = AxiosResponse;
type Input = {
types: { 'text': string, 'yes-no': boolean },
options: {
[type: string]: { message: string }
},
keys: {
'access-token': 'text',
'repo-name': 'text',
'private': 'yes-no',
'retry': 'yes-no',
}
};
type Output = string;
export const createRepo = () => makeAdapter<Resolve, Input, Output>(
async (input, output) => {
const url = 'https://api.github.com/user/repos';
const headers = {
Authorization: 'token ' + await input(
'access-token', 'text', {message: 'Personal Access Token: '}
)
};
const data = {
name: await input(
'repo-name', 'text', {message: 'Repository Name: '}
),
private: await input(
'private', 'yes-no', {message: 'Private?: '}
)
};
output('Making API call to GitHub...');
let response;
try {
response = await axios.post(url, data, {headers});
} catch (e) {
output('Something went wrong... Status code: ' + e.response.status);
if (await input('retry', 'yes-no', {message: 'Would you like to try again? '})) {
return await createRepo().attach({input, output});
} else {
throw e;
}
}
output('Repository created!');
return response;
}
);
Now that our input and output functions are injected, we can use this service anywhere. Say we want to use it as part of a CLI application:
// cli-app.ts
import {prompt} from 'inquirer'; // inquirer is tool for getting cli input
import {createRepo} from './github-service';
const then = console.info;
const output = console.log;
const input = async (type, key, options) => {
if (type === 'text') type = 'input';
if (type === 'yes-no') type = 'confirm';
return await prompt([{
name: key,
type: type,
message: options.message
}]);
};
createRepo() // calling our service gives us an `Adapter`
.attach({then, output, input}) // here we "plug in" our attachments
.exec(); // and this executes our code
We could also utilize the same service in a web application:
// browser-app.ts
import service from './service';
service() // you can also attach handlers independently,
.then(console.info) // like so:
.catch(console.error)
.output(console.log)
.input(async (type, key, options) => {
if (type === 'yes-no') {
const yn = window.prompt(options.message + '(Y/n)');
return yn === 'y';
} else {
return window.prompt(options.message);
}
})
.exec();
Hopefully seeing these two applications side by side illustrates the advantages of using adapter
.
Installation
npm install adapter
or
yarn add adapter
Documentation
More complete documentation is coming soon!
Author
Tanner Nielsen [email protected]