{"id":13659084,"url":"https://github.com/rapid7/savery","last_synced_at":"2025-04-24T12:30:51.162Z","repository":{"id":66307160,"uuid":"70486969","full_name":"rapid7/savery","owner":"rapid7","description":null,"archived":true,"fork":false,"pushed_at":"2016-10-14T16:40:47.000Z","size":184,"stargazers_count":21,"open_issues_count":1,"forks_count":3,"subscribers_count":77,"default_branch":"master","last_synced_at":"2024-10-11T11:50:47.003Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rapid7.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-10-10T12:47:59.000Z","updated_at":"2023-01-28T08:50:34.000Z","dependencies_parsed_at":"2023-03-10T23:48:33.970Z","dependency_job_id":null,"html_url":"https://github.com/rapid7/savery","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fsavery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fsavery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fsavery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fsavery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rapid7","download_url":"https://codeload.github.com/rapid7/savery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223952670,"owners_count":17230933,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T05:01:05.147Z","updated_at":"2024-11-10T12:31:18.220Z","avatar_url":"https://github.com/rapid7.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# savery\n\n### Table of contents\n* [Installation](#installation)\n* [Usage](#usage)\n* [Methods](#methods)\n* [Advanced usage](#advanged-usage)\n* [Supported browsers](#supported-browsers)\n* [Additional information](#additional-information)\n* [Development](#development)\n\n### Installation\n\n```\n$ npm i savery --save\n```\n\n### Usage\n\n```javascript\n// ES2015+\nimport savery from 'savery';\n\n// CommonJS\nconst savery = require('savery');\n\n// script\nconst savery = window.savery;\n\n// save the file immediately with a single line\nsavery.save('.foo { display: block; }', 'foo.css');\n\n// or create use the partial applcation function option with name and options\nconst saveCssFile = savery('foo.css', {\n    onBeforeSave() {\n      console.log('Starting!');\n    }  \n});\n\n// and then pass it the data\nconst file = saveCssFile('.foo { display: block; }');\n\nfile.save(); // to execute the creation of the file from the data\nfile.abort(); // if you want to abort the request to create the file\n\n// all save executions are chainable\nsavery.save('.foo { display: block; }', 'foo.css')\n    .then((saveryInstance) =\u003e {\n        console.log('I am complete! Here is the instance to prove it: ', saveryInstance);\n    })\n    .catch((saveryInstance) =\u003e {\n        console.log('Oops, something went wrong. :(');\n        \n        throw saveryInstance.error;\n    });\n```\n\nAs you can see, you can either call `savery.save` to immediately fire the save process, or you can create a partial function which accepts `filename` and `saveryOptions` but returns a function that accepts the `data`. The reason for the partial function is the ability for reuse. For example, if you know the file type and want a consistent filename from a remote download:\n\n```javascript\nimport axios from 'axios';\nimport savery from 'savery';\n\nconst PDF_NAME = 'my-consistently-named.pdf';\n\n// save the consistent implementation\nconst pdfFileSavery = savery(PDF_NAME);\n\n// create a function that accepts the data and calls the .save()\nconst savePdfFile = (data) =\u003e {\n  return pdfFileSavery(data).save();\n};\n\n// in your API call, you can pass it as a simple method\nconst getPdf = () =\u003e {\n  axios({\n    method: 'get',\n    responseType: 'arraybuffer',\n    uri: '/location/of/pdf'\n  })\n    .then(savePdfFile);\n};\n```\n\nOr if you needed to support a dynamic filename, it could still be a simple one-liner:\n\n```javascript\nimport axios from 'axios';\nimport savery from 'savery';\n\n// create a function that accepts the data and calls the .save()\nconst savePdfFile = (name, options = {}) =\u003e {\n  const saveryInstance = savery(name, options);\n  \n  return (data) =\u003e {\n    return saveryInstance(data).save();\n  };\n};\n\n// in your API call, you can pass it as a simple method\nconst getPdf = (dynamicFilename) =\u003e {\n  axios({\n    method: 'get',\n    responseType: 'arraybuffer',\n    uri: '/location/of/pdf'\n  })\n    .then(savePdfFile(dynamicFileName));\n};\n```\n\nWith the partial applcation function usage, you have control over the execution while still maximizing code reuse and readability.\n\n### Methods\n\n**savery(filename: string = 'download.txt', saveryOptions: Object = {}): saveryInstance**\n\nThe `savery` function accepts the `filename` and `options` parameter, both with defaults. It returns a `saveryInstance` that has the attributes passed applied.\n\n**saveryOptions**\n```javascript\n{\n  onAbort: ?Function,\n  onAfterSave: ?Function,\n  onBeforeSave: ?Function,\n  onError: ?Function,\n  onStartSave: ?Function,\n  onEndSave: ?Function,\n  shouldAutoBom: ?boolean = true,\n  type: ?string\n}\n```\n\nAll `saveryOptions` properties are optional, and `type` specifically will default to being intuited from the filename extension and that extension's standard MIME type. The `shouldAutoBom` feature automatically provides Unicode text encoding hints (see [byte-order mark](https://en.wikipedia.org/wiki/Byte_order_mark) for more details).\n\n**saveryInstance.save(): Promise**\n\nThis will execute the saving of the file, and return the `Promise` of the file's processing. This allows you to chain the results:\n\n```javascript\nconst saveryInstance = savery('foo.txt');\n\nsaveryInstance.save()\n  .then((instance) =\u003e {\n    console.log(instance); // instance after completion\n  })\n  .catch((instance) =\u003e {\n    console.log(instance); // instance after the error completion\n    console.error(instance.error); // completion error\n  });\n```\n\nThe lifecycle methods shown above in options are fired as part of the save process in the order expected:\n* `onBeforeSave` (before all other execution)\n* `onStartSave` (before blob is created)\n* `onEndSave` (after blob is created and save is attempted)\n* `onAfterSave` (after all other execution)\n\n**saveryInstance.abort(): void**\n\nThis will abort the instance execution immediately. This fires two methods in the order shown:\n\n* `onAbort` (immediately upon aborting)\n* `onError` (once aborted, internally it fires the `onError` so that the `.catch()` in the chain is also called)\n\n**savery.save(data: any, filename: string = 'download.txt', saveryOptions: Object = {}): Promise**\n\nThis will immediate trigger the save process with the `data`, `filename`, and `saveryOptions` passed, and will return the same `Promise` that calling `save()` on a `saveryInstance` will.\n\n### Advanced usage\n\n`savery` uses a list of commonly-used MIME types which is intended to be comprehensive enough for most common use cases, however if you want to include the complete list of MIME types in your package you can do either of the following:\n\n* Build systems (`webpack`, `browserify`, etc.)\n  * Add the `SAVERY=full` environment variable to the command that runs your build script\n* `\u003cscript\u003e` tag\n  * Use the `savery-full` script instead of the `savery` script\n  \nThis will tell `savery` to use the [mime-types](https://github.com/jshttp/mime-types) package instead of the local list. \n\n### Supported browsers\n\n* Full support\n  * Firefox 20+\n  * Chrome\n  * Edge\n  * Internet Explorer 10+\n  * Opera 15+\n* Partial support (filenames not supported)\n  * Firefox \u003c20\n  * Safari\n  * Opera \u003c15\n* Requires [Blob polyfill](https://www.npmjs.com/package/blob-polyfill)\n  * Firefox \u003c20\n  * Opera \u003c15\n  * Safari \u003c6\n\nPlease note that it is highly recommended to include a `Promise` polyfill, as it will be needed for `savery` to work in the following environments:\n* IE 10/11\n* IE Mobile 10/11\n* Safari \u003c7.1\n* iOS Safari \u003c8\n* Android browser \u003c4.4.4\n\n### Additional information\n\nThe `data` property in `savery` will accept anything for use in the `Blob` construction, however `canvas` elements specifically need to have the data converted by the `canvas.toBlob()` method prior to calling via `savery`.\n\n### Development\n\nStandard stuff, clone the repo and `npm install` dependencies. The npm scripts available:\n* `build` =\u003e run webpack to build savery-full.js with NODE_ENV=development\n* `build:lite` =\u003e run webpack to build savery.js with NODE_ENV=development\n* `build:minifed` =\u003e run webpack to build savery-full.min.js with NODE_ENV=production\n* `build:minifed:lite` =\u003e run webpack to build savery.min.js with NODE_ENV=production\n* `dev` =\u003e run webpack dev server to run example app based on full MIME type map provided by `mime-types` package\n* `dev:lite` =\u003e run webpack dev server to run example app based on local lite MIME type map\n* `flow` =\u003e run flowtype analysis on `src` folder\n* `lint` =\u003e run ESLint against all files in the `src` folder\n* `prepublish` =\u003e run `lint`, `test`, `transpile`, `build`, and `build-minified`\n* `test` =\u003e run AVA test functions with `NODE_ENV=test`\n* `test:watch` =\u003e same as `test`, but runs persistent watcher\n* `transpile` =\u003e run babel against all files in `src` to create files in `lib`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapid7%2Fsavery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frapid7%2Fsavery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapid7%2Fsavery/lists"}