├── index.js ├── cli ├── templates │ ├── gitignore │ ├── env.json │ ├── f │ │ ├── function.json │ │ └── index.js │ ├── package.json │ └── README.md ├── cli.js ├── error_log.js ├── env.js ├── commands │ ├── version.js │ ├── release.js │ ├── rollback.js │ ├── rebuild.js │ ├── logout.js │ ├── http.js │ ├── __nomethod__.js │ ├── login.js │ ├── down.js │ ├── restart.js │ ├── pkg.js │ ├── register.js │ ├── f │ │ └── create.js │ ├── info.js │ ├── init.js │ ├── get.js │ ├── user.js │ ├── up.js │ └── create.js ├── bin.js ├── scripts.js ├── credentials.js └── parser.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /index.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /cli/templates/gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | env.json 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | .stdlib 5 | -------------------------------------------------------------------------------- /cli/templates/env.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "key": "value" 4 | }, 5 | "release": { 6 | "key": "value" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /cli/cli.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const CommandLineInterface = require('cmnd').CommandLineInterface; 4 | const CLI = new CommandLineInterface(); 5 | 6 | CLI.load(__dirname, './commands'); 7 | 8 | module.exports = CLI; 9 | -------------------------------------------------------------------------------- /cli/templates/f/function.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "function", 3 | "description": "Function", 4 | "args": [ 5 | "First argument", 6 | "Second argument" 7 | ], 8 | "kwargs": { 9 | "alpha": "Keyword argument alpha", 10 | "beta": "Keyword argument beta" 11 | }, 12 | "http": { 13 | "headers": {} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cli/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "service", 3 | "version": "0.0.0", 4 | "description": "Service", 5 | "author": "Author", 6 | "main": "f/function/index.js", 7 | "dependencies": {}, 8 | "private": true, 9 | "stdlib": { 10 | "name": "username/service", 11 | "defaultFunction": "function", 12 | "timeout": 10000, 13 | "publish": false 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cli/error_log.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | module.exports = (err) => { 4 | 5 | console.log(); 6 | err.message && console.log(`${chalk.bold.red('Error: ')}${err.message}`); 7 | err.details && Object.keys(err.details).forEach(k => { 8 | let details = err.details[k]; 9 | details = details instanceof Array ? details : [details]; 10 | console.log(` ${chalk.bold(k)}`); 11 | details.forEach(d => console.log(` - ${d}`)) 12 | }); 13 | console.log(); 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /cli/env.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | module.exports = () => { 5 | 6 | let env = {}; 7 | 8 | if (fs.existsSync(path.join(process.cwd(), 'env.json'))) { 9 | let envName = 'dev'; 10 | try { 11 | env = require(path.join(process.cwd(), 'env.json'))[envName] || {}; 12 | } catch (e) { 13 | env = {}; 14 | console.warn('Warning: invalid JSON in env.json'); 15 | } 16 | env.ENV = envName; 17 | } 18 | 19 | return env; 20 | 21 | }; 22 | -------------------------------------------------------------------------------- /cli/commands/version.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | 5 | class VersionCommand extends Command { 6 | 7 | constructor() { 8 | 9 | super('version'); 10 | 11 | } 12 | 13 | help() { 14 | 15 | return { 16 | description: 'Returns currently installed version of StdLib command lines tools' 17 | }; 18 | 19 | } 20 | 21 | run(params, callback) { 22 | 23 | let pkg = require('../../package.json'); 24 | callback(null, pkg.version); 25 | 26 | } 27 | 28 | } 29 | 30 | module.exports = VersionCommand; 31 | -------------------------------------------------------------------------------- /cli/commands/release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const UpCommand = require('./up.js'); 5 | 6 | class ReleaseCommand extends Command { 7 | 8 | constructor() { 9 | 10 | super('release'); 11 | 12 | } 13 | 14 | help() { 15 | 16 | return { 17 | description: 'Pushes release of StdLib package to registry and cloud (Alias of `lib up -r`)' 18 | }; 19 | 20 | } 21 | 22 | run(params, callback) { 23 | 24 | params.flags.r = true; 25 | params.args = []; 26 | 27 | UpCommand.prototype.run.call(this, params, callback); 28 | 29 | } 30 | 31 | } 32 | 33 | module.exports = ReleaseCommand; 34 | -------------------------------------------------------------------------------- /cli/commands/rollback.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const DownCommand = require('./down.js'); 5 | 6 | class RollbackCommand extends Command { 7 | 8 | constructor() { 9 | 10 | super('rollback'); 11 | 12 | } 13 | 14 | help() { 15 | 16 | return { 17 | description: 'Rolls back (removes) release of StdLib package (alias of `lib down -r`)' 18 | }; 19 | 20 | } 21 | 22 | run(params, callback) { 23 | 24 | params.flags.r = params.args[0]; 25 | params.args = []; 26 | 27 | DownCommand.prototype.run.call(this, params, callback); 28 | 29 | } 30 | 31 | } 32 | 33 | module.exports = RollbackCommand; 34 | -------------------------------------------------------------------------------- /cli/templates/f/index.js: -------------------------------------------------------------------------------- 1 | /* Import dependencies, declare constants */ 2 | 3 | /** 4 | * Your function call 5 | * @param {Object} params Execution parameters 6 | * Members 7 | * - {Array} args Arguments passed to function 8 | * - {Object} kwargs Keyword arguments (key-value pairs) passed to function 9 | * - {String} remoteAddress The IPv4 or IPv6 address of the caller 10 | * 11 | * @param {Function} callback Execute this to end the function call 12 | * Arguments 13 | * - {Error} error The error to show if function fails 14 | * - {Any} returnValue JSON serializable (or Buffer) return value 15 | */ 16 | module.exports = (params, callback) => { 17 | 18 | callback(null, 'hello world'); 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /cli/bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | let cmd = process.argv[1]; 6 | 7 | if (cmd[cmd.length - 1] === 'f') { 8 | let name = 'username.service'; 9 | if (process.argv[2]) { 10 | name = process.argv[2]; 11 | if (name.indexOf('/') > -1) { 12 | name = name.replace(/\@(.*?)(\/|$)/gi, '[@$1]$2').split('/').join('.'); 13 | if (name.substr(0, 2) === '..') { 14 | name = name.substr(1); 15 | } 16 | } else if (name !== '.') { 17 | name = 'username.service'; 18 | } 19 | } 20 | let args = process.argv.slice(3).join(' '); 21 | console.log(`\nThe \`f\` command has been deprecated, use the following instead:\n\n\t\lib ${name} ${args}\n`); 22 | process.exit(1); 23 | } 24 | 25 | const CLI = require('./cli.js'); 26 | CLI.run(process.argv.slice(2)); 27 | -------------------------------------------------------------------------------- /cli/commands/rebuild.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const RestartCommand = require('./restart.js'); 5 | 6 | class RebuildCommand extends Command { 7 | 8 | constructor() { 9 | 10 | super('rebuild'); 11 | 12 | } 13 | 14 | help() { 15 | 16 | return { 17 | description: 'Rebuilds a service (useful for registry performance updates), alias of `lib restart -b`', 18 | args: [ 19 | 'environment' 20 | ], 21 | flags: { 22 | r: 'Rebuild a release package' 23 | }, 24 | vflags: { 25 | release: 'Rebuild a release package' 26 | } 27 | }; 28 | 29 | } 30 | 31 | run(params, callback) { 32 | 33 | params.flags.b = []; 34 | 35 | RestartCommand.prototype.run.call(this, params, callback); 36 | 37 | } 38 | 39 | } 40 | 41 | module.exports = RebuildCommand; 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lib.cli", 3 | "version": "2.0.1", 4 | "description": "StdLib: Standard Library for Microservices Command Line Tools", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "engines": { 10 | "node" : ">=6.0.0" 11 | }, 12 | "keywords": [ 13 | "lib", 14 | "serverless", 15 | "microservice", 16 | "stdlib", 17 | "stdlib.com", 18 | "standard", 19 | "library", 20 | "node", 21 | "ruby", 22 | "python" 23 | ], 24 | "author": "Keith Horwood", 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/stdlib/lib.git" 28 | }, 29 | "license": "MIT", 30 | "bin": { 31 | "stdlib": "cli/bin.js", 32 | "lib": "cli/bin.js", 33 | "f": "cli/bin.js" 34 | }, 35 | "dependencies": { 36 | "api-res": "^0.0.7", 37 | "async": "^2.0.1", 38 | "chalk": "^1.1.3", 39 | "cmnd": "~0.2.0", 40 | "inquirer": "~0.11.3", 41 | "tar-stream": "^1.5.2", 42 | "lib": "~2.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cli/commands/logout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | 7 | const async = require('async'); 8 | 9 | class LogoutCommand extends Command { 10 | 11 | constructor() { 12 | 13 | super('logout'); 14 | 15 | } 16 | 17 | help() { 18 | 19 | return { 20 | description: 'Logs out of StdLib in this workspace' 21 | }; 22 | 23 | } 24 | 25 | run(params, callback) { 26 | 27 | let host = params.flags.h ? params.flags.h[0] : 'https://api.polybit.com'; 28 | let port = params.flags.p && params.flags.p[0]; 29 | 30 | let resource = new APIResource(host, port); 31 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 32 | 33 | resource.request('v1/access_tokens').destroy(null, {}, (err, response) => { 34 | 35 | if (err) { 36 | return callback(err); 37 | } 38 | 39 | Credentials.write('ACCESS_TOKEN', ''); 40 | return callback(null, 'Logged out successfully'); 41 | 42 | }); 43 | 44 | } 45 | 46 | } 47 | 48 | module.exports = LogoutCommand; 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 - 2017 Polybit Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /cli/commands/http.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const path = require('path'); 5 | 6 | const parser = require('../parser.js'); 7 | const scripts = require('../scripts.js'); 8 | 9 | class HTTPCommand extends Command { 10 | 11 | constructor() { 12 | 13 | super('http'); 14 | 15 | } 16 | 17 | help() { 18 | 19 | return { 20 | description: 'Creates HTTP Server for Current Service', 21 | flags: {p: 'Port (Default 8170)'}, 22 | vflags: {port: 'Port (Default 8170)'} 23 | }; 24 | 25 | } 26 | 27 | run(params, callback) { 28 | 29 | let port = (params.flags.p || params.vflags.port || [])[0] || 8170; 30 | let offline = !!(params.flags.o || params.vflags.offline); 31 | let pkg = {}; 32 | 33 | try { 34 | pkg = require(path.join(process.cwd(), 'package.json')); 35 | } catch (e) { 36 | throw new Error('Invalid package.json in this directory'); 37 | return true; 38 | } 39 | 40 | scripts.run(pkg, '+http'); 41 | 42 | if (!offline) { 43 | parser.check(err => parser.createServer(pkg, port, !!err)); 44 | } else { 45 | parser.createServer(pkg, port, offline); 46 | } 47 | 48 | } 49 | 50 | } 51 | 52 | module.exports = HTTPCommand; 53 | -------------------------------------------------------------------------------- /cli/commands/__nomethod__.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const lib = require('lib'); 5 | const path = require('path'); 6 | 7 | const env = require('../env.js'); 8 | 9 | class __nomethod__Command extends Command { 10 | 11 | constructor() { 12 | 13 | super('*'); 14 | 15 | } 16 | 17 | help() { 18 | 19 | return { 20 | description: 'Runs a StdLib Function (requires a period)', 21 | args: [ 22 | 'all arguments converted to params.args' 23 | ], 24 | flags: { 25 | f: 'Specify a file to send (overrides args and kwargs)' 26 | }, 27 | vflags: { 28 | '*': 'all verbose flagss converted to params.kwargs' 29 | } 30 | }; 31 | 32 | } 33 | 34 | run(params, callback) { 35 | 36 | if (params.name.indexOf('.') === -1) { 37 | if (params.name.indexOf('/') > -1) { 38 | return callback(new Error(`Deprecated service path usage, please try \`lib ${params.name.split('/').join('.')}\` instead`)); 39 | } 40 | return callback(new Error(`Command "${params.name}" does not exist.`)); 41 | } 42 | 43 | let args = params.args.slice(); 44 | let kwargs = Object.keys(params.vflags).reduce((kwargs, key) => { 45 | kwargs[key] = params.vflags[key].join(' '); 46 | return kwargs 47 | }, {}); 48 | 49 | let cb = (err, result) => { 50 | 51 | if (err) { 52 | return callback(err); 53 | } 54 | 55 | if (result instanceof Buffer) { 56 | console.log(result.toString('binary')); 57 | } else if (typeof result === 'object') { 58 | console.log(JSON.stringify(result, null, 2)); 59 | } else { 60 | console.log(result); 61 | } 62 | 63 | }; 64 | 65 | try { 66 | process.env = env(); 67 | lib[params.name](...args, kwargs, cb); 68 | } catch(e) { 69 | return callback(e); 70 | } 71 | 72 | } 73 | 74 | } 75 | 76 | module.exports = __nomethod__Command; 77 | -------------------------------------------------------------------------------- /cli/scripts.js: -------------------------------------------------------------------------------- 1 | const spawnSync = require('child_process').spawnSync; 2 | const spawn = require('child_process').spawn; 3 | 4 | module.exports = { 5 | 6 | run: (pkg, type, callback) => { 7 | 8 | callback = callback || function() {}; 9 | 10 | const PATH = process.env.PATH; // cache path 11 | const BACKGROUND = type && type[0] === '+'; 12 | let bgproc = []; 13 | 14 | let scripts = pkg && pkg.stdlib && pkg.stdlib.scripts && pkg.stdlib.scripts[type]; 15 | 16 | if (!scripts) { 17 | return callback(); 18 | } 19 | 20 | let npmPathCommand = spawnSync('npm', ['bin']); 21 | let npmPath = npmPathCommand.stdout.toString().trim(); 22 | process.env.PATH = npmPath + ':' + process.env.PATH; 23 | 24 | let cmds = scripts instanceof Array ? scripts : [scripts]; 25 | for (let i = 0; i < cmds.length; i++) { 26 | let cmd = cmds[i].split(' '); 27 | if (!cmd.length) { 28 | continue; 29 | } 30 | if (BACKGROUND) { 31 | let n = i; 32 | let command = spawn(cmd[0], cmd.slice(1), {stdio: [0, null, null]}); 33 | command.stdout.on('data', data => { 34 | data = (data || '').toString(); 35 | data = data.split('\n').map(d => `[${type}${n ? ' ' + n : ''}] ${d}`).join('\n'); 36 | process.stdout.write(data + '\n'); 37 | }); 38 | command.stderr.on('data', data => { 39 | data = (data || '').toString(); 40 | data = data.split('\n').map(d => `[${type}${n ? ' ' + n : ''}] ${d}`).join('\n'); 41 | process.stdout.write(data + '\n'); 42 | }); 43 | bgproc.push(command); 44 | } else { 45 | let command = spawnSync(cmd[0], cmd.slice(1), {stdio: [0, 1, 2]}); 46 | if (command.status !== 0) { 47 | process.env.PATH = PATH; 48 | return callback(new Error(`Error running "${type}" script (${i})`)); 49 | } 50 | } 51 | } 52 | 53 | bgproc.length && process.on('exit', () => bgproc.forEach(proc => proc.kill())); 54 | 55 | process.env.PATH = PATH; 56 | return callback(); 57 | 58 | } 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /cli/commands/login.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | const errorLog = require('../error_log.js'); 7 | 8 | const inquirer = require('inquirer'); 9 | const async = require('async'); 10 | 11 | class PolyLoginCommand extends Command { 12 | 13 | constructor() { 14 | 15 | super('login'); 16 | 17 | } 18 | 19 | help() { 20 | 21 | return { 22 | description: 'Logs in to StdLib in this directory' 23 | }; 24 | 25 | } 26 | 27 | run(params, callback) { 28 | 29 | let host = params.flags.h ? params.flags.h[0] : 'https://api.polybit.com'; 30 | let port = params.flags.p && params.flags.p[0]; 31 | 32 | let email = params.vflags.email || params.vflags.email || [] 33 | email = email[0]; 34 | let password = params.vflags.password || params.vflags.password || []; 35 | password = password[0]; 36 | 37 | let questions = []; 38 | 39 | email || questions.push({ 40 | name: 'email', 41 | type: 'input', 42 | default: '', 43 | message: 'E-mail', 44 | }); 45 | 46 | password || questions.push({ 47 | name: 'password', 48 | type: 'password', 49 | message: 'Password', 50 | }); 51 | 52 | let loopCb = (err) => { 53 | 54 | err && errorLog(err); 55 | 56 | inquirer.prompt(questions, (promptResult) => { 57 | 58 | email = email || promptResult.email; 59 | password = password || promptResult.password; 60 | 61 | let resource = new APIResource(host, port); 62 | 63 | resource.request('v1/access_tokens').create({}, {grant_type: 'password', username: email, password: password}, (err, response) => { 64 | 65 | if (err) { 66 | return loopCb(err); 67 | } 68 | 69 | Credentials.write('ACCESS_TOKEN', response.data[0].access_token); 70 | return callback(null, 'Logged in successfully'); 71 | 72 | }); 73 | 74 | }); 75 | 76 | }; 77 | 78 | loopCb(); 79 | 80 | } 81 | 82 | } 83 | 84 | module.exports = PolyLoginCommand; 85 | -------------------------------------------------------------------------------- /cli/credentials.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const FILENAME = '.stdlib'; 6 | 7 | function findPath(maxDepth) { 8 | 9 | maxDepth = parseInt(maxDepth) || 0; 10 | 11 | let cwd = process.cwd(); 12 | let directories = cwd.split(path.sep); 13 | let stdlibPath = ''; 14 | 15 | for (let i = directories.length; i > 0; i--) { 16 | let relpath = path.join(directories.slice(0, i).join(path.sep), FILENAME); 17 | if (fs.existsSync(relpath)) { 18 | stdlibPath = relpath; 19 | break; 20 | } 21 | if (!(--maxDepth)) { 22 | break; 23 | } 24 | } 25 | 26 | return stdlibPath; 27 | 28 | } 29 | 30 | function readCredentials() { 31 | 32 | let cred = ''; 33 | let stdlibPath = findPath(); 34 | 35 | if (!stdlibPath) { 36 | throw new Error(`Please initialize stdlib in directory tree`); 37 | } 38 | 39 | cred = fs.readFileSync(stdlibPath).toString(); 40 | 41 | return cred 42 | .split('\n') 43 | .filter(v => v) 44 | .map(l => l.split('=')) 45 | .reduce((p, c) => { return (p[c[0]] = c[1]), p; }, {}) 46 | 47 | } 48 | 49 | function writeCredentials(obj, pathname) { 50 | 51 | let stdlibPath = pathname ? path.join(pathname, FILENAME) : findPath(); 52 | 53 | if (!stdlibPath) { 54 | throw new Error(`Please initialize stdlib in directory tree`); 55 | } 56 | 57 | let str = Object.keys(obj).map(k => `${k}=${obj[k]}`).join('\n') + '\n'; 58 | fs.writeFileSync(stdlibPath, str); 59 | 60 | } 61 | 62 | module.exports = { 63 | 64 | create: () => { 65 | 66 | writeCredentials({CREATED_AT: Math.floor(new Date().valueOf() / 1000)}, process.cwd()); 67 | return true; 68 | 69 | }, 70 | 71 | location: (depth) => { 72 | 73 | let loc = findPath(depth).split(path.sep); 74 | loc.pop(); 75 | return loc.join(path.sep); 76 | 77 | }, 78 | 79 | read: (key) => { 80 | 81 | return readCredentials()[key]; 82 | 83 | }, 84 | 85 | write: (key, value) => { 86 | 87 | let cred = readCredentials(); 88 | cred[key] = value; 89 | writeCredentials(cred); 90 | return true; 91 | 92 | } 93 | 94 | }; 95 | -------------------------------------------------------------------------------- /cli/commands/down.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | 7 | const fs = require('fs'); 8 | const path = require('path'); 9 | 10 | class DownCommand extends Command { 11 | 12 | constructor() { 13 | 14 | super('down'); 15 | 16 | } 17 | 18 | help() { 19 | 20 | return { 21 | description: 'Removes StdLib package from registry and cloud environment', 22 | args: [ 23 | 'environment' 24 | ], 25 | flags: { 26 | r: 'Remove a release version (provide number)', 27 | }, 28 | vflags: { 29 | release: 'Remove a release version (provide number)', 30 | } 31 | }; 32 | 33 | } 34 | 35 | run(params, callback) { 36 | 37 | let environment = params.args[0]; 38 | let version; 39 | let release = params.flags.r || params.vflags.release; 40 | 41 | if (release && environment) { 42 | return callback(new Error('Can not remove an release with an environment')); 43 | } 44 | 45 | if (!release && !environment) { 46 | return callback(new Error('Please specify an environment')); 47 | } 48 | 49 | if (release) { 50 | version = release[0]; 51 | environment = null; 52 | } 53 | 54 | let host = 'registry.stdlib.com'; 55 | let port = 443; 56 | 57 | let hostname = (params.flags.h && params.flags.h[0]) || ''; 58 | let matches = hostname.match(/^(https?:\/\/)?(.*?)(:\d+)?$/); 59 | 60 | if (hostname && matches) { 61 | host = matches[2]; 62 | port = parseInt((matches[3] || '').substr(1) || (hostname.indexOf('https') === 0 ? 443 : 80)); 63 | } 64 | 65 | let pkg; 66 | 67 | try { 68 | pkg = require(path.join(process.cwd(), 'package.json')); 69 | } catch(e) { 70 | return callback(new Error('Invalid package.json')); 71 | } 72 | 73 | if (!pkg.stdlib) { 74 | return callback(new Error('No stdlib information set in "package.json"')); 75 | } 76 | 77 | if (!pkg.stdlib.name) { 78 | return callback(new Error('No stdlib name set in "package.json"')); 79 | } 80 | 81 | let resource = new APIResource(host, port); 82 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 83 | 84 | let endpoint = environment ? 85 | `${pkg.stdlib.name}@${environment}` : 86 | `${pkg.stdlib.name}@${version || pkg.version}`; 87 | 88 | return resource.request(endpoint).stream( 89 | 'DELETE', 90 | null, 91 | (data) => { 92 | data.length > 1 && process.stdout.write(data.toString()); 93 | }, 94 | (err, response) => { 95 | 96 | if (err) { 97 | return callback(err); 98 | } 99 | 100 | if (response[response.length - 1] === 1) { 101 | return callback(new Error('There was an error processing your request')); 102 | } else { 103 | return callback(null); 104 | } 105 | 106 | } 107 | ); 108 | 109 | } 110 | 111 | } 112 | 113 | module.exports = DownCommand; 114 | -------------------------------------------------------------------------------- /cli/commands/restart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | 7 | const fs = require('fs'); 8 | const path = require('path'); 9 | 10 | class RestartCommand extends Command { 11 | 12 | constructor() { 13 | 14 | super('restart'); 15 | 16 | } 17 | 18 | help() { 19 | 20 | return { 21 | description: 'Restarts a StdLib service (if necessary)', 22 | args: [ 23 | 'environment' 24 | ], 25 | flags: { 26 | r: 'Restart a release package', 27 | b: 'Rebuild service fully' 28 | }, 29 | vflags: { 30 | release: 'Restart a release package', 31 | build: 'Rebuild service fully' 32 | } 33 | }; 34 | 35 | } 36 | 37 | run(params, callback) { 38 | 39 | let environment = params.args[0]; 40 | let version; 41 | let release = params.flags.r || params.vflags.release; 42 | let rebuild = params.flags.b || params.vflags.build; 43 | 44 | if (release && environment) { 45 | return callback(new Error('Can not reset a release with an environment')); 46 | } 47 | 48 | if (!release && !environment) { 49 | return callback(new Error('Please specify an environment')); 50 | } 51 | 52 | if (release) { 53 | version = release[0]; 54 | environment = null; 55 | } 56 | 57 | let host = 'registry.stdlib.com'; 58 | let port = 443; 59 | 60 | let hostname = (params.flags.h && params.flags.h[0]) || ''; 61 | let matches = hostname.match(/^(https?:\/\/)?(.*?)(:\d+)?$/); 62 | 63 | if (hostname && matches) { 64 | host = matches[2]; 65 | port = parseInt((matches[3] || '').substr(1) || (hostname.indexOf('https') === 0 ? 443 : 80)); 66 | } 67 | 68 | let pkg; 69 | 70 | try { 71 | pkg = require(path.join(process.cwd(), 'package.json')); 72 | } catch(e) { 73 | return callback(new Error('Invalid package.json')); 74 | } 75 | 76 | if (!pkg.stdlib) { 77 | return callback(new Error('No stdlib information set in "package.json"')); 78 | } 79 | 80 | if (!pkg.stdlib.name) { 81 | return callback(new Error('No stdlib name set in "package.json"')); 82 | } 83 | 84 | let resource = new APIResource(host, port); 85 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 86 | 87 | let endpoint = environment ? 88 | `${pkg.stdlib.name}@${environment}` : 89 | `${pkg.stdlib.name}@${version || pkg.version}`; 90 | 91 | return resource.request(`${endpoint}/${rebuild ? 'rebuild' : 'restart'}`).stream( 92 | 'PUT', 93 | null, 94 | (data) => { 95 | data.length > 1 && process.stdout.write(data.toString()); 96 | }, 97 | (err, response) => { 98 | 99 | if (err) { 100 | return callback(err); 101 | } 102 | 103 | if (response[response.length - 1] === 1) { 104 | return callback(new Error('There was an error processing your request')); 105 | } else { 106 | return callback(null); 107 | } 108 | 109 | } 110 | ); 111 | 112 | } 113 | 114 | } 115 | 116 | module.exports = RestartCommand; 117 | -------------------------------------------------------------------------------- /cli/templates/README.md: -------------------------------------------------------------------------------- 1 | # Your stdlib service: {{username}}/{{service}} 2 | 3 | This is the README for your service. 4 | 5 | A few notes; 6 | 7 | `package.json` is NPM-compatible and contains some stdlib configuration details. 8 | `.gitignore` has also been provided for your convenience. 9 | 10 | # package.json 11 | 12 | This is a standard `package.json`. You'll notice an additional `"stdlib"` field. 13 | You can configure your service for the stdlib registry using; 14 | 15 | `name` - The name to register on stdlib, in the format of `/`. 16 | In order to compile to the registry you must have permission to compile to the 17 | provided username's account. 18 | 19 | `defaultFunction` - Execute if provided no function route (root service). 20 | If not specified, your base service route will provide a list of available 21 | functions in JSON format. 22 | 23 | `timeout` - The time in ms at which to kill service execution. Free accounts are 24 | limited to 30 seconds (30000). 25 | 26 | `publish` - Whether to publish releases (versioned) to the stdlib public 27 | directory. Packages pushed to the registry in non-release environments will 28 | never be published. 29 | 30 | # env.json 31 | 32 | Environment configuration for your service. Each top level key (i.e. 33 | `"dev"` and `"release"`) specifies their own set of key-value 34 | pairs for a specific execution environment. The keys and values specified 35 | are automatically added to the `process.env` variable in Node.js. 36 | 37 | `"dev"` is the *non-configurable* name of the local environment, but can 38 | also be used as an environment name for compilation 39 | (i.e. `$ lib up development`). 40 | 41 | `"release"` is the *non-configurable* name of the production environment when 42 | you create releases with `$ lib release`. 43 | 44 | You can add additional environments and key-value pairs, and use them for 45 | compilation with `lib up `. Note that free accounts are 46 | restricted to one compilation environment (aside from `"release"`). 47 | 48 | *We recommend against checking this file in to version control*. It will be 49 | saved with your tarball and is privately retrievable from the stdlib registry 50 | using your account credentials. It has been added to `.gitignore` by default. 51 | 52 | # f/{{func}}/function.json 53 | 54 | This is your function definition file. The following fields can be used for 55 | execution configuration of specific functions within your service. 56 | 57 | `name` - The function name. This maps to an execution route over HTTP. For 58 | example, `{{username}}/{{service}}/{{func}}` would map to the first 59 | function you've created. 60 | 61 | `description` - A brief description of the function. To provide detailed 62 | information about function execution, overwrite this README. 63 | 64 | `args` - An `Array` describing each argument as you expect them to be passed to 65 | `params.args`. 66 | 67 | `kwargs` - An `Object` describing each keyword argument as you expect them to be 68 | passed to `params.kwargs` 69 | 70 | `http` - Information to provide to function requests over HTTP. 71 | 72 | `http.headers` - HTTP headers to return in the response. Examples are 73 | `"Content-Type"` to specify file type if your function returns a `Buffer` or 74 | `"Access-Control-Allow-Origin"` to restrict browser-based function requests. 75 | 76 | # f/{{func}}/index.js 77 | 78 | The entry point to your function described in `f/{{func}}/function.json`. 79 | This is *non-configurable*. You may add as many subdirectories and supportive 80 | files as you like, but `index.js` will remain the entry point and *must* 81 | export a function to be active. 82 | -------------------------------------------------------------------------------- /cli/commands/pkg.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | const Command = require('cmnd').Command; 7 | const APIResource = require('api-res'); 8 | const Credentials = require('../credentials.js'); 9 | 10 | const chalk = require('chalk'); 11 | 12 | class PkgCommand extends Command { 13 | 14 | constructor() { 15 | 16 | super('pkg'); 17 | 18 | } 19 | 20 | help() { 21 | 22 | return { 23 | description: 'Downloads StdLib tarball (.tgz)', 24 | args: [ 25 | 'full service name' 26 | ], 27 | flags: { 28 | f: 'Force command if not in root directory', 29 | o: 'Output path for the .tgz package' 30 | }, 31 | vflags: { 32 | force: 'Force command if not in root directory', 33 | output: 'Output path for the .tgz package' 34 | } 35 | }; 36 | 37 | } 38 | 39 | run(params, callback) { 40 | 41 | let service = params.args[0] || ''; 42 | let outputPath = params.flags.o || params.vflags.output || []; 43 | outputPath = outputPath[0] || '.'; 44 | 45 | let force = params.flags.hasOwnProperty('f') || params.vflags.hasOwnProperty('force'); 46 | 47 | if (!force && !Credentials.location(1)) { 48 | console.log(); 49 | console.log(chalk.bold.red('Oops!')); 50 | console.log(); 51 | console.log(`You're trying to download a tarball,`); 52 | console.log(`But you're not in a root stdlib project directory.`); 53 | console.log(`We recommend against this.`); 54 | console.log(); 55 | console.log(`Use ${chalk.bold('lib pkg ' + service + ' --force')} to override.`); 56 | console.log(); 57 | return callback(null); 58 | } 59 | 60 | let host = 'registry.stdlib.com'; 61 | let port = 443; 62 | 63 | let hostname = (params.flags.h && params.flags.h[0]) || ''; 64 | let matches = hostname.match(/^(https?:\/\/)?(.*?)(:\d+)?$/); 65 | 66 | if (hostname && matches) { 67 | host = matches[2]; 68 | port = parseInt((matches[3] || '').substr(1) || (hostname.indexOf('https') === 0 ? 443 : 80)); 69 | } 70 | 71 | let info = params.args[0]; 72 | 73 | let resource = new APIResource(host, port); 74 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 75 | 76 | let endpoint = `${service}/package.tgz`; 77 | 78 | console.log(); 79 | console.log(`Downloading ${chalk.bold(host + '/' + endpoint)}...`); 80 | console.log(); 81 | 82 | return resource.request(endpoint).index({}, (err, response) => { 83 | 84 | if (err) { 85 | return callback(err); 86 | } 87 | 88 | let pathname = path.join(outputPath, `${service.replace(/\//g, '.')}.package.tgz`); 89 | pathname = outputPath[0] !== '/' ? path.join(process.cwd(), pathname) : pathname; 90 | 91 | let directories = pathname.split(path.sep); 92 | for (let i = 1; i < directories.length - 1; i++) { 93 | let relpath = pathname.split(path.sep).slice(0, i + 1).join(path.sep); 94 | try { 95 | !fs.existsSync(relpath) && fs.mkdirSync(relpath); 96 | } catch (e) { 97 | console.error(e); 98 | return callback(new Error(`Could not create directory ${relpath}`)); 99 | } 100 | } 101 | 102 | try { 103 | fs.writeFileSync(pathname, response); 104 | } catch (e) { 105 | console.error(e); 106 | return callback(new Error(`Could not write file ${pathname}`)); 107 | } 108 | 109 | console.log(chalk.bold.green('Success!')); 110 | console.log(); 111 | console.log(`${chalk.bold(service)} package saved to:`); 112 | console.log(` ${chalk.bold(pathname)}`); 113 | console.log(); 114 | return callback(null); 115 | 116 | }); 117 | 118 | } 119 | 120 | } 121 | 122 | module.exports = PkgCommand; 123 | -------------------------------------------------------------------------------- /cli/commands/register.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | const errorLog = require('../error_log.js'); 7 | 8 | const inquirer = require('inquirer'); 9 | const async = require('async'); 10 | 11 | class RegisterCommand extends Command { 12 | 13 | constructor() { 14 | 15 | super('register'); 16 | 17 | } 18 | 19 | help() { 20 | 21 | return { 22 | description: 'Registers a new StdLib user account' 23 | }; 24 | 25 | } 26 | 27 | run(params, callback) { 28 | 29 | let host = params.flags.h ? params.flags.h[0] : 'https://api.polybit.com'; 30 | let port = params.flags.p && params.flags.p[0]; 31 | 32 | let email = params.flags.e || params.vflags.email || []; 33 | email = email[0]; 34 | 35 | let questions = []; 36 | let previous = {}; 37 | 38 | email || questions.push({ 39 | name: 'email', 40 | type: 'input', 41 | default: '', 42 | message: 'E-mail' 43 | }); 44 | 45 | questions.push({ 46 | name: 'username', 47 | type: 'input', 48 | default: '', 49 | message: 'Desired Username' 50 | }); 51 | 52 | questions.push({ 53 | name: 'password', 54 | type: 'password', 55 | message: 'Password' 56 | }); 57 | 58 | questions.push({ 59 | name: 'repeat_password', 60 | type: 'password', 61 | message: 'Repeat Password' 62 | }); 63 | 64 | let loopCb = (err) => { 65 | 66 | if (err) { 67 | 68 | errorLog(err); 69 | 70 | if (err.details) { 71 | 72 | if (err.details.hasOwnProperty('password')) { 73 | delete previous.password; 74 | delete previous.repeat_password; 75 | } else { 76 | for (let i = questions.length - 1; i >= 0; i--) { 77 | if (questions[i].name === 'password' || questions[i].name === 'repeat_password') { 78 | questions.splice(i, 1); 79 | } 80 | } 81 | } 82 | 83 | if (err.details.hasOwnProperty('username')) { 84 | delete previous.username; 85 | } else { 86 | for (let i = questions.length - 1; i >= 0; i--) { 87 | if (questions[i].name === 'username') { 88 | questions.splice(i, 1); 89 | } 90 | } 91 | } 92 | 93 | } 94 | 95 | } 96 | 97 | inquirer.prompt(questions, (promptResult) => { 98 | 99 | email = email || promptResult.email; 100 | let password = previous.password || promptResult.password; 101 | let repeat_password = previous.repeat_password || promptResult.repeat_password; 102 | let username = previous.username || promptResult.username; 103 | 104 | previous.password = password; 105 | previous.repeat_password = repeat_password; 106 | previous.username = username; 107 | 108 | let resource = new APIResource(host, port); 109 | resource.request('v1/users').create( 110 | {}, 111 | { 112 | email: email, 113 | username: username, 114 | password: password, 115 | repeat_password: repeat_password 116 | }, 117 | (err, response) => { 118 | 119 | if (err) { 120 | return loopCb(err); 121 | } 122 | 123 | params.flags.e = [email]; 124 | params.vflags.email = [email]; 125 | params.flags.p = [password]; 126 | params.vflags.password = [password]; 127 | 128 | require('./login.js').prototype.run(params, callback); 129 | 130 | } 131 | ); 132 | 133 | }); 134 | 135 | }; 136 | 137 | loopCb(); 138 | 139 | } 140 | 141 | } 142 | 143 | module.exports = RegisterCommand; 144 | -------------------------------------------------------------------------------- /cli/commands/f/create.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const Credentials = require('../../credentials.js'); 5 | 6 | const inquirer = require('inquirer'); 7 | 8 | const fs = require('fs'); 9 | const path = require('path'); 10 | const chalk = require('chalk'); 11 | 12 | class FCreateCommand extends Command { 13 | 14 | constructor() { 15 | 16 | super('f', 'create'); 17 | 18 | } 19 | 20 | help() { 21 | 22 | return { 23 | description: 'Creates a new function for a (local) service', 24 | args: [ 25 | 'function name' 26 | ], 27 | flags: { 28 | 'w': 'Overwrite existing function' 29 | }, 30 | vflags: { 31 | 'write-over': 'Overwrite existing function' 32 | } 33 | }; 34 | 35 | } 36 | 37 | run(params, callback) { 38 | 39 | let functionName = params.args[0]; 40 | 41 | let write = params.flags.hasOwnProperty('w') || params.vflags.hasOwnProperty('write-over'); 42 | 43 | if (!fs.existsSync('package.json') || !Credentials.location()) { 44 | console.log(); 45 | console.log(chalk.bold.red('Oops!')); 46 | console.log(); 47 | console.log(`You're trying to create a new function in development,`); 48 | console.log(`But you're either not in a stdlib workspace,`); 49 | console.log(` or not in a service directory with a "package.json"`); 50 | console.log(); 51 | return callback(null); 52 | } 53 | 54 | let questions = []; 55 | 56 | functionName || questions.push({ 57 | name: 'functionName', 58 | type: 'input', 59 | default: '', 60 | message: 'Function Name' 61 | }); 62 | 63 | inquirer.prompt(questions, (promptResult) => { 64 | 65 | functionName = functionName || promptResult.functionName; 66 | 67 | let fPath = path.join(process.cwd(), 'f'); 68 | let functionPath; 69 | 70 | !fs.existsSync(fPath) && fs.mkdirSync(fPath); 71 | 72 | let directories = functionName.split('/'); 73 | 74 | for (let i = 0; i < directories.length; i++) { 75 | let relpath = path.join.apply(path, [fPath].concat(directories.slice(0, i + 1))); 76 | if (i === directories.length - 1 && fs.existsSync(relpath)) { 77 | if (!write) { 78 | console.log(); 79 | console.log(chalk.bold.red('Oops!')); 80 | console.log(); 81 | console.log(`The function you're trying to create already seems to exist:`); 82 | console.log(` ${chalk.bold(pathname)}`); 83 | console.log(); 84 | console.log(`Try removing the existing directory first.`); 85 | console.log(); 86 | console.log(`Use ${chalk.bold('lib f:create ' + functionName + ' --write-over')} to override.`); 87 | console.log(); 88 | return callback(null); 89 | } 90 | } 91 | !fs.existsSync(relpath) && fs.mkdirSync(relpath); 92 | functionPath = relpath; 93 | } 94 | 95 | let json = { 96 | func: require(path.join(__dirname, '../../templates/f/function.json')) 97 | }; 98 | 99 | json.func.name = functionName; 100 | 101 | fs.writeFileSync( 102 | path.join(functionPath, 'function.json'), 103 | JSON.stringify(json.func, null, 2) 104 | ); 105 | 106 | let files = { 107 | func: { 108 | copy: { 109 | 'index.js': fs.readFileSync(path.join(__dirname, '../../templates/f/index.js')), 110 | } 111 | } 112 | }; 113 | 114 | Object.keys(files.func.copy).forEach(filename => { 115 | fs.writeFileSync(path.join(functionPath, filename), files.func.copy[filename]) 116 | }); 117 | 118 | console.log(); 119 | console.log(chalk.bold.green('Success!')); 120 | console.log(); 121 | console.log(`Function ${chalk.bold(functionName)} created at:`); 122 | console.log(` ${chalk.bold(functionPath)}`); 123 | console.log(); 124 | return callback(null); 125 | 126 | }); 127 | 128 | } 129 | 130 | } 131 | 132 | module.exports = FCreateCommand; 133 | -------------------------------------------------------------------------------- /cli/commands/info.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | 7 | const chalk = require('chalk'); 8 | 9 | let formatDigits = (num, figs) => { 10 | 11 | num = (parseInt(Math.max(num, 0)) || 0).toString(); 12 | let zeroes = Math.max(figs - num.length, 0); 13 | return `${Array(zeroes + 1).join('0')}${num}`; 14 | 15 | }; 16 | 17 | let formatDate = function(str) { 18 | 19 | let date = new Date(str); 20 | let months = 'January February March April May June July August September October November December'.split(' '); 21 | let ends = ['th', 'st', 'nd', 'rd', 'th']; 22 | 23 | let y = chalk.bold(date.getFullYear()); 24 | let m = chalk.bold(months[date.getMonth()]); 25 | let d = chalk.bold(date.getDate()); 26 | let e = chalk.bold(ends[d] || 'th'); 27 | let hh = chalk.bold(formatDigits(date.getHours(), 2)); 28 | let mm = chalk.bold(formatDigits(date.getMinutes(), 2)); 29 | let ss = chalk.bold(formatDigits(date.getSeconds(), 2)); 30 | let ms = chalk.bold(formatDigits(date.valueOf() % 1000, 3)); 31 | 32 | return `${m} ${d}${e}, ${y} at ${hh}:${mm}:${ss}.${ms}`; 33 | 34 | }; 35 | 36 | class InfoCommand extends Command { 37 | 38 | constructor() { 39 | 40 | super('info'); 41 | 42 | } 43 | 44 | help() { 45 | 46 | return { 47 | description: 'Retrieves information about a user or package', 48 | args: [ 49 | 'username | full service name' 50 | ] 51 | }; 52 | 53 | } 54 | 55 | run(params, callback) { 56 | 57 | let service = params.args[0] || ''; 58 | 59 | let host = 'registry.stdlib.com'; 60 | let port = 443; 61 | 62 | let hostname = (params.flags.h && params.flags.h[0]) || ''; 63 | let matches = hostname.match(/^(https?:\/\/)?(.*?)(:\d+)?$/); 64 | 65 | if (hostname && matches) { 66 | host = matches[2]; 67 | port = parseInt((matches[3] || '').substr(1) || (hostname.indexOf('https') === 0 ? 443 : 80)); 68 | } 69 | 70 | let info = params.args[0]; 71 | 72 | let resource = new APIResource(host, port); 73 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 74 | 75 | return resource.request(service).index({}, (err, response) => { 76 | 77 | if (err) { 78 | return callback(err); 79 | } 80 | 81 | if (response instanceof Array) { 82 | // list of packages 83 | let list = response; 84 | 85 | console.log(); 86 | console.log(`User ${chalk.bold(service)} has ${chalk.bold(list.length)} service packages.`); 87 | console.log(); 88 | 89 | list.forEach(pkg => { 90 | 91 | console.log(`Package: ${chalk.bold(pkg.identifier)} ` + (pkg.publish ? chalk.green('(Published)') : chalk.red('(Unpublished)'))); 92 | console.log(`Service: ${chalk.bold(pkg.url)}`); 93 | console.log(`This package was created on ${formatDate(pkg.created)}.`); 94 | console.log(); 95 | 96 | }); 97 | 98 | } else { 99 | // specific package 100 | let pkg = response; 101 | 102 | console.log(); 103 | console.log(`Package: ${chalk.bold(pkg.identifier)} ` + (pkg.publish ? chalk.green('(Published)') : chalk.red('(Unpublished)'))); 104 | console.log(`Service: ${chalk.bold(pkg.url)}`); 105 | console.log(`This package was created on ${formatDate(pkg.created)}.`); 106 | console.log(`This package has ${chalk.bold(pkg.functionCount)} functions.`); 107 | console.log(); 108 | 109 | Object.keys(pkg.functions).forEach((name) => { 110 | let fn = pkg.functions[name]; 111 | console.log([ 112 | ` ${chalk.bold([pkg.identifier, fn.name].join('/'))}`, 113 | ` ${fn.description}`, 114 | ``, 115 | (fn.args || []).map((arg, i) => ` [${i}] ${arg}`).join('\n'), 116 | Object.keys(fn.kwargs || {}).map(k => ` {${k}} ${fn.kwargs[k]}`).join('\n'), 117 | `` 118 | ].join('\n')); 119 | }); 120 | 121 | } 122 | 123 | return callback(null); 124 | 125 | }); 126 | 127 | } 128 | 129 | } 130 | 131 | module.exports = InfoCommand; 132 | -------------------------------------------------------------------------------- /cli/parser.js: -------------------------------------------------------------------------------- 1 | // Uses stdlib reflect service to parse args, kwargs 2 | const https = require('https'); 3 | const http = require('http'); 4 | const path = require('path'); 5 | const url = require('url'); 6 | const fs = require('fs'); 7 | 8 | const chalk = require('chalk'); 9 | const lib = require('lib'); 10 | 11 | const env = require('./env.js'); 12 | 13 | module.exports = { 14 | createServer: function createServer(pkg, port, offline) { 15 | 16 | offline = !!offline; 17 | 18 | let serviceName = (pkg.stdlib && pkg.stdlib.name) || pkg.name || ''; 19 | process.env = env(); 20 | 21 | if (offline) { 22 | console.warn( 23 | chalk.bold.yellow('Info:') + 24 | ' Operating in offline mode, kwargs can only be processed via query parameters' 25 | ); 26 | } 27 | 28 | let server = http.createServer((req, res) => { 29 | 30 | let urlParts = url.parse(req.url, true); 31 | let pathname = req.url[0] !== '/' ? `/${req.url}` : req.url; 32 | pathname = pathname.split('?')[0]; 33 | let libname = pathname.split('/').join('.'); 34 | 35 | let response = (err, params) => { 36 | 37 | console.log(`[function: ${libname}] ${JSON.stringify({args: params.args, kwargs: params.kwargs})}`); 38 | 39 | if (err) { 40 | res.writeHead(400, {'Content-Type': 'text/plain'}); 41 | return res.end(`Error: ${err.message}`); 42 | } 43 | 44 | lib[`${libname}`](...params.args, params.kwargs, (err, result, headers) => { 45 | 46 | if (err) { 47 | res.writeHead(400, {'Content-Type': 'text/plain'}); 48 | res.end(`Error: ${err.message}`); 49 | } else { 50 | res.writeHead(200, headers); 51 | if (result instanceof Buffer || typeof result !== 'object') { 52 | res.end(result); 53 | } else { 54 | try { 55 | result = JSON.stringify(result); 56 | } catch (e) { 57 | result = '{}'; 58 | } 59 | res.end(result); 60 | } 61 | } 62 | 63 | }); 64 | 65 | }; 66 | 67 | if (offline) { 68 | response(null, {args: [], kwargs: urlParts.query, remoteAddress: '::1'}); 69 | } else { 70 | this.reflect(req, response); 71 | } 72 | 73 | }); 74 | 75 | server.listen(port); 76 | console.log(); 77 | console.log(`HTTP development server listening for service ${chalk.bold.green(serviceName)} on port ${chalk.bold(port)}`); 78 | console.log(); 79 | 80 | }, 81 | check: function check(callback) { 82 | 83 | this.send(null, null, null, null, callback); 84 | 85 | }, 86 | send: function send(search, method, headers, buffer, callback) { 87 | 88 | search = search || ''; 89 | method = method || 'GET'; 90 | headers = headers || {}; 91 | buffer = buffer || new Buffer(0); 92 | delete headers['accept-encoding']; // no gzip 93 | delete headers['host']; // no host 94 | 95 | let libreq = https.request({ 96 | hostname: 'f.stdlib.com', 97 | port: 443, 98 | path: `/stdlib/reflect${search}`, 99 | method: method, 100 | headers: headers 101 | }, (libres) => { 102 | 103 | let lbuffers = []; 104 | 105 | libres.on('data', chunk => lbuffers.push(chunk)); 106 | libres.on('end', () => { 107 | 108 | let lbuffer = Buffer.concat(lbuffers); 109 | let json = {}; 110 | 111 | try { 112 | json = JSON.parse(lbuffer.toString()); 113 | } catch (e) { 114 | return callback(new Error('Unexpected stdlib reflect response: ' + lbuffer.toString())); 115 | } 116 | 117 | callback(null, json); 118 | 119 | }); 120 | 121 | }); 122 | 123 | libreq.on('error', (err) => callback(new Error('Could not connect to stdlib reflect'))); 124 | libreq.write(buffer) 125 | libreq.end(); 126 | 127 | }, 128 | reflect: function reflect(req, callback) { 129 | 130 | let buffers = []; 131 | let search = url.parse(req.url, true).search; 132 | 133 | req.on('data', chunk => buffers.push(chunk)); 134 | req.on('end', () => { 135 | 136 | let buffer = Buffer.concat(buffers); 137 | this.send(search, req.method, req.headers, buffer, callback); 138 | 139 | }); 140 | 141 | } 142 | }; 143 | -------------------------------------------------------------------------------- /cli/commands/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | 7 | const inquirer = require('inquirer'); 8 | const async = require('async'); 9 | const chalk = require('chalk'); 10 | 11 | class InitCommand extends Command { 12 | 13 | constructor() { 14 | 15 | super('init'); 16 | 17 | } 18 | 19 | help() { 20 | 21 | return { 22 | description: 'Initializes StdLib workspace', 23 | args: [ 24 | 'environment' 25 | ], 26 | flags: { 27 | f: 'Force command to overwrite existing workspace', 28 | n: 'No login - don\'t require an internet connection' 29 | }, 30 | vflags: { 31 | 'force': 'Force command to overwrite existing workspace', 32 | 'no-login': 'No login - don\'t require an internet connection' 33 | } 34 | }; 35 | 36 | } 37 | 38 | run(params, callback) { 39 | 40 | let host = params.flags.h ? params.flags.h[0] : 'https://api.polybit.com'; 41 | let port = params.flags.p && params.flags.p[0]; 42 | 43 | let force = params.flags.hasOwnProperty('f') || params.vflags.hasOwnProperty('force'); 44 | let nologin = params.flags.hasOwnProperty('n') || params.vflags.hasOwnProperty('no-login'); 45 | 46 | let cloc = Credentials.location(); 47 | 48 | if (!force && cloc && (cloc !== process.cwd())) { 49 | console.log(); 50 | console.log(chalk.bold.red('Oops!')); 51 | console.log(); 52 | console.log(`A stdlib workspace already exists above this one.`); 53 | console.log(`We recommend you do not initialize another.`); 54 | console.log(`The path of the stdlib workspace is:`) 55 | console.log(` ${chalk.bold(cloc)}`); 56 | console.log(); 57 | console.log(`Use ${chalk.bold('lib init --force')} to override.`); 58 | console.log(); 59 | return callback(null); 60 | } 61 | 62 | Credentials.create(); 63 | 64 | let cb = (err) => { 65 | if (err) { 66 | return callback(err); 67 | } 68 | console.log(); 69 | console.log(chalk.bold.green(`Congratulations!`)); 70 | console.log(`Your stdlib development environment has been initialized.`); 71 | console.log(); 72 | console.log(`Use ${chalk.bold('lib create ')} to create a new (local) service package.`); 73 | console.log(`or type ${chalk.bold('lib get ')} to download an existing service package.`); 74 | console.log() 75 | console.log(`Additionally, use ${chalk.bold('lib help')} to see more commands.`) 76 | console.log(); 77 | console.log(chalk.bold('Happy building! :)')); 78 | console.log(); 79 | callback(null); 80 | }; 81 | 82 | if (nologin) { 83 | return cb(); 84 | } 85 | 86 | console.log(); 87 | console.log(chalk.bold.green('Welcome to stdlib! :)')) 88 | console.log(); 89 | console.log(`To use the ${chalk.bold('stdlib')} registry, you must have a registered Polybit account.`); 90 | console.log(`It will allow you to push your services to the cloud and manage environments.`); 91 | console.log(`It\'s ${chalk.bold.underline.green('free')} to create an account. Let's get started!`); 92 | console.log(); 93 | console.log(`Please enter your e-mail to login or register.`); 94 | console.log(); 95 | 96 | let questions = []; 97 | 98 | questions.push({ 99 | name: 'email', 100 | type: 'input', 101 | default: '', 102 | message: 'E-mail' 103 | }); 104 | 105 | inquirer.prompt(questions, (promptResult) => { 106 | 107 | let email = promptResult.email; 108 | 109 | let resource = new APIResource(host, port); 110 | resource.request('v1/user_exists').index({email: email}, (err, response) => { 111 | 112 | if (err) { 113 | return callback(err); 114 | } 115 | 116 | params.flags.e = [email]; 117 | params.vflags.email = [email]; 118 | 119 | if (!response.data.length) { 120 | console.log(); 121 | console.log(`${chalk.bold.green('Welcome!')} It appears you do not yet have an account.`); 122 | console.log('Please create a username and password.'); 123 | console.log(); 124 | require('./register.js').prototype.run(params, cb); 125 | } else { 126 | console.log(); 127 | console.log(`Welcome back, ${chalk.bold.green(response.data[0].username)}!`); 128 | console.log('Please enter your password.'); 129 | console.log(); 130 | require('./login.js').prototype.run(params, cb); 131 | } 132 | 133 | }); 134 | 135 | }); 136 | 137 | } 138 | 139 | } 140 | 141 | module.exports = InitCommand; 142 | -------------------------------------------------------------------------------- /cli/commands/get.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const child_process = require('child_process'); 6 | 7 | const Command = require('cmnd').Command; 8 | const APIResource = require('api-res'); 9 | const Credentials = require('../credentials.js'); 10 | 11 | const chalk = require('chalk'); 12 | 13 | class GetCommand extends Command { 14 | 15 | constructor() { 16 | 17 | super('get'); 18 | 19 | } 20 | 21 | help() { 22 | 23 | return { 24 | description: 'Retrieves and extracts StdLib package', 25 | args: [ 26 | 'full service name' 27 | ], 28 | flags: { 29 | f: 'Force command if not in root directory', 30 | w: 'Write over - overwrite the target directory contents' 31 | }, 32 | vflags: { 33 | 'force': 'Force command if not in root directory', 34 | 'write-over': 'Write over - overwrite the target directory contents' 35 | } 36 | }; 37 | 38 | } 39 | 40 | run(params, callback) { 41 | 42 | let service = params.args[0] || ''; 43 | let outputPath = params.flags.o || params.vflags.output || []; 44 | outputPath = outputPath[0] || '.'; 45 | 46 | let force = params.flags.hasOwnProperty('f') || params.vflags.hasOwnProperty('force'); 47 | let write = params.flags.hasOwnProperty('w') || params.vflags.hasOwnProperty('write-over'); 48 | 49 | let pathname = path.join(outputPath, service); 50 | pathname = outputPath[0] !== '/' ? path.join(process.cwd(), pathname) : pathname; 51 | 52 | if (!service) { 53 | console.log(); 54 | console.log(chalk.bold.red('Oops!')); 55 | console.log(); 56 | console.log(`Please specify a service name`); 57 | console.log(); 58 | return callback(null); 59 | } 60 | 61 | if (!force && !Credentials.location(1)) { 62 | console.log(); 63 | console.log(chalk.bold.red('Oops!')); 64 | console.log(); 65 | console.log(`You're trying to retrieve a package,`); 66 | console.log(`But you're not in a root stdlib project directory.`); 67 | console.log(`We recommend against this.`); 68 | console.log(); 69 | console.log(`Use ${chalk.bold('lib get ' + service + ' --force')} to override.`); 70 | console.log(); 71 | return callback(null); 72 | } 73 | 74 | if (!write && fs.existsSync(pathname)) { 75 | console.log(); 76 | console.log(chalk.bold.red('Oops!')); 77 | console.log(); 78 | console.log(`The directory you're retrieving to already exists:`); 79 | console.log(` ${chalk.bold(pathname)}`); 80 | console.log(); 81 | console.log(`Try removing the existing directory first.`); 82 | console.log(); 83 | console.log(`Use ${chalk.bold('lib get ' + service + ' --write-over')} to override.`); 84 | console.log(); 85 | return callback(null); 86 | } 87 | 88 | let host = 'registry.stdlib.com'; 89 | let port = 443; 90 | 91 | let hostname = (params.flags.h && params.flags.h[0]) || ''; 92 | let matches = hostname.match(/^(https?:\/\/)?(.*?)(:\d+)?$/); 93 | 94 | if (hostname && matches) { 95 | host = matches[2]; 96 | port = parseInt((matches[3] || '').substr(1) || (hostname.indexOf('https') === 0 ? 443 : 80)); 97 | } 98 | 99 | let info = params.args[0]; 100 | 101 | let resource = new APIResource(host, port); 102 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 103 | 104 | let endpoint = `${service}/package.tgz`; 105 | 106 | console.log(); 107 | console.log(`Retrieving ${chalk.bold(host + '/' + endpoint)}...`); 108 | console.log(); 109 | 110 | return resource.request(endpoint).index({}, (err, response) => { 111 | 112 | if (err) { 113 | return callback(err); 114 | } 115 | 116 | let directories = pathname.split(path.sep); 117 | for (let i = 1; i < directories.length; i++) { 118 | let relpath = pathname.split(path.sep).slice(0, i + 1).join(path.sep); 119 | try { 120 | !fs.existsSync(relpath) && fs.mkdirSync(relpath); 121 | } catch (e) { 122 | console.error(e); 123 | return callback(new Error(`Could not create directory ${relpath}`)); 124 | } 125 | } 126 | 127 | let tmpPath = `/tmp/${service.replace(/\//g, '.')}.tgz`; 128 | try { 129 | fs.writeFileSync(tmpPath, response); 130 | } catch (e) { 131 | console.error(e); 132 | return callback(new Error(`Could not write temporary file ${tmpPath}`)); 133 | } 134 | 135 | child_process.exec(`tar -xzf ${tmpPath} -C ${pathname}`, (err) => { 136 | 137 | // cleanup 138 | fs.unlinkSync(tmpPath); 139 | 140 | if (err) { 141 | return callback(`Could not extract from package`); 142 | } 143 | 144 | console.log(chalk.bold.green('Success!')); 145 | console.log(); 146 | console.log(`${chalk.bold(service)} package retrieved to:`); 147 | console.log(` ${chalk.bold(pathname)}`); 148 | console.log(); 149 | return callback(null); 150 | 151 | }); 152 | 153 | }); 154 | 155 | } 156 | 157 | } 158 | 159 | module.exports = GetCommand; 160 | -------------------------------------------------------------------------------- /cli/commands/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | 7 | const chalk = require('chalk'); 8 | const inquirer = require('inquirer'); 9 | 10 | let formatDigits = (num, figs) => { 11 | 12 | num = (parseInt(Math.max(num, 0)) || 0).toString(); 13 | let zeroes = Math.max(figs - num.length, 0); 14 | return `${Array(zeroes + 1).join('0')}${num}`; 15 | 16 | }; 17 | 18 | let formatDate = function(str) { 19 | 20 | let date = new Date(str); 21 | let months = 'January February March April May June July August September October November December'.split(' '); 22 | let ends = ['th', 'st', 'nd', 'rd', 'th']; 23 | 24 | let y = chalk.bold(date.getFullYear()); 25 | let m = chalk.bold(months[date.getMonth()]); 26 | let d = chalk.bold(date.getDate()); 27 | let e = chalk.bold(ends[d] || 'th'); 28 | let hh = chalk.bold(formatDigits(date.getHours(), 2)); 29 | let mm = chalk.bold(formatDigits(date.getMinutes(), 2)); 30 | let ss = chalk.bold(formatDigits(date.getSeconds(), 2)); 31 | let ms = chalk.bold(formatDigits(date.valueOf() % 1000, 3)); 32 | 33 | return `${m} ${d}${e}, ${y} at ${hh}:${mm}:${ss}.${ms}`; 34 | 35 | }; 36 | 37 | class UserCommand extends Command { 38 | 39 | constructor() { 40 | 41 | super('user'); 42 | 43 | } 44 | 45 | help() { 46 | 47 | return { 48 | description: 'Retrieves (and sets) current user information', 49 | flags: { 50 | s: ' Sets a specified key-value pair' 51 | }, 52 | vflags: { 53 | set: ' Sets a specified key-value pair', 54 | 'new-password': 'Sets a new password via a prompt', 55 | 'reset-password': ' Sends a password reset request for the specified e-mail address' 56 | } 57 | }; 58 | 59 | } 60 | 61 | run(params, callback) { 62 | 63 | let host = 'api.polybit.com'; 64 | let port = 443; 65 | 66 | let hostname = (params.flags.h && params.flags.h[0]) || ''; 67 | let matches = hostname.match(/^(https?:\/\/)?(.*?)(:\d+)?$/); 68 | 69 | if (hostname && matches) { 70 | host = matches[2]; 71 | port = parseInt((matches[3] || '').substr(1) || (hostname.indexOf('https') === 0 ? 443 : 80)); 72 | } 73 | 74 | let resource = new APIResource(host, port); 75 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 76 | 77 | // If resetting password 78 | if (params.vflags['reset-password']) { 79 | let resetEmail = params.vflags['reset-password'][0]; 80 | return resource.request('v1/password_reset_requests').create({}, {email: resetEmail}, (err, response) => { 81 | 82 | if (err) { 83 | return callback(err); 84 | } 85 | 86 | console.log('Password reset e-mail sent. Check the inbox for ' + resetEmail + ' for more details.'); 87 | return callback(null); 88 | 89 | }); 90 | } 91 | 92 | if (params.vflags['new-password']) { 93 | return inquirer.prompt( 94 | [ 95 | { 96 | name: 'old_password', 97 | type: 'password', 98 | default: '', 99 | message: 'Old Password' 100 | }, 101 | { 102 | name: 'password', 103 | type: 'password', 104 | default: '', 105 | message: 'New Password' 106 | }, 107 | { 108 | name: 'repeat_password', 109 | type: 'password', 110 | default: '', 111 | message: 'Repeat Password' 112 | } 113 | ], 114 | (promptResult) => { 115 | 116 | resource.request('v1/users').index({me: true}, (err, response) => { 117 | 118 | if (err) { 119 | return callback(err); 120 | } 121 | 122 | let user = response.data[0]; 123 | if (!user) { 124 | return callback(new Error('We couldn\'t retrieve your user data. Try again shortly.')); 125 | } 126 | 127 | resource.request('v1/users').update(user.id, {}, promptResult, (err, response) => { 128 | 129 | if (err) { 130 | return callback(err); 131 | } 132 | 133 | let user = response.data[0]; 134 | if (!user) { 135 | return callback(new Error('We couldn\'t change your password. Try again shortly.')); 136 | } 137 | 138 | return callback(null, 'Password changed successfully.'); 139 | 140 | }); 141 | 142 | }); 143 | 144 | } 145 | ); 146 | } 147 | 148 | let set = params.vflags.set || params.flags.s || []; 149 | let update = null; 150 | 151 | if (set.length) { 152 | update = {}; 153 | update[set[0]] = set.slice(1).join(' '); 154 | if (update.password) { 155 | return callback(new Error('Please use --new-password to set your password')); 156 | } 157 | } 158 | 159 | let fnComplete = (user, callback) => { 160 | 161 | var len = 20; 162 | Object.keys(user).forEach(function(k) { 163 | var alen = Math.max(1, len - k.length + 1); 164 | console.log(k + ': ' + Array(alen).join(' ') + user[k]); 165 | }); 166 | 167 | console.log(); 168 | callback(null); 169 | 170 | }; 171 | 172 | resource.request('v1/users').index({me: true}, (err, response) => { 173 | 174 | if (err) { 175 | return callback(err); 176 | } 177 | 178 | let user = response.data[0]; 179 | if (!user) { 180 | return callback(new Error('We couldn\'t retrieve your user data. Try again shortly.')); 181 | } 182 | 183 | if (!update) { 184 | return fnComplete(user, callback); 185 | } 186 | 187 | resource.request('v1/users').update(user.id, {}, update, (err, response) => { 188 | 189 | if (err) { 190 | return callback(err); 191 | } 192 | 193 | let user = response.data[0]; 194 | if (!user) { 195 | return callback(new Error('We couldn\'t set your user data. Try again shortly.')); 196 | } 197 | 198 | fnComplete(user, callback); 199 | 200 | }); 201 | 202 | }); 203 | 204 | } 205 | 206 | } 207 | 208 | module.exports = UserCommand; 209 | -------------------------------------------------------------------------------- /cli/commands/up.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | const scripts = require('../scripts.js'); 7 | 8 | const fs = require('fs'); 9 | const zlib = require('zlib'); 10 | const path = require('path'); 11 | 12 | const async = require('async'); 13 | const tar = require('tar-stream'); 14 | 15 | 16 | function readFiles(base, properties, dir, data) { 17 | 18 | dir = dir || '/'; 19 | data = data || []; 20 | properties = properties || {}; 21 | 22 | let ignore = properties.ignore || {}; 23 | 24 | return fs.readdirSync(path.join(base, dir)).reduce((data, f) => { 25 | 26 | let pathname = path.join(dir, f); 27 | let fullpath = path.join(base, pathname); 28 | 29 | for (let i = 0; i < ignore.length; i++) { 30 | if (ignore[i][0] === '/') { 31 | if (pathname.split(path.sep).join('/') === ignore[i]) { 32 | return data; 33 | } 34 | } else { 35 | if (f === ignore[i]) { 36 | return data; 37 | } 38 | } 39 | } 40 | 41 | if (fs.statSync(fullpath).isDirectory()) { 42 | return readFiles(base, properties, pathname, data); 43 | } else { 44 | let filename = pathname[0] === path.sep ? pathname.substr(1) : pathname; 45 | let buffer = fs.readFileSync(fullpath); 46 | filename = filename.split(path.sep).join('/'); // Windows 47 | data.push({filename: filename, buffer: buffer}); 48 | return data; 49 | } 50 | 51 | }, data); 52 | 53 | }; 54 | 55 | class UpCommand extends Command { 56 | 57 | constructor() { 58 | 59 | super('up'); 60 | 61 | } 62 | 63 | help() { 64 | 65 | return { 66 | description: 'Pushes StdLib package to registry and cloud environment', 67 | args: [ 68 | 'environment' 69 | ], 70 | flags: { 71 | r: 'Upload a release package', 72 | }, 73 | vflags: { 74 | release: 'Upload a release package', 75 | } 76 | }; 77 | 78 | } 79 | 80 | run(params, callback) { 81 | 82 | let environment = params.args[0]; 83 | let release = params.flags.r || params.vflags.release; 84 | 85 | if (release && environment) { 86 | return callback(new Error('Can not release to an environment')); 87 | } 88 | 89 | if (!environment && !release) { 90 | return callback(new Error('Please specify an environment')); 91 | } 92 | 93 | if (release && release[0]) { 94 | return callback(new Error('Can only release to the version specified in "package.json"')); 95 | } 96 | 97 | let host = 'registry.stdlib.com'; 98 | let port = 443; 99 | 100 | let hostname = (params.flags.h && params.flags.h[0]) || ''; 101 | let matches = hostname.match(/^(https?:\/\/)?(.*?)(:\d+)?$/); 102 | 103 | if (hostname && matches) { 104 | host = matches[2]; 105 | port = parseInt((matches[3] || '').substr(1) || (hostname.indexOf('https') === 0 ? 443 : 80)); 106 | } 107 | 108 | let pkg; 109 | 110 | try { 111 | pkg = require(path.join(process.cwd(), 'package.json')); 112 | } catch(e) { 113 | return callback(new Error('Invalid package.json')); 114 | } 115 | 116 | if (!pkg.stdlib) { 117 | return callback(new Error('No stdlib information set in "package.json"')); 118 | } 119 | 120 | if (!pkg.stdlib.name) { 121 | return callback(new Error('No stdlib name set in "package.json"')); 122 | } 123 | 124 | scripts.run(pkg, 'preup', err => { 125 | 126 | if (err) { 127 | return callback(err); 128 | } 129 | 130 | let resource = new APIResource(host, port); 131 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 132 | 133 | !fs.existsSync('/tmp') && fs.mkdirSync('/tmp'); 134 | !fs.existsSync('/tmp/stdlib') && fs.mkdirSync('/tmp/stdlib', 0o777); 135 | let tmpPath = `/tmp/stdlib/${pkg.stdlib.name.replace(/\//g, '.')}.${new Date().valueOf()}.tar.gz`; 136 | 137 | let start = new Date().valueOf(); 138 | 139 | let tarball = fs.createWriteStream(tmpPath, {mode: 0o777}); 140 | 141 | let pack = tar.pack(); 142 | 143 | let defignore = ['/node_modules', '/.stdlib', '/.git', '.DS_Store']; 144 | let libignore = fs.existsSync('.libignore') ? fs.readFileSync('.libignore').toString() : ''; 145 | libignore = libignore.split('\n').map(v => v.replace(/^\s(.*)\s$/, '$1')).filter(v => v); 146 | while (defignore.length) { 147 | let ignore = defignore.pop(); 148 | (libignore.indexOf(ignore) === -1) && libignore.push(ignore); 149 | } 150 | 151 | let data = readFiles( 152 | process.cwd(), 153 | {ignore: libignore} 154 | ); 155 | 156 | // pipe the pack stream to your file 157 | pack.pipe(tarball); 158 | 159 | // Run everything in parallel... 160 | 161 | async.parallel(data.map((file) => { 162 | return (callback) => { 163 | pack.entry({name: file.filename}, file.buffer, callback); 164 | }; 165 | }), (err) => { 166 | 167 | if (err) { 168 | return callback(err); 169 | } 170 | 171 | pack.finalize(); 172 | 173 | }); 174 | 175 | tarball.on('close', () => { 176 | 177 | let buffer = fs.readFileSync(tmpPath); 178 | fs.unlinkSync(tmpPath); 179 | 180 | zlib.gzip(buffer, (err, result) => { 181 | 182 | if (err) { 183 | return callback(err); 184 | } 185 | 186 | let t = new Date().valueOf() - start; 187 | // console.log(`Service "${pkg.stdlib.name}" compressed in ${t}ms.`); 188 | // console.log(`File size: ${result.length} bytes`); 189 | 190 | let endpoint = environment ? 191 | `${pkg.stdlib.name}@${environment}` : 192 | `${pkg.stdlib.name}@${pkg.version}`; 193 | 194 | return resource.request(endpoint).stream( 195 | 'POST', 196 | result, 197 | (data) => { 198 | data.length > 1 && process.stdout.write(data.toString()); 199 | }, 200 | (err, response) => { 201 | 202 | if (err) { 203 | return callback(err); 204 | } 205 | 206 | if (response[response.length - 1] === 1) { 207 | return callback(new Error('There was an error processing your request')); 208 | } else { 209 | return callback(null); 210 | } 211 | 212 | } 213 | ); 214 | 215 | }); 216 | 217 | }); 218 | 219 | }); 220 | 221 | } 222 | 223 | } 224 | 225 | module.exports = UpCommand; 226 | -------------------------------------------------------------------------------- /cli/commands/create.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Command = require('cmnd').Command; 4 | const APIResource = require('api-res'); 5 | const Credentials = require('../credentials.js'); 6 | 7 | const async = require('async'); 8 | const inquirer = require('inquirer'); 9 | 10 | const fs = require('fs'); 11 | const path = require('path'); 12 | const chalk = require('chalk'); 13 | const lib = require('lib'); 14 | 15 | const spawnSync = require('child_process').spawnSync; 16 | 17 | function deepAssign(o1, o2) { 18 | Object.keys(o2).forEach(k => { 19 | if ( 20 | o1[k] && o2[k] && 21 | typeof o1[k] === 'object' && typeof o2[k] === 'object' 22 | ) { 23 | deepAssign(o1[k], o2[k]); 24 | } else { 25 | o1[k] = o2[k]; 26 | } 27 | }); 28 | } 29 | 30 | class CreateCommand extends Command { 31 | 32 | constructor() { 33 | 34 | super('create'); 35 | 36 | } 37 | 38 | help() { 39 | 40 | return { 41 | description: 'Creates a new (local) service', 42 | args: [ 43 | 'service' 44 | ], 45 | flags: { 46 | n: 'No login - don\'t require an internet connection', 47 | w: 'Write over - overwrite the current directory contents', 48 | t: 'Template - a stdlib service template to use' 49 | }, 50 | vflags: { 51 | 'no-login': 'No login - don\'t require an internet connection', 52 | 'write-over': 'Write over - overwrite the current directory contents', 53 | 'template': 'Template - a stdlib service template to use' 54 | } 55 | }; 56 | 57 | } 58 | 59 | run(params, callback) { 60 | 61 | let name = params.args[0]; 62 | 63 | let host = params.flags.h ? params.flags.h[0] : 'https://api.polybit.com'; 64 | let port = params.flags.p && params.flags.p[0]; 65 | 66 | let nologin = params.flags.hasOwnProperty('n') || params.vflags.hasOwnProperty('no-login'); 67 | 68 | let force = params.flags.hasOwnProperty('f') || params.vflags.hasOwnProperty('force'); 69 | let write = params.flags.hasOwnProperty('w') || params.vflags.hasOwnProperty('write-over'); 70 | 71 | let extPkgName = (params.flags.t || params.vflags.template || [])[0]; 72 | let extPkg = null; 73 | 74 | if (!force && !Credentials.location(1)) { 75 | console.log(); 76 | console.log(chalk.bold.red('Oops!')); 77 | console.log(); 78 | console.log(`You're trying to create a new service in development,`); 79 | console.log(`But you're not in a root stdlib project directory.`); 80 | console.log(`We recommend against this.`); 81 | console.log(); 82 | console.log(`Use ${chalk.bold('lib create --force')} to override.`); 83 | console.log(); 84 | return callback(null); 85 | } 86 | 87 | console.log(); 88 | console.log(`Awesome! Let's create a ${chalk.bold.green('stdlib')} service!`); 89 | extPkgName && console.log(`We'll use the template ${chalk.bold.green(extPkgName)} to proceed.`); 90 | console.log(); 91 | 92 | let questions = []; 93 | 94 | name || questions.push({ 95 | name: 'name', 96 | type: 'input', 97 | default: '', 98 | message: 'Service Name' 99 | }); 100 | 101 | let login = []; 102 | !nologin && login.push((cb) => { 103 | 104 | let resource = new APIResource(host, port); 105 | resource.authorize(Credentials.read('ACCESS_TOKEN')); 106 | 107 | resource.request('v1/users').index({me: true}, (err, response) => { 108 | 109 | if (err) { 110 | return cb(err); 111 | } 112 | 113 | return cb(null, response.data[0]); 114 | 115 | }); 116 | 117 | }); 118 | 119 | inquirer.prompt(questions, (promptResult) => { 120 | 121 | name = name || promptResult.name; 122 | let functionName = 'main'; // set default function name 123 | let username; 124 | 125 | if (name.indexOf('/') > -1) { 126 | username = name.split('/')[0]; 127 | name = name.split('/').slice(1).join('/').replace(/\//g, '-'); 128 | } 129 | 130 | // NOTE: Not offline friendly. Always log in user... 131 | // login = username ? [] : login; 132 | 133 | async.series(login, (err, results) => { 134 | 135 | if (err) { 136 | return callback(err); 137 | } 138 | 139 | let defaultUser = {username: 'dev', email: ''}; 140 | let user = nologin ? defaultUser : results[0]; 141 | user = user || defaultUser; 142 | 143 | username = username || user.username; 144 | 145 | // Do template fetching... 146 | let extPkgCalls = []; 147 | 148 | if (extPkgName) { 149 | 150 | console.log(`Fetching template ${chalk.bold.green(extPkgName)}...`); 151 | console.log(); 152 | 153 | extPkgCalls = [ 154 | cb => { 155 | lib.stdlib.templates({name: extPkgName}, (err, result) => { 156 | cb(err, result); 157 | }); 158 | }, 159 | cb => { 160 | lib.stdlib.templates.files({name: extPkgName}, (err, result) => { 161 | cb(err, result); 162 | }); 163 | } 164 | ]; 165 | 166 | } 167 | 168 | async.series(extPkgCalls, (err, results) => { 169 | 170 | if (err) { 171 | return callback(new Error(`Error retrieving template: ${extPkgName}`)); 172 | } 173 | 174 | if (results.length === 2) { 175 | extPkg = { 176 | pkg: results[0], 177 | files: results[1] 178 | }; 179 | } 180 | 181 | !fs.existsSync(username) && fs.mkdirSync(username); 182 | let servicePath = path.join(process.cwd(), username, name); 183 | let fPath = path.join(servicePath, 'f'); 184 | let functionPath; 185 | 186 | if (fs.existsSync(servicePath)) { 187 | 188 | if (!write) { 189 | 190 | console.log(); 191 | console.log(chalk.bold.red('Oops!')); 192 | console.log(); 193 | console.log(`The directory you're creating a stdlib project in already exists:`); 194 | console.log(` ${chalk.bold(servicePath)}`); 195 | console.log(); 196 | console.log(`Try removing the existing directory first.`); 197 | console.log(); 198 | console.log(`Use ${chalk.bold('lib create --write-over')} to override.`); 199 | console.log(); 200 | return callback(null); 201 | 202 | } 203 | 204 | } else { 205 | 206 | fs.mkdirSync(servicePath); 207 | fs.mkdirSync(fPath); 208 | 209 | } 210 | 211 | let directories = functionName.split('/'); 212 | for (let i = 0; i < directories.length; i++) { 213 | let relpath = path.join.apply(path, [fPath].concat(directories.slice(0, i + 1))); 214 | !fs.existsSync(relpath) && fs.mkdirSync(relpath); 215 | functionPath = relpath; 216 | } 217 | 218 | let json = { 219 | pkg: require(path.join(__dirname, '../templates/package.json')), 220 | func: require(path.join(__dirname, '../templates/f/function.json')) 221 | }; 222 | 223 | json.pkg.name = name; 224 | json.pkg.author = user.username + (user.email ? ` <${user.email}>` : ''); 225 | json.pkg.main = ['f', functionName, 'index.js'].join('/'); 226 | json.pkg.stdlib.name = [username, name].join('/'); 227 | json.pkg.stdlib.defaultFunction = functionName; 228 | 229 | // EXTERNAL: Assign package details 230 | if (extPkg && extPkg.pkg) { 231 | deepAssign(json.pkg, extPkg.pkg); 232 | } 233 | 234 | fs.writeFileSync( 235 | path.join(servicePath, 'package.json'), 236 | JSON.stringify(json.pkg, null, 2) 237 | ); 238 | 239 | json.func.name = functionName; 240 | 241 | fs.writeFileSync( 242 | path.join(functionPath, 'function.json'), 243 | JSON.stringify(json.func, null, 2) 244 | ); 245 | 246 | let files = { 247 | base: { 248 | copy: { 249 | '.gitignore': fs.readFileSync(path.join(__dirname, '../templates/gitignore')), 250 | 'env.json': fs.readFileSync(path.join(__dirname, '../templates/env.json')) 251 | }, 252 | template: { 253 | 'README.md': fs.readFileSync(path.join(__dirname, '../templates/README.md')).toString() 254 | } 255 | }, 256 | func: { 257 | copy: { 258 | 'index.js': fs.readFileSync(path.join(__dirname, '../templates/f/index.js')), 259 | } 260 | } 261 | }; 262 | 263 | let templateData = { 264 | username: username, 265 | service: name, 266 | func: functionName 267 | }; 268 | 269 | Object.keys(files.base.copy).forEach(filename => { 270 | fs.writeFileSync(path.join(servicePath, filename), files.base.copy[filename]) 271 | }); 272 | 273 | Object.keys(files.base.template).forEach(filename => { 274 | let template = files.base.template[filename]; 275 | Object.keys(templateData).forEach(k => { 276 | template = template.replace(new RegExp(`\{\{${k}\}\}`, 'gi'), templateData[k]); 277 | }); 278 | fs.writeFileSync(path.join(servicePath, filename), template); 279 | }); 280 | 281 | Object.keys(files.func.copy).forEach(filename => { 282 | fs.writeFileSync(path.join(functionPath, filename), files.func.copy[filename]) 283 | }); 284 | 285 | // EXTERNAL: Unzip tar 286 | if (extPkg && extPkg.files && extPkg.files.length) { 287 | let tmpPath = path.join(path.parse(process.cwd()).root, 'tmp'); 288 | let tarPath = path.join(tmpPath, 'stdlib-addon.tgz'); 289 | !fs.existsSync(tmpPath) && fs.mkdirSync(tmpPath); 290 | fs.existsSync(tarPath) && fs.unlinkSync(tarPath); 291 | fs.writeFileSync(tarPath, extPkg.files); 292 | let tarCommands = ['-xzf', tarPath]; 293 | // Win32 support 294 | /^win/.test(process.platform) && tarCommands.push('--force-local'); 295 | let command = spawnSync( 296 | 'tar', 297 | tarCommands, 298 | { 299 | stdio: [0, 1, 2], 300 | cwd: servicePath 301 | } 302 | ); 303 | fs.unlinkSync(tarPath); 304 | if (command.status !== 0) { 305 | return callback(new Error(`Could not install template ${extPkgName}`)); 306 | } 307 | } 308 | 309 | if ( 310 | (json.pkg.depenencies && Object.keys(json.pkg.dependencies).length) || 311 | (json.pkg.devDependencies && Object.keys(json.pkg.devDependencies).length) 312 | ) { 313 | console.log(`Installing npm packages...`); 314 | console.log(); 315 | let command = spawnSync( 316 | /^win/.test(process.platform) ? 'npm.cmd' : 'npm', 317 | ['install'], 318 | { 319 | stdio: [0, 1, 2], 320 | cwd: servicePath, 321 | env: process.env 322 | } 323 | ); 324 | if (command.status !== 0) { 325 | console.log(command.error); 326 | console.log(chalk.bold.yellow('Warn: ') + 'Error with npm install'); 327 | } 328 | } 329 | 330 | console.log(chalk.bold.green('Success!')); 331 | console.log(); 332 | console.log(`Service ${chalk.bold([username, name].join('/'))} created at:`); 333 | console.log(` ${chalk.bold(servicePath)}`); 334 | console.log(); 335 | console.log(`Use the following to enter your service directory:`); 336 | console.log(` ${chalk.bold('cd ' + [username, name].join('/'))}`); 337 | console.log(); 338 | console.log(`Type ${chalk.bold('lib help')} for more commands.`); 339 | console.log(); 340 | return callback(null); 341 | 342 | }); 343 | 344 | }); 345 | 346 | }); 347 | 348 | } 349 | 350 | } 351 | 352 | module.exports = CreateCommand; 353 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![StdLib](http://stdlib.com/static/images/stdlib-256.png) 2 | ## A Standard Library for Microservices 3 | 4 | [StdLib is a Standard Library for Microservices](https://stdlib.com) 5 | 6 | StdLib is the *fastest, easiest* way to turn functions into infinitely scalable, self-healing 7 | web services. It has three components: 8 | 9 | 1. A central registry for microservices 10 | 2. A distribution platform for hosting at scale 11 | 3. A development framework for package management and service creation 12 | 13 | StdLib is based on Function as a Service ("server-less") architecture, 14 | popularized by AWS Lambda. You can use StdLib to build modular, scalable web services 15 | for yourself and other developers in *minutes* without having to manage servers, 16 | gateways, domains, write documentation, or build SDKs. Your development workflow 17 | has never been easier - focus on writing code you love, let StdLib handle 18 | everything else. 19 | 20 | You can view services published by our large and growing developer community 21 | [on the StdLib search page](https://stdlib.com/search). 22 | 23 | ![stdlib-process](http://stdlib.com/static/images/stdlib_usage.gif) 24 | 25 | # Table of Contents 26 | 27 | 1. [Getting Started](#getting-started) 28 | 2. [Creating Your First Service](#creating-your-first-service) 29 | 3. [Connecting Service Endpoints](#connecting-service-endpoints) 30 | 4. [Accessing Your Microservices From Other Applications](#accessing-your-microservices-from-other-applications) 31 | 5. [Accessing Your Microservices Over HTTP](#accessing-your-microservices-over-http) 32 | 6. [Version Control and Package Management](#version-control-and-package-management) 33 | 7. [Additional Functionality](#additional-functionality) 34 | 8. [Acknowledgements](#acknowledgements) 35 | 9. [Contact](#contact) 36 | 37 | # Getting Started 38 | 39 | To get started with StdLib, first make sure you have Node 6.x installed, 40 | [available from the official Node.js website](https://nodejs.org). Next install 41 | the StdLib CLI tools with: 42 | 43 | ``` 44 | $ npm install lib.cli -g 45 | ``` 46 | 47 | And you're now ready to start building! 48 | 49 | # Upgrading From Previous Versions 50 | 51 | If you're running a previous version of StdLib and having issues with the CLI, 52 | try cleaning up the old CLI binary links first; 53 | 54 | ``` 55 | $ rm /usr/local/bin/f 56 | $ rm /usr/local/bin/lib 57 | $ rm /usr/local/bin/stdlib 58 | ``` 59 | 60 | # Creating Your First Service 61 | 62 | The first thing you'll want to do is create a workspace. Create a new directory 63 | you intend to build your services in and initialize the workspace. 64 | 65 | ``` 66 | $ mkdir stdlib-workspace 67 | $ cd stdlib-workspace 68 | $ lib init 69 | ``` 70 | 71 | You'll be asked for an e-mail address to log in to the StdLib registry, 72 | via the Polybit API server. If you don't yet have an account, you can create 73 | one from the command line. Note that you can skip account creation with 74 | `lib init --no-login`. You'll be unable to use the registry, but it's useful 75 | for creating workspaces when you don't have internet access. 76 | 77 | Next, create your service: 78 | 79 | ``` 80 | $ lib create 81 | ``` 82 | 83 | You'll be asked for a default function name, which is the entry point 84 | into your service (useful if you only want a single entry point). This will automatically 85 | generate a service project scaffold in `stdlib-workspace//`. 86 | 87 | Once created, enter the service directory: 88 | 89 | ``` 90 | $ cd your-username/your-service 91 | ``` 92 | 93 | In this directory, you'll see something like: 94 | 95 | ``` 96 | - f/ 97 | - defaultFunction/ 98 | - function.json 99 | - index.js 100 | - package.json 101 | - env.json 102 | - README.md 103 | ``` 104 | 105 | At this point, there's a "hello world" function that's been automatically 106 | created. StdLib comes paired with a simple `f` command for testing your functions 107 | locally and running them in the cloud. To test your function: 108 | 109 | ``` 110 | $ lib . 111 | > "hello world" 112 | ``` 113 | 114 | If we examine the `f/defaultFunction/index.js` file, we see the following: 115 | 116 | ```javascript 117 | module.exports = (params, callback) => { 118 | 119 | callback(null, 'hello world'); 120 | 121 | }; 122 | ``` 123 | 124 | If necessary, we can pass some of these parameters to it (`params.args` and `params.kwargs`) 125 | using: 126 | 127 | ``` 128 | lib . arg0 arg1 --kwarg0 "Hello World" --kwarg1 Goodbye 129 | ``` 130 | 131 | Though it won't change the function output as-is. `params.args` would be equal 132 | to `["arg0", "arg1"]` and `params.kwargs` would be 133 | `{"kwarg0":"Hello World","kwarg1":"Goodbye"}`. 134 | 135 | ## Pushing to the Cloud 136 | 137 | To push your function to a development environment in the cloud... 138 | 139 | ``` 140 | $ lib up dev 141 | $ lib your-username.your-service[@dev] 142 | > "hello world" 143 | ``` 144 | 145 | And to release it (when you're ready!) 146 | 147 | ``` 148 | $ lib release 149 | $ lib your-username.your-service 150 | > "hello world" 151 | ``` 152 | 153 | You can check out your service on the web, and use it in applications at: 154 | 155 | ``` 156 | https://your-username.stdlib.com/your-service 157 | ``` 158 | 159 | That's it! You haven't written a line of code yet, and you have mastery over 160 | building a service, testing it in a development (staging) environment online, 161 | and releasing it for private (or public) consumption. 162 | 163 | **Note:** You'll need to set `"publish": true` in the `lib` key of your 164 | `package.json` file to see your service appear in the public registry. It's 165 | set to `false` by default. 166 | 167 | **Another Note:** Staging environments (like the one created with `lib up dev`) 168 | are *mutable* and can be replaced indefinitely. Releases (`lib release`) are 169 | *immutable* and can never be overwritten. However, any service can be torn down 170 | with `lib down ` or `lib down -r ` (but releases 171 | can't be replaced once removed, to prevent mistakes and / or bad actors). 172 | 173 | # Connecting Service Endpoints 174 | 175 | You'll notice that you can create more than one function per service. While 176 | you can structure your project however you'd like internally, it should also 177 | be noted that these functions have zero-latency access to each other. You 178 | can access them internally with the `lib` [package on NPM](https://github.com/stdlib/lib-node), 179 | which behaves similarly to the `lib` command for testing. Use: 180 | 181 | ``` 182 | $ npm install lib --save 183 | ``` 184 | 185 | In your main service directory to add it, and use it like so: 186 | 187 | #### f/add/index.js 188 | ```javascript 189 | module.exports = (params, callback) => { 190 | 191 | return callback(null, params.args[1] + params.args[2]); 192 | 193 | }; 194 | ``` 195 | 196 | #### f/add-double/index.js 197 | ```javascript 198 | const lib = require('lib'); 199 | 200 | module.exports = (params, callback) => { 201 | 202 | return lib['.add'](params.args[0], params.args[1], (err, result) => { 203 | 204 | callback(err, result * 2); 205 | 206 | }); 207 | 208 | }; 209 | ``` 210 | 211 | In this case, calling `lib .add 1 2` will return `3` and `lib .add-double 1 2` 212 | will return `6`. These map directly to individual service endpoints. **Note** that 213 | when chaining like this, *a single service execution instance* is being used so 214 | be careful about setting service timeouts appropriately. 215 | 216 | # Accessing Your Microservices From Other Applications 217 | 218 | As mentioned in the previous section, you can use the NPM `lib` package that's 219 | [available on GitHub and NPM](https://github.com/stdlib/lib-node) to access your 220 | microservices from legacy Node.js applications and even the web browser. We'll 221 | have more SDKs coming out in the following months. 222 | 223 | A legacy app would call a function (username.liveService with version 0.2.1): 224 | 225 | ```javascript 226 | const lib = require('lib'); 227 | 228 | lib.username.liveService['@0.2.1']('hello', 'world', {keyword: 'argument'}, function (err, result) { 229 | 230 | if (err) { 231 | // handle it 232 | } 233 | 234 | // do something with result 235 | 236 | }); 237 | ``` 238 | 239 | Which would speak to your microservice... 240 | 241 | ```javascript 242 | module.exports = (params, callback) => { 243 | 244 | params.args[0] === 'hello'; // true 245 | params.args[1] === 'world'; // true 246 | params.kwargs.keyword === 'argument'; // true 247 | 248 | callback(null, 'Done!'); 249 | 250 | }; 251 | ``` 252 | 253 | # Accessing Your Microservices Over HTTP 254 | 255 | We definitely recommend using the [lib library on NPM](https://github.com/stdlib/lib-node) 256 | to make microservice calls as specified above, but you can also make HTTPS 257 | requests directly to the StdLib gateway. HTTP query parameters are mapped 258 | automatically to keyword arguments: 259 | 260 | ``` 261 | https://username.stdlib.com/liveService@1.12.2?name=Keith 262 | ``` 263 | 264 | Maps directly to: 265 | 266 | ```javascript 267 | module.exports = (params, callback) => { 268 | 269 | params.kwargs.name === 'Keith'; // true 270 | 271 | callback(null, 'Done!'); 272 | 273 | }; 274 | ``` 275 | 276 | **Note** that you will not be able to pass in anything to the `params.args` 277 | parameter. 278 | 279 | # Version Control and Package Management 280 | 281 | A quick note on version control - StdLib is *not* a replacement for normal 282 | git-based workflows, it is a supplement focused around service creation and 283 | execution. 284 | 285 | You have unlimited access to any release (that hasn't been torn down) 286 | with `lib pkg ` to download the tarball (`.tgz`) and 287 | `lib get ` to automatically download and unpack the 288 | tarball to a working directory. 289 | 290 | Tarballs (and package contents) are *closed-source*. 291 | Nobody but you (and potentially your teammates) has access to these. It's up to 292 | you whether or not you share the guts of your service with others on GitHub or NPM. 293 | 294 | As mentioned above: releases are *immutable* and can not be overwritten (but can 295 | be removed, just not replaced afterwards) and development / staging environments 296 | are *mutable*, you can overwrite them as much as you'd like. 297 | 298 | # Additional Functionality 299 | 300 | StdLib comes packed with a bunch of other goodies - if your service goes down 301 | for any reason (the service platform is acting up), use `lib restart`. 302 | Similarly, as we roll out updates to the platform the builds we're using on 303 | AWS Lambda may change. You can update your service to our latest build using 304 | `lib rebuild`. We may recommend this from time-to-time, so pay attention 305 | to e-mails and the community. 306 | 307 | To see a full list of commands available for the CLI tools, type: 308 | 309 | ``` 310 | $ lib help 311 | ``` 312 | 313 | We've conveniently copy-and-pasted the output here for you to peruse; 314 | 315 | ``` 316 | * [all arguments converted to params.args] 317 | -f Specify a file to send (overrides args and kwargs) 318 | --* all verbose flagss converted to params.kwargs 319 | 320 | Runs a StdLib Function (requires a period) 321 | 322 | create [service] 323 | -n No login - don't require an internet connection 324 | -t Template - a stdlib service template to use 325 | -w Write over - overwrite the current directory contents 326 | --no-login No login - don't require an internet connection 327 | --template Template - a stdlib service template to use 328 | --write-over Write over - overwrite the current directory contents 329 | 330 | Creates a new (local) service 331 | 332 | down [environment] 333 | -r Remove a release version (provide number) 334 | --release Remove a release version (provide number) 335 | 336 | Removes StdLib package from registry and cloud environment 337 | 338 | f:create [function name] 339 | -w Overwrite existing function 340 | --write-over Overwrite existing function 341 | 342 | Creates a new function for a (local) service 343 | 344 | get [full service name] 345 | -f Force command if not in root directory 346 | -w Write over - overwrite the target directory contents 347 | --force Force command if not in root directory 348 | --write-over Write over - overwrite the target directory contents 349 | 350 | Retrieves and extracts StdLib package 351 | 352 | http 353 | -p Port (Default 8170) 354 | --port Port (Default 8170) 355 | 356 | Creates HTTP Server for Current Service 357 | 358 | info [username | full service name] 359 | Retrieves information about a user or package 360 | 361 | init [environment] 362 | -f Force command to overwrite existing workspace 363 | -n No login - don't require an internet connection 364 | --force Force command to overwrite existing workspace 365 | --no-login No login - don't require an internet connection 366 | 367 | Initializes StdLib workspace 368 | 369 | login 370 | Logs in to StdLib in this directory 371 | 372 | logout 373 | Logs out of StdLib in this workspace 374 | 375 | pkg [full service name] 376 | -f Force command if not in root directory 377 | -o Output path for the .tgz package 378 | --force Force command if not in root directory 379 | --output Output path for the .tgz package 380 | 381 | Downloads StdLib tarball (.tgz) 382 | 383 | rebuild [environment] 384 | -r Rebuild a release package 385 | --release Rebuild a release package 386 | 387 | Rebuilds a service (useful for registry performance updates), alias of `lib restart -b` 388 | 389 | register 390 | Registers a new StdLib user account 391 | 392 | release 393 | Pushes release of StdLib package to registry and cloud (Alias of `lib up -r`) 394 | 395 | restart [environment] 396 | -b Rebuild service fully 397 | -r Restart a release package 398 | --build Rebuild service fully 399 | --release Restart a release package 400 | 401 | Restarts a StdLib service (if necessary) 402 | 403 | rollback 404 | Rolls back (removes) release of StdLib package (alias of `lib down -r`) 405 | 406 | up [environment] 407 | -r Upload a release package 408 | --release Upload a release package 409 | 410 | Pushes StdLib package to registry and cloud environment 411 | 412 | user 413 | -s Sets a specified key-value pair 414 | --new-password Sets a new password via a prompt 415 | --reset-password Sends a password reset request for the specified e-mail address 416 | --set Sets a specified key-value pair 417 | 418 | Retrieves (and sets) current user information 419 | 420 | version 421 | Returns currently installed version of StdLib command lines tools 422 | ``` 423 | 424 | # That's it! 425 | 426 | Yep, it's really that easy. To keep up-to-date on developments, please 427 | star us here on GitHub, and sign up a user account for the registry. You 428 | can read more about service hosting and keep track of official updates on 429 | [the official StdLib website, stdlib.com](https://stdlib.com). 430 | 431 | # Acknowledgements 432 | 433 | StdLib is a product of and © 2016 - 2017 Polybit Inc. 434 | 435 | We'd love for you to pay attention to [@Polybit](https://twitter.com/polybit) and 436 | what we're building next! If you'd consider joining the team, [shoot us an e-mail](mailto:careers@stdlib.com). 437 | 438 | You can also follow me, the original author, on Twitter: [@keithwhor](https://twitter.com/keithwhor). 439 | 440 | Issues encouraged, PRs welcome, and we're happy to have you on board! 441 | Enjoy and happy building :) 442 | 443 | # Thanks 444 | 445 | Special thanks to; [AngelPad](https://angelpad.org), 446 | [Brian LeRoux](https://twitter.com/brianleroux), 447 | [Boris Mann](https://twitter.com/bmann), 448 | [TJ Holowaychuk](https://twitter.com/tjholowaychuk) 449 | --------------------------------------------------------------------------------