├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # memo directory 61 | memo/ 62 | .idea 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 sinofseven 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM](https://nodei.co/npm/serverless-s3-remover.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/serverless-s3-remover/) 2 | [![NPM](https://nodei.co/npm-dl/serverless-s3-remover.png?height=2)](https://nodei.co/npm/serverless-s3-remover/) 3 | # serverless-s3-remover 4 | plugin for serverless to make buckets empty before remove 5 | 6 | # Usage 7 | Run next command. 8 | ```bash 9 | $ npm install serverless-s3-remover 10 | ``` 11 | 12 | Add to your serverless.yml 13 | ```yaml 14 | plugins: 15 | - serverless-s3-remover 16 | 17 | custom: 18 | remover: 19 | buckets: 20 | - my-bucket-1 21 | - my-bucket-2 22 | ``` 23 | 24 | You can specify any number of `bucket`s that you want. 25 | 26 | Now you can make all buckets empty by running: 27 | ```bash 28 | $ sls s3remove 29 | ``` 30 | 31 | # When removing 32 | When removing serverless stack, this plugin automatically make buckets empty before removing stack. 33 | ```sh 34 | $ sls remove 35 | ``` 36 | 37 | # Using Prompt 38 | You can use prompt before deleting bucket. 39 | 40 | ```yaml 41 | custom: 42 | remover: 43 | prompt: true # default value is `false` 44 | buckets: 45 | - remover-bucket-a 46 | - remover-bucket-b 47 | ``` 48 | 49 | ![terminal.png](https://user-images.githubusercontent.com/57114/31264298-0896f1ec-aaa3-11e7-9a8e-86e3c3f34e23.png) 50 | 51 | # Populating the configuration object before using it 52 | ```yaml 53 | custom: 54 | boolean: 55 | true: true 56 | false: false 57 | remover: 58 | prompt: ${self:custom.boolean.${opt:s3-remover-prompt, 'true'}} 59 | ``` 60 | 61 | I can use the command line argument ```--s3-remover-prompt false``` to disable the prompt feature. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const chalk = require('chalk'); 4 | const prompt = require('prompt'); 5 | const messagePrefix = 'S3 Remover: '; 6 | 7 | class Remover { 8 | constructor(serverless, options) { 9 | this.serverless = serverless; 10 | this.options = options; 11 | this.provider = this.serverless.getProvider('aws'); 12 | 13 | this.commands = { 14 | s3remove: { 15 | usage: 'Remove all files in S3 buckets', 16 | lifecycleEvents: [ 17 | 'remove' 18 | ], 19 | options: { 20 | verbose: { 21 | usage: 'Increase verbosity', 22 | shortcut: 'v' 23 | } 24 | } 25 | } 26 | }; 27 | 28 | this.hooks = { 29 | 'before:remove:remove': () => Promise.resolve().then(this.remove.bind(this)), 30 | 's3remove:remove': () => Promise.resolve().then(this.remove.bind(this)) 31 | }; 32 | } 33 | 34 | log(message) { 35 | if (this.options.verbose) { 36 | this.serverless.cli.log(message); 37 | } 38 | } 39 | 40 | remove() { 41 | const self = this; 42 | 43 | const getAllKeys = (bucket) => { 44 | const get = (src = {}) => { 45 | const data = src.data; 46 | const keys = src.keys || []; 47 | const param = { 48 | Bucket: bucket 49 | }; 50 | if (data) { 51 | param.ContinuationToken = data.NextContinuationToken; 52 | } 53 | return self.provider.request('S3', 'listObjectsV2', param).then((result) => { 54 | return new Promise((resolve) => { 55 | resolve({ 56 | data: result, keys: keys.concat(result.Contents.map((item) => { 57 | return item.Key; 58 | })) 59 | }); 60 | }); 61 | }); 62 | }; 63 | const list = (src = {}) => { 64 | return get(src).then((result) => { 65 | if (result.data.IsTruncated) { 66 | return list(result); 67 | } else { 68 | const keys = result.keys; 69 | const batched = []; 70 | for (let i = 0; i < keys.length; i += 1000) { 71 | const objects = keys.slice(i, i + 1000).map((item) => { 72 | return {Key: item}; 73 | }); 74 | batched.push({ 75 | Bucket: bucket, 76 | Delete: { 77 | Objects: objects 78 | } 79 | }); 80 | } 81 | return new Promise((resolve) => { 82 | resolve(batched); 83 | }); 84 | } 85 | }); 86 | }; 87 | return list(); 88 | }; 89 | const executeRemove = (params) => { 90 | return Promise.all(params.map(param => { 91 | return self.provider.request('S3', 'deleteObjects', param); 92 | })); 93 | }; 94 | 95 | const populateConfig = () => { 96 | return this.serverless.variables.populateObject(this.serverless.service.custom.remover) 97 | .then(fileConfig => { 98 | const defaultConfig = { 99 | prompt: false, 100 | buckets: [], 101 | }; 102 | return Object.assign({}, defaultConfig, fileConfig); 103 | }); 104 | }; 105 | 106 | return new Promise((resolve) => { 107 | return populateConfig().then(config => { 108 | if (!config.prompt) { 109 | let promisses = []; 110 | for (const b of config.buckets) { 111 | promisses.push(getAllKeys(b).then(executeRemove).then(() => { 112 | const message = `Success: ${b} is empty.`; 113 | self.log(message); 114 | self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`); 115 | }).catch((err) => { 116 | const message = `Faild: ${b} may not be empty.`; 117 | self.log(message); 118 | self.log(err); 119 | self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`); 120 | })); 121 | } 122 | return Promise.all(promisses).then(resolve); 123 | } 124 | prompt.message = messagePrefix; 125 | prompt.delimiter = ''; 126 | prompt.start(); 127 | const schema = {properties: {}}; 128 | config.buckets.forEach((b) => { 129 | schema.properties[b] = { 130 | message: `Make ${b} empty. Are you sure? [yes/no]:`, 131 | validator: /(yes|no)/, 132 | required: true, 133 | warning: 'Must respond yes or no' 134 | }; 135 | }); 136 | prompt.get(schema, (err, result) => { 137 | let promisses = []; 138 | for (const b of config.buckets) { 139 | if (result[b].match(/^y/)) { 140 | promisses.push(getAllKeys(b).then(executeRemove).then(() => { 141 | const message = `Success: ${b} is empty.`; 142 | self.log(message); 143 | self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`); 144 | }).catch(() => { 145 | const message = `Faild: ${b} may not be empty.`; 146 | self.log(message); 147 | self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`); 148 | })); 149 | } else { 150 | promisses.push(Promise.resolve().then(() => { 151 | const message = `Remove cancelled: ${b}`; 152 | self.log(message); 153 | self.serverless.cli.consoleLog(`${messagePrefix}${chalk.yellow(message)}`); 154 | })); 155 | } 156 | } 157 | Promise.all(promisses).then(resolve); 158 | }); 159 | }); 160 | }); 161 | } 162 | } 163 | 164 | module.exports = Remover; 165 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-s3-remover", 3 | "version": "0.6.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-styles": { 8 | "version": "3.2.1", 9 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 10 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 11 | "requires": { 12 | "color-convert": "^1.9.0" 13 | } 14 | }, 15 | "async": { 16 | "version": "0.9.2", 17 | "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", 18 | "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" 19 | }, 20 | "balanced-match": { 21 | "version": "1.0.0", 22 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 23 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 24 | }, 25 | "brace-expansion": { 26 | "version": "1.1.11", 27 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 28 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 29 | "requires": { 30 | "balanced-match": "^1.0.0", 31 | "concat-map": "0.0.1" 32 | } 33 | }, 34 | "chalk": { 35 | "version": "2.4.1", 36 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 37 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | }, 44 | "color-convert": { 45 | "version": "1.9.2", 46 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", 47 | "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", 48 | "requires": { 49 | "color-name": "1.1.1" 50 | } 51 | }, 52 | "color-name": { 53 | "version": "1.1.1", 54 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", 55 | "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" 56 | }, 57 | "colors": { 58 | "version": "1.3.0", 59 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.0.tgz", 60 | "integrity": "sha512-EDpX3a7wHMWFA7PUHWPHNWqOxIIRSJetuwl0AS5Oi/5FMV8kWm69RTlgm00GKjBO1xFHMtBbL49yRtMMdticBw==" 61 | }, 62 | "concat-map": { 63 | "version": "0.0.1", 64 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 65 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 66 | }, 67 | "cycle": { 68 | "version": "1.0.3", 69 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 70 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" 71 | }, 72 | "deep-equal": { 73 | "version": "0.2.2", 74 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", 75 | "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=" 76 | }, 77 | "escape-string-regexp": { 78 | "version": "1.0.5", 79 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 80 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 81 | }, 82 | "eyes": { 83 | "version": "0.1.8", 84 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 85 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" 86 | }, 87 | "fs.realpath": { 88 | "version": "1.0.0", 89 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 90 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 91 | }, 92 | "glob": { 93 | "version": "7.1.2", 94 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 95 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 96 | "requires": { 97 | "fs.realpath": "^1.0.0", 98 | "inflight": "^1.0.4", 99 | "inherits": "2", 100 | "minimatch": "^3.0.4", 101 | "once": "^1.3.0", 102 | "path-is-absolute": "^1.0.0" 103 | } 104 | }, 105 | "has-flag": { 106 | "version": "3.0.0", 107 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 108 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 109 | }, 110 | "i": { 111 | "version": "0.3.6", 112 | "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", 113 | "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=" 114 | }, 115 | "inflight": { 116 | "version": "1.0.6", 117 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 118 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 119 | "requires": { 120 | "once": "^1.3.0", 121 | "wrappy": "1" 122 | } 123 | }, 124 | "inherits": { 125 | "version": "2.0.3", 126 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 127 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 128 | }, 129 | "isstream": { 130 | "version": "0.1.2", 131 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 132 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 133 | }, 134 | "minimatch": { 135 | "version": "3.0.4", 136 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 137 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 138 | "requires": { 139 | "brace-expansion": "^1.1.7" 140 | } 141 | }, 142 | "minimist": { 143 | "version": "0.0.8", 144 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 145 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 146 | }, 147 | "mkdirp": { 148 | "version": "0.5.1", 149 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 150 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 151 | "requires": { 152 | "minimist": "0.0.8" 153 | } 154 | }, 155 | "mute-stream": { 156 | "version": "0.0.7", 157 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", 158 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" 159 | }, 160 | "ncp": { 161 | "version": "1.0.1", 162 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", 163 | "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=" 164 | }, 165 | "once": { 166 | "version": "1.4.0", 167 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 168 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 169 | "requires": { 170 | "wrappy": "1" 171 | } 172 | }, 173 | "path-is-absolute": { 174 | "version": "1.0.1", 175 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 176 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 177 | }, 178 | "pkginfo": { 179 | "version": "0.4.1", 180 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", 181 | "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=" 182 | }, 183 | "prompt": { 184 | "version": "1.0.0", 185 | "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz", 186 | "integrity": "sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4=", 187 | "requires": { 188 | "colors": "^1.1.2", 189 | "pkginfo": "0.x.x", 190 | "read": "1.0.x", 191 | "revalidator": "0.1.x", 192 | "utile": "0.3.x", 193 | "winston": "2.1.x" 194 | } 195 | }, 196 | "read": { 197 | "version": "1.0.7", 198 | "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", 199 | "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", 200 | "requires": { 201 | "mute-stream": "~0.0.4" 202 | } 203 | }, 204 | "revalidator": { 205 | "version": "0.1.8", 206 | "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", 207 | "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=" 208 | }, 209 | "rimraf": { 210 | "version": "2.6.2", 211 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 212 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 213 | "requires": { 214 | "glob": "^7.0.5" 215 | } 216 | }, 217 | "stack-trace": { 218 | "version": "0.0.10", 219 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 220 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 221 | }, 222 | "supports-color": { 223 | "version": "5.4.0", 224 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 225 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 226 | "requires": { 227 | "has-flag": "^3.0.0" 228 | } 229 | }, 230 | "utile": { 231 | "version": "0.3.0", 232 | "resolved": "https://registry.npmjs.org/utile/-/utile-0.3.0.tgz", 233 | "integrity": "sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo=", 234 | "requires": { 235 | "async": "~0.9.0", 236 | "deep-equal": "~0.2.1", 237 | "i": "0.3.x", 238 | "mkdirp": "0.x.x", 239 | "ncp": "1.0.x", 240 | "rimraf": "2.x.x" 241 | } 242 | }, 243 | "winston": { 244 | "version": "2.1.1", 245 | "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", 246 | "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", 247 | "requires": { 248 | "async": "~1.0.0", 249 | "colors": "1.0.x", 250 | "cycle": "1.0.x", 251 | "eyes": "0.1.x", 252 | "isstream": "0.1.x", 253 | "pkginfo": "0.3.x", 254 | "stack-trace": "0.0.x" 255 | }, 256 | "dependencies": { 257 | "async": { 258 | "version": "1.0.0", 259 | "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", 260 | "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" 261 | }, 262 | "colors": { 263 | "version": "1.0.3", 264 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", 265 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" 266 | }, 267 | "pkginfo": { 268 | "version": "0.3.1", 269 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", 270 | "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" 271 | } 272 | } 273 | }, 274 | "wrappy": { 275 | "version": "1.0.2", 276 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 277 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-s3-remover", 3 | "version": "0.6.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:sinofseven/serverless-s3-remover.git", 6 | "author": "sinofseven(workspace) ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "chalk": "^2.1.0", 10 | "prompt": "^1.0.0" 11 | } 12 | } 13 | --------------------------------------------------------------------------------