first commit
This commit is contained in:
1
node_modules/steno/.npmignore
generated
vendored
Normal file
1
node_modules/steno/.npmignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
test.js
|
||||
3
node_modules/steno/.travis.yml
generated
vendored
Normal file
3
node_modules/steno/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.12"
|
||||
22
node_modules/steno/LICENSE
generated
vendored
Normal file
22
node_modules/steno/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
79
node_modules/steno/README.md
generated
vendored
Normal file
79
node_modules/steno/README.md
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
# steno [](https://www.npmjs.org/package/steno) [](https://travis-ci.org/typicode/steno)
|
||||
|
||||
> Simple file writer with __atomic writing__ and __race condition prevention__.
|
||||
|
||||
Can be used as a drop-in replacement to `fs.writeFile()`.
|
||||
|
||||
Built on [graceful-fs](https://github.com/isaacs/node-graceful-fs) and used in [lowdb](https://github.com/typicode/lowdb).
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install steno --save
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
const steno = require('steno')
|
||||
|
||||
steno.writeFile('file.json', data, err => {
|
||||
if (err) throw err
|
||||
})
|
||||
```
|
||||
|
||||
## The problem it solves
|
||||
|
||||
### Without steno
|
||||
|
||||
Let's say you have a server and want to save data to disk:
|
||||
|
||||
```javascript
|
||||
var data = { counter: 0 }
|
||||
|
||||
server.post('/', (req, res) => {
|
||||
// Increment counter
|
||||
++data.counter
|
||||
|
||||
// Save data asynchronously
|
||||
fs.writeFile('data.json', JSON.stringify(data), err => {
|
||||
if (err) throw err
|
||||
res.end()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Now if you have many requests, for example `1000`, there's a risk that you end up with:
|
||||
|
||||
```javascript
|
||||
// In your server
|
||||
data.counter === 1000
|
||||
|
||||
// In data.json
|
||||
data.counter === 865 // ... or any other value
|
||||
```
|
||||
|
||||
Why? Because, `fs.write` doesn't guarantee that the call order will be kept. Also, if the server is killed while `data.json` is being written, the file can get corrupted.
|
||||
|
||||
### With steno
|
||||
|
||||
```javascript
|
||||
server.post('/increment', (req, res) => {
|
||||
++data.counter
|
||||
|
||||
steno.writeFile('data.json', JSON.stringify(data), err => {
|
||||
if (err) throw err
|
||||
res.end()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
With steno you'll always have the same data in your server and file. And in case of a crash, file integrity will be preserved.
|
||||
|
||||
if needed, you can also use `steno.writeFileSync()` which offers atomic writing too.
|
||||
|
||||
__Important: works only in a single instance of Node.__
|
||||
|
||||
## License
|
||||
|
||||
MIT - [Typicode](https://github.com/typicode)
|
||||
80
node_modules/steno/index.js
generated
vendored
Normal file
80
node_modules/steno/index.js
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
var fs = require('graceful-fs')
|
||||
var path = require('path')
|
||||
|
||||
var writers = {}
|
||||
|
||||
// Returns a temporary file
|
||||
// Example: for /some/file will return /some/.~file
|
||||
function getTempFile (file) {
|
||||
return path.join(path.dirname(file), '.~' + path.basename(file))
|
||||
}
|
||||
|
||||
function Writer (file) {
|
||||
this.file = file
|
||||
this.callbacks = []
|
||||
this.nextData = null
|
||||
this.nextCallbacks = []
|
||||
}
|
||||
|
||||
Writer.prototype.write = function (data, cb) {
|
||||
if (this.lock) {
|
||||
// File is locked
|
||||
// Save callback for later
|
||||
this.nextCallbacks.push(cb)
|
||||
// Set next data to be written
|
||||
this.nextData = data
|
||||
} else {
|
||||
// File is not locked
|
||||
// Lock it
|
||||
this.lock = true
|
||||
|
||||
// Write data to a temporary file
|
||||
var tmpFile = getTempFile(this.file)
|
||||
fs.writeFile(tmpFile, data, function (err) {
|
||||
if (err) {
|
||||
// On error, call all the stored callbacks and the current one
|
||||
// Then return
|
||||
while (this.callbacks.length) this.callbacks.shift()(err)
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
// On success rename the temporary file to the real file
|
||||
fs.rename(tmpFile, this.file, function (err) {
|
||||
// call all the stored callbacks and the current one
|
||||
while (this.callbacks.length) this.callbacks.shift()(err)
|
||||
cb()
|
||||
|
||||
// Unlock file
|
||||
this.lock = false
|
||||
|
||||
// Write next data if any
|
||||
if (this.nextData) {
|
||||
var data = this.nextData
|
||||
this.callbacks = this.nextCallbacks
|
||||
|
||||
this.nextData = null
|
||||
this.nextCallbacks = []
|
||||
|
||||
this.write(data, this.callbacks.pop())
|
||||
}
|
||||
}.bind(this))
|
||||
}.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.writeFile = function (file, data, cb) {
|
||||
// Convert to absolute path
|
||||
file = path.resolve(file)
|
||||
|
||||
// Create or get writer
|
||||
writers[file] = writers[file] || new Writer(file)
|
||||
|
||||
// Write
|
||||
writers[file].write(data, cb)
|
||||
}
|
||||
|
||||
module.exports.writeFileSync = function (file, data) {
|
||||
fs.writeFileSync(getTempFile(file), data)
|
||||
fs.renameSync(getTempFile(file), file)
|
||||
}
|
||||
42
node_modules/steno/package.json
generated
vendored
Normal file
42
node_modules/steno/package.json
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "steno",
|
||||
"version": "0.4.4",
|
||||
"description": "Simple file writer with race condition prevention and atomic writing",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "node test | tap-dot && standard",
|
||||
"prepush": "npm test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/typicode/steno.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fs",
|
||||
"file",
|
||||
"write",
|
||||
"writer",
|
||||
"asynchronous",
|
||||
"race",
|
||||
"condition",
|
||||
"atomic",
|
||||
"writing",
|
||||
"safe"
|
||||
],
|
||||
"author": "typicode",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/typicode/steno/issues"
|
||||
},
|
||||
"homepage": "https://github.com/typicode/steno",
|
||||
"devDependencies": {
|
||||
"after": "^0.8.1",
|
||||
"husky": "^0.11.1",
|
||||
"standard": "^6.0.7",
|
||||
"tap-dot": "^0.2.3",
|
||||
"tape": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.3"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user