Heeyeon

Add member_join function and Modify overall structure

Showing 129 changed files with 4800 additions and 31 deletions
const express = require('express')
const app = express()
const route = require('./router.js')
const bodyParser = require('body-parser')
const expressSession = require('express-session');
const passport = require('./passport.js')
const flash = require('connect-flash')
const path=require('path')
app.set('views', __dirname + '/public')
app.set('view engine', 'ejs')
app.use(express.static(__dirname+'/public'))
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())
app.use(passport.initialize())
app.use(passport.session())
app.use(flash())
app.use(expressSession({
secret: 'my Key',
resave: true,
saveUninitialized:true
}))
app.use('/', route)
app.listen(3000, () => {
console.log("3000 port is on!")
const express = require('express');
const session = require('express-session');
const passport = require('passport'), LocalStrategy = require('passport-local').Strategy;
const fs=require('fs');
const fileStore = require('session-file-store')(session);
const app = express();
//Middle Ware list
app.use(express.urlencoded({extended:false}));
app.use(session({
secret: 'secret key',
resave: false,
saveUninitialized: false,
store : new fileStore()
}));
app.use(passport.initialize());
app.use(passport.session());
//사용자 정보 세션 읽기, 쓰기
passport.serializeUser(function(user, done) { //쓰기
done(null, user.email);
});
passport.deserializeUser(function(id, done) { //읽기
done(null, id);
});
//첫 페이지
app.get('/',(req,res)=>{
let page = getFirstPage('Passport','This is Passport Example Page',authInfo(req));
res.send(page);
});
//메인 페이지
//Express에서 정적파일(ex: main.html, main.js)들을 사용할경우
//경로를 미리 제시해 주는 부분
app.get('/main',(req,res)=>{
res.sendFile(__dirname+'/main/main.html')
})
//로그인 페이지
app.get('/login',(req,res)=>{
let page = getLoginButton(`<a href="/">뒤로가기</a>`);
res.send(page);
});
//로그인 인증 (Passport)
passport.use(new LocalStrategy({
//로그인 페이지 input 태그 내 name
usernameField: 'email',
passwordField: 'password'
},
(id, password, done)=>{
console.log(id,password);
//회원 정보가 한개이상 있을때
if(user){
console.log(user);
//아이디가 다를때
if (id !== user.email)
return done(null, false, { message: '아이디가 다르다' });
//비밀번호가 다를때
else if (password !== user.password)
return done(null, false, { message: '비번이 다르다' });
//아이디, 비밀번호 모두 맞을 경우
return done(null, user);
}
}));
//로그인 처리 (Passport)
app.post('/login',
passport.authenticate('local', {
//성공시, 메인페이지 이동
//실패시 로그인 페이지 이동
successRedirect: '/',
failureRedirect: '/login'
}));
//회원가입 페이지 Get
app.get('/join',(req,res)=>{
let page = getPage('회원가입',`
<form action="/join" method="post">
<input type="email" name="email" placeholder="email"><br>
<input type="password" name="password" placeholder="****"><br>
<input type="name" name="name" placeholder="이름"><br>
<input type="submit" value="회원가입"><br>
</form>
`,'<a href="/login">뒤로가기</a>');
res.send(page);
});
//회원가입 처리 Post : 예제를 위해 간단 저장 방식으로 구현
var user = {};
app.post('/join',(req,res)=>{
user.email = req.body.email;
user.password = req.body.password;
user.name=req.body.name;
//로그인 페이지로 이동
res.redirect('/login');
});
//로그 아웃 처리
app.get('/logout',(req,res)=>{
//passport 정보 삭제
req.logout();
//서버측 세션 삭제
req.session.destroy(()=>{
//클라이언트 측 세션 암호화 쿠키 삭제
res.cookie('connect.sid','',{maxAge:0});
res.redirect('/');
});
});
//포트 연결
app.listen(3000,()=>console.log(`http://localhost:3000`));
//로그인 로그아웃 여부
const authInfo = (req)=>{
if(req.user) return `${user.name} | <a href="/logout">로그아웃</a>`;
return `<a href="/login">login</a>`;
}
//페이지 템플릿
const getPage = (title, content, auth) =>{
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Passport Example</title>
</head>
<body>
${auth}
<h1>${title}</h1>
<p>${content}</p>
</body>
</html>
`;
}
//로그인 버튼
const getLoginButton = (auth) =>{
return `
<!DOCTYPE html>
<html>
<head>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<title><%= title %></title>
</head>
<body>
${auth}
<div class="container">
<div class="row">
<div class="main">
<h3>Login</h3>
<form role="form" method="POST" action="/login">
<div class="form-group">
<label for="userId">ID</label>
<input type="text" class="form-control" id="email" name="email">
</div>
<div class="form-group">
<label for="password">비밀번호</label>
<input type="password" class="form-control" id="password" name="password">
</div>
<button type="submit" class="btn btn btn-primary">
Enter
</button>
</form>
<div>
<a href="/join" style="background : #E5E5E5;padding : 2px; border: 0.5px solid black;cursor:pointer;border-radius:3px;font-size:13px;color:black;text-decoration:none;">회원가입</a>
</div>
</div>
</div>
</div>
</body>
</html>
`;
}
//첫 페이지 화면
const getFirstPage =(title, content, auth) =>{
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Passport Example</title>
</head>
<body>
${auth}
<h1>${title}</h1>
<p>${content}</p>
<div>
<input type="button" value="page move" onClick="movepage()"/>
</div>
<script type="text/javascript">
function movepage(){
location.href="main";
}</script>
</body>
</html>
`;
}
\ No newline at end of file
......
This diff is collapsed. Click to expand it.
module.exports = {
'env': {
'browser': false,
'commonjs': true,
'es6': true,
'node': true
},
'extends': 'eslint:recommended',
'rules': {
'indent': [
'error',
2
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
]
}
};
MIT License
Copyright (c) 2017 Fedor Indutny
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.
# ASN1.js
ASN.1 DER Encoder/Decoder and DSL.
## Example
Define model:
```javascript
var asn = require('asn1.js');
var Human = asn.define('Human', function() {
this.seq().obj(
this.key('firstName').octstr(),
this.key('lastName').octstr(),
this.key('age').int(),
this.key('gender').enum({ 0: 'male', 1: 'female' }),
this.key('bio').seqof(Bio)
);
});
var Bio = asn.define('Bio', function() {
this.seq().obj(
this.key('time').gentime(),
this.key('description').octstr()
);
});
```
Encode data:
```javascript
var output = Human.encode({
firstName: 'Thomas',
lastName: 'Anderson',
age: 28,
gender: 'male',
bio: [
{
time: +new Date('31 March 1999'),
description: 'freedom of mind'
}
]
}, 'der');
```
Decode data:
```javascript
var human = Human.decode(output, 'der');
console.log(human);
/*
{ firstName: <Buffer 54 68 6f 6d 61 73>,
lastName: <Buffer 41 6e 64 65 72 73 6f 6e>,
age: 28,
gender: 'male',
bio:
[ { time: 922820400000,
description: <Buffer 66 72 65 65 64 6f 6d 20 6f 66 20 6d 69 6e 64> } ] }
*/
```
### Partial decode
Its possible to parse data without stopping on first error. In order to do it,
you should call:
```javascript
var human = Human.decode(output, 'der', { partial: true });
console.log(human);
/*
{ result: { ... },
errors: [ ... ] }
*/
```
#### LICENSE
This software is licensed under the MIT License.
Copyright Fedor Indutny, 2017.
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.
'use strict';
const asn1 = exports;
asn1.bignum = require('bn.js');
asn1.define = require('./asn1/api').define;
asn1.base = require('./asn1/base');
asn1.constants = require('./asn1/constants');
asn1.decoders = require('./asn1/decoders');
asn1.encoders = require('./asn1/encoders');
'use strict';
const encoders = require('./encoders');
const decoders = require('./decoders');
const inherits = require('inherits');
const api = exports;
api.define = function define(name, body) {
return new Entity(name, body);
};
function Entity(name, body) {
this.name = name;
this.body = body;
this.decoders = {};
this.encoders = {};
}
Entity.prototype._createNamed = function createNamed(Base) {
const name = this.name;
function Generated(entity) {
this._initNamed(entity, name);
}
inherits(Generated, Base);
Generated.prototype._initNamed = function _initNamed(entity, name) {
Base.call(this, entity, name);
};
return new Generated(this);
};
Entity.prototype._getDecoder = function _getDecoder(enc) {
enc = enc || 'der';
// Lazily create decoder
if (!this.decoders.hasOwnProperty(enc))
this.decoders[enc] = this._createNamed(decoders[enc]);
return this.decoders[enc];
};
Entity.prototype.decode = function decode(data, enc, options) {
return this._getDecoder(enc).decode(data, options);
};
Entity.prototype._getEncoder = function _getEncoder(enc) {
enc = enc || 'der';
// Lazily create encoder
if (!this.encoders.hasOwnProperty(enc))
this.encoders[enc] = this._createNamed(encoders[enc]);
return this.encoders[enc];
};
Entity.prototype.encode = function encode(data, enc, /* internal */ reporter) {
return this._getEncoder(enc).encode(data, reporter);
};
'use strict';
const inherits = require('inherits');
const Reporter = require('../base/reporter').Reporter;
const Buffer = require('safer-buffer').Buffer;
function DecoderBuffer(base, options) {
Reporter.call(this, options);
if (!Buffer.isBuffer(base)) {
this.error('Input not Buffer');
return;
}
this.base = base;
this.offset = 0;
this.length = base.length;
}
inherits(DecoderBuffer, Reporter);
exports.DecoderBuffer = DecoderBuffer;
DecoderBuffer.isDecoderBuffer = function isDecoderBuffer(data) {
if (data instanceof DecoderBuffer) {
return true;
}
// Or accept compatible API
const isCompatible = typeof data === 'object' &&
Buffer.isBuffer(data.base) &&
data.constructor.name === 'DecoderBuffer' &&
typeof data.offset === 'number' &&
typeof data.length === 'number' &&
typeof data.save === 'function' &&
typeof data.restore === 'function' &&
typeof data.isEmpty === 'function' &&
typeof data.readUInt8 === 'function' &&
typeof data.skip === 'function' &&
typeof data.raw === 'function';
return isCompatible;
};
DecoderBuffer.prototype.save = function save() {
return { offset: this.offset, reporter: Reporter.prototype.save.call(this) };
};
DecoderBuffer.prototype.restore = function restore(save) {
// Return skipped data
const res = new DecoderBuffer(this.base);
res.offset = save.offset;
res.length = this.offset;
this.offset = save.offset;
Reporter.prototype.restore.call(this, save.reporter);
return res;
};
DecoderBuffer.prototype.isEmpty = function isEmpty() {
return this.offset === this.length;
};
DecoderBuffer.prototype.readUInt8 = function readUInt8(fail) {
if (this.offset + 1 <= this.length)
return this.base.readUInt8(this.offset++, true);
else
return this.error(fail || 'DecoderBuffer overrun');
};
DecoderBuffer.prototype.skip = function skip(bytes, fail) {
if (!(this.offset + bytes <= this.length))
return this.error(fail || 'DecoderBuffer overrun');
const res = new DecoderBuffer(this.base);
// Share reporter state
res._reporterState = this._reporterState;
res.offset = this.offset;
res.length = this.offset + bytes;
this.offset += bytes;
return res;
};
DecoderBuffer.prototype.raw = function raw(save) {
return this.base.slice(save ? save.offset : this.offset, this.length);
};
function EncoderBuffer(value, reporter) {
if (Array.isArray(value)) {
this.length = 0;
this.value = value.map(function(item) {
if (!EncoderBuffer.isEncoderBuffer(item))
item = new EncoderBuffer(item, reporter);
this.length += item.length;
return item;
}, this);
} else if (typeof value === 'number') {
if (!(0 <= value && value <= 0xff))
return reporter.error('non-byte EncoderBuffer value');
this.value = value;
this.length = 1;
} else if (typeof value === 'string') {
this.value = value;
this.length = Buffer.byteLength(value);
} else if (Buffer.isBuffer(value)) {
this.value = value;
this.length = value.length;
} else {
return reporter.error('Unsupported type: ' + typeof value);
}
}
exports.EncoderBuffer = EncoderBuffer;
EncoderBuffer.isEncoderBuffer = function isEncoderBuffer(data) {
if (data instanceof EncoderBuffer) {
return true;
}
// Or accept compatible API
const isCompatible = typeof data === 'object' &&
data.constructor.name === 'EncoderBuffer' &&
typeof data.length === 'number' &&
typeof data.join === 'function';
return isCompatible;
};
EncoderBuffer.prototype.join = function join(out, offset) {
if (!out)
out = Buffer.alloc(this.length);
if (!offset)
offset = 0;
if (this.length === 0)
return out;
if (Array.isArray(this.value)) {
this.value.forEach(function(item) {
item.join(out, offset);
offset += item.length;
});
} else {
if (typeof this.value === 'number')
out[offset] = this.value;
else if (typeof this.value === 'string')
out.write(this.value, offset);
else if (Buffer.isBuffer(this.value))
this.value.copy(out, offset);
offset += this.length;
}
return out;
};
'use strict';
const base = exports;
base.Reporter = require('./reporter').Reporter;
base.DecoderBuffer = require('./buffer').DecoderBuffer;
base.EncoderBuffer = require('./buffer').EncoderBuffer;
base.Node = require('./node');
This diff is collapsed. Click to expand it.
'use strict';
const inherits = require('inherits');
function Reporter(options) {
this._reporterState = {
obj: null,
path: [],
options: options || {},
errors: []
};
}
exports.Reporter = Reporter;
Reporter.prototype.isError = function isError(obj) {
return obj instanceof ReporterError;
};
Reporter.prototype.save = function save() {
const state = this._reporterState;
return { obj: state.obj, pathLen: state.path.length };
};
Reporter.prototype.restore = function restore(data) {
const state = this._reporterState;
state.obj = data.obj;
state.path = state.path.slice(0, data.pathLen);
};
Reporter.prototype.enterKey = function enterKey(key) {
return this._reporterState.path.push(key);
};
Reporter.prototype.exitKey = function exitKey(index) {
const state = this._reporterState;
state.path = state.path.slice(0, index - 1);
};
Reporter.prototype.leaveKey = function leaveKey(index, key, value) {
const state = this._reporterState;
this.exitKey(index);
if (state.obj !== null)
state.obj[key] = value;
};
Reporter.prototype.path = function path() {
return this._reporterState.path.join('/');
};
Reporter.prototype.enterObject = function enterObject() {
const state = this._reporterState;
const prev = state.obj;
state.obj = {};
return prev;
};
Reporter.prototype.leaveObject = function leaveObject(prev) {
const state = this._reporterState;
const now = state.obj;
state.obj = prev;
return now;
};
Reporter.prototype.error = function error(msg) {
let err;
const state = this._reporterState;
const inherited = msg instanceof ReporterError;
if (inherited) {
err = msg;
} else {
err = new ReporterError(state.path.map(function(elem) {
return '[' + JSON.stringify(elem) + ']';
}).join(''), msg.message || msg, msg.stack);
}
if (!state.options.partial)
throw err;
if (!inherited)
state.errors.push(err);
return err;
};
Reporter.prototype.wrapResult = function wrapResult(result) {
const state = this._reporterState;
if (!state.options.partial)
return result;
return {
result: this.isError(result) ? null : result,
errors: state.errors
};
};
function ReporterError(path, msg) {
this.path = path;
this.rethrow(msg);
}
inherits(ReporterError, Error);
ReporterError.prototype.rethrow = function rethrow(msg) {
this.message = msg + ' at: ' + (this.path || '(shallow)');
if (Error.captureStackTrace)
Error.captureStackTrace(this, ReporterError);
if (!this.stack) {
try {
// IE only adds stack when thrown
throw new Error(this.message);
} catch (e) {
this.stack = e.stack;
}
}
return this;
};
'use strict';
// Helper
function reverse(map) {
const res = {};
Object.keys(map).forEach(function(key) {
// Convert key to integer if it is stringified
if ((key | 0) == key)
key = key | 0;
const value = map[key];
res[value] = key;
});
return res;
}
exports.tagClass = {
0: 'universal',
1: 'application',
2: 'context',
3: 'private'
};
exports.tagClassByName = reverse(exports.tagClass);
exports.tag = {
0x00: 'end',
0x01: 'bool',
0x02: 'int',
0x03: 'bitstr',
0x04: 'octstr',
0x05: 'null_',
0x06: 'objid',
0x07: 'objDesc',
0x08: 'external',
0x09: 'real',
0x0a: 'enum',
0x0b: 'embed',
0x0c: 'utf8str',
0x0d: 'relativeOid',
0x10: 'seq',
0x11: 'set',
0x12: 'numstr',
0x13: 'printstr',
0x14: 't61str',
0x15: 'videostr',
0x16: 'ia5str',
0x17: 'utctime',
0x18: 'gentime',
0x19: 'graphstr',
0x1a: 'iso646str',
0x1b: 'genstr',
0x1c: 'unistr',
0x1d: 'charstr',
0x1e: 'bmpstr'
};
exports.tagByName = reverse(exports.tag);
'use strict';
const constants = exports;
// Helper
constants._reverse = function reverse(map) {
const res = {};
Object.keys(map).forEach(function(key) {
// Convert key to integer if it is stringified
if ((key | 0) == key)
key = key | 0;
const value = map[key];
res[value] = key;
});
return res;
};
constants.der = require('./der');
'use strict';
const inherits = require('inherits');
const bignum = require('bn.js');
const DecoderBuffer = require('../base/buffer').DecoderBuffer;
const Node = require('../base/node');
// Import DER constants
const der = require('../constants/der');
function DERDecoder(entity) {
this.enc = 'der';
this.name = entity.name;
this.entity = entity;
// Construct base tree
this.tree = new DERNode();
this.tree._init(entity.body);
}
module.exports = DERDecoder;
DERDecoder.prototype.decode = function decode(data, options) {
if (!DecoderBuffer.isDecoderBuffer(data)) {
data = new DecoderBuffer(data, options);
}
return this.tree._decode(data, options);
};
// Tree methods
function DERNode(parent) {
Node.call(this, 'der', parent);
}
inherits(DERNode, Node);
DERNode.prototype._peekTag = function peekTag(buffer, tag, any) {
if (buffer.isEmpty())
return false;
const state = buffer.save();
const decodedTag = derDecodeTag(buffer, 'Failed to peek tag: "' + tag + '"');
if (buffer.isError(decodedTag))
return decodedTag;
buffer.restore(state);
return decodedTag.tag === tag || decodedTag.tagStr === tag ||
(decodedTag.tagStr + 'of') === tag || any;
};
DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) {
const decodedTag = derDecodeTag(buffer,
'Failed to decode tag of "' + tag + '"');
if (buffer.isError(decodedTag))
return decodedTag;
let len = derDecodeLen(buffer,
decodedTag.primitive,
'Failed to get length of "' + tag + '"');
// Failure
if (buffer.isError(len))
return len;
if (!any &&
decodedTag.tag !== tag &&
decodedTag.tagStr !== tag &&
decodedTag.tagStr + 'of' !== tag) {
return buffer.error('Failed to match tag: "' + tag + '"');
}
if (decodedTag.primitive || len !== null)
return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
// Indefinite length... find END tag
const state = buffer.save();
const res = this._skipUntilEnd(
buffer,
'Failed to skip indefinite length body: "' + this.tag + '"');
if (buffer.isError(res))
return res;
len = buffer.offset - state.offset;
buffer.restore(state);
return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
};
DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) {
for (;;) {
const tag = derDecodeTag(buffer, fail);
if (buffer.isError(tag))
return tag;
const len = derDecodeLen(buffer, tag.primitive, fail);
if (buffer.isError(len))
return len;
let res;
if (tag.primitive || len !== null)
res = buffer.skip(len);
else
res = this._skipUntilEnd(buffer, fail);
// Failure
if (buffer.isError(res))
return res;
if (tag.tagStr === 'end')
break;
}
};
DERNode.prototype._decodeList = function decodeList(buffer, tag, decoder,
options) {
const result = [];
while (!buffer.isEmpty()) {
const possibleEnd = this._peekTag(buffer, 'end');
if (buffer.isError(possibleEnd))
return possibleEnd;
const res = decoder.decode(buffer, 'der', options);
if (buffer.isError(res) && possibleEnd)
break;
result.push(res);
}
return result;
};
DERNode.prototype._decodeStr = function decodeStr(buffer, tag) {
if (tag === 'bitstr') {
const unused = buffer.readUInt8();
if (buffer.isError(unused))
return unused;
return { unused: unused, data: buffer.raw() };
} else if (tag === 'bmpstr') {
const raw = buffer.raw();
if (raw.length % 2 === 1)
return buffer.error('Decoding of string type: bmpstr length mismatch');
let str = '';
for (let i = 0; i < raw.length / 2; i++) {
str += String.fromCharCode(raw.readUInt16BE(i * 2));
}
return str;
} else if (tag === 'numstr') {
const numstr = buffer.raw().toString('ascii');
if (!this._isNumstr(numstr)) {
return buffer.error('Decoding of string type: ' +
'numstr unsupported characters');
}
return numstr;
} else if (tag === 'octstr') {
return buffer.raw();
} else if (tag === 'objDesc') {
return buffer.raw();
} else if (tag === 'printstr') {
const printstr = buffer.raw().toString('ascii');
if (!this._isPrintstr(printstr)) {
return buffer.error('Decoding of string type: ' +
'printstr unsupported characters');
}
return printstr;
} else if (/str$/.test(tag)) {
return buffer.raw().toString();
} else {
return buffer.error('Decoding of string type: ' + tag + ' unsupported');
}
};
DERNode.prototype._decodeObjid = function decodeObjid(buffer, values, relative) {
let result;
const identifiers = [];
let ident = 0;
let subident = 0;
while (!buffer.isEmpty()) {
subident = buffer.readUInt8();
ident <<= 7;
ident |= subident & 0x7f;
if ((subident & 0x80) === 0) {
identifiers.push(ident);
ident = 0;
}
}
if (subident & 0x80)
identifiers.push(ident);
const first = (identifiers[0] / 40) | 0;
const second = identifiers[0] % 40;
if (relative)
result = identifiers;
else
result = [first, second].concat(identifiers.slice(1));
if (values) {
let tmp = values[result.join(' ')];
if (tmp === undefined)
tmp = values[result.join('.')];
if (tmp !== undefined)
result = tmp;
}
return result;
};
DERNode.prototype._decodeTime = function decodeTime(buffer, tag) {
const str = buffer.raw().toString();
let year;
let mon;
let day;
let hour;
let min;
let sec;
if (tag === 'gentime') {
year = str.slice(0, 4) | 0;
mon = str.slice(4, 6) | 0;
day = str.slice(6, 8) | 0;
hour = str.slice(8, 10) | 0;
min = str.slice(10, 12) | 0;
sec = str.slice(12, 14) | 0;
} else if (tag === 'utctime') {
year = str.slice(0, 2) | 0;
mon = str.slice(2, 4) | 0;
day = str.slice(4, 6) | 0;
hour = str.slice(6, 8) | 0;
min = str.slice(8, 10) | 0;
sec = str.slice(10, 12) | 0;
if (year < 70)
year = 2000 + year;
else
year = 1900 + year;
} else {
return buffer.error('Decoding ' + tag + ' time is not supported yet');
}
return Date.UTC(year, mon - 1, day, hour, min, sec, 0);
};
DERNode.prototype._decodeNull = function decodeNull() {
return null;
};
DERNode.prototype._decodeBool = function decodeBool(buffer) {
const res = buffer.readUInt8();
if (buffer.isError(res))
return res;
else
return res !== 0;
};
DERNode.prototype._decodeInt = function decodeInt(buffer, values) {
// Bigint, return as it is (assume big endian)
const raw = buffer.raw();
let res = new bignum(raw);
if (values)
res = values[res.toString(10)] || res;
return res;
};
DERNode.prototype._use = function use(entity, obj) {
if (typeof entity === 'function')
entity = entity(obj);
return entity._getDecoder('der').tree;
};
// Utility methods
function derDecodeTag(buf, fail) {
let tag = buf.readUInt8(fail);
if (buf.isError(tag))
return tag;
const cls = der.tagClass[tag >> 6];
const primitive = (tag & 0x20) === 0;
// Multi-octet tag - load
if ((tag & 0x1f) === 0x1f) {
let oct = tag;
tag = 0;
while ((oct & 0x80) === 0x80) {
oct = buf.readUInt8(fail);
if (buf.isError(oct))
return oct;
tag <<= 7;
tag |= oct & 0x7f;
}
} else {
tag &= 0x1f;
}
const tagStr = der.tag[tag];
return {
cls: cls,
primitive: primitive,
tag: tag,
tagStr: tagStr
};
}
function derDecodeLen(buf, primitive, fail) {
let len = buf.readUInt8(fail);
if (buf.isError(len))
return len;
// Indefinite form
if (!primitive && len === 0x80)
return null;
// Definite form
if ((len & 0x80) === 0) {
// Short form
return len;
}
// Long form
const num = len & 0x7f;
if (num > 4)
return buf.error('length octect is too long');
len = 0;
for (let i = 0; i < num; i++) {
len <<= 8;
const j = buf.readUInt8(fail);
if (buf.isError(j))
return j;
len |= j;
}
return len;
}
'use strict';
const decoders = exports;
decoders.der = require('./der');
decoders.pem = require('./pem');
'use strict';
const inherits = require('inherits');
const Buffer = require('safer-buffer').Buffer;
const DERDecoder = require('./der');
function PEMDecoder(entity) {
DERDecoder.call(this, entity);
this.enc = 'pem';
}
inherits(PEMDecoder, DERDecoder);
module.exports = PEMDecoder;
PEMDecoder.prototype.decode = function decode(data, options) {
const lines = data.toString().split(/[\r\n]+/g);
const label = options.label.toUpperCase();
const re = /^-----(BEGIN|END) ([^-]+)-----$/;
let start = -1;
let end = -1;
for (let i = 0; i < lines.length; i++) {
const match = lines[i].match(re);
if (match === null)
continue;
if (match[2] !== label)
continue;
if (start === -1) {
if (match[1] !== 'BEGIN')
break;
start = i;
} else {
if (match[1] !== 'END')
break;
end = i;
break;
}
}
if (start === -1 || end === -1)
throw new Error('PEM section not found for: ' + label);
const base64 = lines.slice(start + 1, end).join('');
// Remove excessive symbols
base64.replace(/[^a-z0-9+/=]+/gi, '');
const input = Buffer.from(base64, 'base64');
return DERDecoder.prototype.decode.call(this, input, options);
};
'use strict';
const inherits = require('inherits');
const Buffer = require('safer-buffer').Buffer;
const Node = require('../base/node');
// Import DER constants
const der = require('../constants/der');
function DEREncoder(entity) {
this.enc = 'der';
this.name = entity.name;
this.entity = entity;
// Construct base tree
this.tree = new DERNode();
this.tree._init(entity.body);
}
module.exports = DEREncoder;
DEREncoder.prototype.encode = function encode(data, reporter) {
return this.tree._encode(data, reporter).join();
};
// Tree methods
function DERNode(parent) {
Node.call(this, 'der', parent);
}
inherits(DERNode, Node);
DERNode.prototype._encodeComposite = function encodeComposite(tag,
primitive,
cls,
content) {
const encodedTag = encodeTag(tag, primitive, cls, this.reporter);
// Short form
if (content.length < 0x80) {
const header = Buffer.alloc(2);
header[0] = encodedTag;
header[1] = content.length;
return this._createEncoderBuffer([ header, content ]);
}
// Long form
// Count octets required to store length
let lenOctets = 1;
for (let i = content.length; i >= 0x100; i >>= 8)
lenOctets++;
const header = Buffer.alloc(1 + 1 + lenOctets);
header[0] = encodedTag;
header[1] = 0x80 | lenOctets;
for (let i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8)
header[i] = j & 0xff;
return this._createEncoderBuffer([ header, content ]);
};
DERNode.prototype._encodeStr = function encodeStr(str, tag) {
if (tag === 'bitstr') {
return this._createEncoderBuffer([ str.unused | 0, str.data ]);
} else if (tag === 'bmpstr') {
const buf = Buffer.alloc(str.length * 2);
for (let i = 0; i < str.length; i++) {
buf.writeUInt16BE(str.charCodeAt(i), i * 2);
}
return this._createEncoderBuffer(buf);
} else if (tag === 'numstr') {
if (!this._isNumstr(str)) {
return this.reporter.error('Encoding of string type: numstr supports ' +
'only digits and space');
}
return this._createEncoderBuffer(str);
} else if (tag === 'printstr') {
if (!this._isPrintstr(str)) {
return this.reporter.error('Encoding of string type: printstr supports ' +
'only latin upper and lower case letters, ' +
'digits, space, apostrophe, left and rigth ' +
'parenthesis, plus sign, comma, hyphen, ' +
'dot, slash, colon, equal sign, ' +
'question mark');
}
return this._createEncoderBuffer(str);
} else if (/str$/.test(tag)) {
return this._createEncoderBuffer(str);
} else if (tag === 'objDesc') {
return this._createEncoderBuffer(str);
} else {
return this.reporter.error('Encoding of string type: ' + tag +
' unsupported');
}
};
DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) {
if (typeof id === 'string') {
if (!values)
return this.reporter.error('string objid given, but no values map found');
if (!values.hasOwnProperty(id))
return this.reporter.error('objid not found in values map');
id = values[id].split(/[\s.]+/g);
for (let i = 0; i < id.length; i++)
id[i] |= 0;
} else if (Array.isArray(id)) {
id = id.slice();
for (let i = 0; i < id.length; i++)
id[i] |= 0;
}
if (!Array.isArray(id)) {
return this.reporter.error('objid() should be either array or string, ' +
'got: ' + JSON.stringify(id));
}
if (!relative) {
if (id[1] >= 40)
return this.reporter.error('Second objid identifier OOB');
id.splice(0, 2, id[0] * 40 + id[1]);
}
// Count number of octets
let size = 0;
for (let i = 0; i < id.length; i++) {
let ident = id[i];
for (size++; ident >= 0x80; ident >>= 7)
size++;
}
const objid = Buffer.alloc(size);
let offset = objid.length - 1;
for (let i = id.length - 1; i >= 0; i--) {
let ident = id[i];
objid[offset--] = ident & 0x7f;
while ((ident >>= 7) > 0)
objid[offset--] = 0x80 | (ident & 0x7f);
}
return this._createEncoderBuffer(objid);
};
function two(num) {
if (num < 10)
return '0' + num;
else
return num;
}
DERNode.prototype._encodeTime = function encodeTime(time, tag) {
let str;
const date = new Date(time);
if (tag === 'gentime') {
str = [
two(date.getUTCFullYear()),
two(date.getUTCMonth() + 1),
two(date.getUTCDate()),
two(date.getUTCHours()),
two(date.getUTCMinutes()),
two(date.getUTCSeconds()),
'Z'
].join('');
} else if (tag === 'utctime') {
str = [
two(date.getUTCFullYear() % 100),
two(date.getUTCMonth() + 1),
two(date.getUTCDate()),
two(date.getUTCHours()),
two(date.getUTCMinutes()),
two(date.getUTCSeconds()),
'Z'
].join('');
} else {
this.reporter.error('Encoding ' + tag + ' time is not supported yet');
}
return this._encodeStr(str, 'octstr');
};
DERNode.prototype._encodeNull = function encodeNull() {
return this._createEncoderBuffer('');
};
DERNode.prototype._encodeInt = function encodeInt(num, values) {
if (typeof num === 'string') {
if (!values)
return this.reporter.error('String int or enum given, but no values map');
if (!values.hasOwnProperty(num)) {
return this.reporter.error('Values map doesn\'t contain: ' +
JSON.stringify(num));
}
num = values[num];
}
// Bignum, assume big endian
if (typeof num !== 'number' && !Buffer.isBuffer(num)) {
const numArray = num.toArray();
if (!num.sign && numArray[0] & 0x80) {
numArray.unshift(0);
}
num = Buffer.from(numArray);
}
if (Buffer.isBuffer(num)) {
let size = num.length;
if (num.length === 0)
size++;
const out = Buffer.alloc(size);
num.copy(out);
if (num.length === 0)
out[0] = 0;
return this._createEncoderBuffer(out);
}
if (num < 0x80)
return this._createEncoderBuffer(num);
if (num < 0x100)
return this._createEncoderBuffer([0, num]);
let size = 1;
for (let i = num; i >= 0x100; i >>= 8)
size++;
const out = new Array(size);
for (let i = out.length - 1; i >= 0; i--) {
out[i] = num & 0xff;
num >>= 8;
}
if(out[0] & 0x80) {
out.unshift(0);
}
return this._createEncoderBuffer(Buffer.from(out));
};
DERNode.prototype._encodeBool = function encodeBool(value) {
return this._createEncoderBuffer(value ? 0xff : 0);
};
DERNode.prototype._use = function use(entity, obj) {
if (typeof entity === 'function')
entity = entity(obj);
return entity._getEncoder('der').tree;
};
DERNode.prototype._skipDefault = function skipDefault(dataBuffer, reporter, parent) {
const state = this._baseState;
let i;
if (state['default'] === null)
return false;
const data = dataBuffer.join();
if (state.defaultBuffer === undefined)
state.defaultBuffer = this._encodeValue(state['default'], reporter, parent).join();
if (data.length !== state.defaultBuffer.length)
return false;
for (i=0; i < data.length; i++)
if (data[i] !== state.defaultBuffer[i])
return false;
return true;
};
// Utility methods
function encodeTag(tag, primitive, cls, reporter) {
let res;
if (tag === 'seqof')
tag = 'seq';
else if (tag === 'setof')
tag = 'set';
if (der.tagByName.hasOwnProperty(tag))
res = der.tagByName[tag];
else if (typeof tag === 'number' && (tag | 0) === tag)
res = tag;
else
return reporter.error('Unknown tag: ' + tag);
if (res >= 0x1f)
return reporter.error('Multi-octet tag encoding unsupported');
if (!primitive)
res |= 0x20;
res |= (der.tagClassByName[cls || 'universal'] << 6);
return res;
}
'use strict';
const encoders = exports;
encoders.der = require('./der');
encoders.pem = require('./pem');
'use strict';
const inherits = require('inherits');
const DEREncoder = require('./der');
function PEMEncoder(entity) {
DEREncoder.call(this, entity);
this.enc = 'pem';
}
inherits(PEMEncoder, DEREncoder);
module.exports = PEMEncoder;
PEMEncoder.prototype.encode = function encode(data, options) {
const buf = DEREncoder.prototype.encode.call(this, data);
const p = buf.toString('base64');
const out = [ '-----BEGIN ' + options.label + '-----' ];
for (let i = 0; i < p.length; i += 64)
out.push(p.slice(i, i + 64));
out.push('-----END ' + options.label + '-----');
return out.join('\n');
};
{
"name": "asn1.js",
"version": "5.4.1",
"description": "ASN.1 encoder and decoder",
"main": "lib/asn1.js",
"scripts": {
"lint-2560": "eslint --fix rfc/2560/*.js rfc/2560/test/*.js",
"lint-5280": "eslint --fix rfc/5280/*.js rfc/5280/test/*.js",
"lint": "eslint --fix lib/*.js lib/**/*.js lib/**/**/*.js && npm run lint-2560 && npm run lint-5280",
"test": "mocha --reporter spec test/*-test.js && cd rfc/2560 && npm i && npm test && cd ../../rfc/5280 && npm i && npm test && cd ../../ && npm run lint"
},
"repository": {
"type": "git",
"url": "git@github.com:indutny/asn1.js"
},
"keywords": [
"asn.1",
"der"
],
"author": "Fedor Indutny",
"license": "MIT",
"bugs": {
"url": "https://github.com/indutny/asn1.js/issues"
},
"homepage": "https://github.com/indutny/asn1.js",
"devDependencies": {
"eslint": "^4.10.0",
"mocha": "^7.0.0"
},
"dependencies": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
}
}
{
"predef": [
"document",
"module",
"require",
"__dirname",
"process",
"console",
"it",
"xit",
"describe",
"xdescribe",
"before",
"beforeEach",
"after",
"afterEach"
],
"node": true,
"es5": true,
"bitwise": true,
"curly": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"plusplus": false,
"undef": true,
"strict": false,
"trailing": false,
"globalstrict": true,
"nonstandard": true,
"white": true,
"indent": 2,
"expr": true,
"multistr": true,
"onevar": false,
"unused": "vars",
"swindent": false
}
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
coverage.html
language: node_js
node_js:
- "0.8"
- "0.10"
Copyright (c) 2012 Jackson Tian
http://weibo.com/shyvo
The MIT License
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.
TESTS = test/*.test.js
REPORTER = spec
TIMEOUT = 2000
JSCOVERAGE = ./node_modules/.bin/jscover
test:
@NODE_ENV=test ./node_modules/mocha/bin/mocha \
--reporter $(REPORTER) \
--timeout $(TIMEOUT) \
$(MOCHA_OPTS) \
$(TESTS)
test-cov:
@$(MAKE) test REPORTER=dot
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html
@$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov
test-all: test test-cov
clean:
@rm -rf node_modules
.PHONY: test test-cov test-all clean
Bagpipe [中文](https://github.com/JacksonTian/bagpipe/blob/master/README_CN.md)
=======
You are the bagpiper.
## Introduction
It is convenient for us to use asynchronism or concurrent to promote our business speed in Node. While, if the amount of concurrent is too large, our server may not support, such that we need to limit the amount of concurrent. Though, the http module contains [http.Agent](http://nodejs.org/docs/latest/api/http.html#http_class_http_agent) to control the amount of sockets, usually, our asynchronous API has packaged in advance. It is not realistic to change the inner API agent, let’s realize it on our own logical layer.
## Installation
```
npm install bagpipe
```
## API
The APIs exposed by Bagpipe only include constructor and instance methods `push`.
Under original status, we may execute concurrent like this, forming 100 concurrent asynchronous invokes.
```
for (var i = 0; i < 100; i++) {
  async(function () {
    // Asynchronous call
  });
}
```
If need to limit concurrency, what is your solution?
Solution from Bagpipe as follows:
```
var Bagpipe = require('bagpipe');
// Sets the max concurrency as 100
var bagpipe = new Bagpipe(10);
for (var i = 0; i < 100; i++) {
bagpipe.push(async, function () {
// execute asynchronous callback
});
}
```
Yes, invoke method only splits method、parameter and callback, then delivery it to bagpipe through `push`.
How does it like compared with your anticipated solution?
### Options
- `refuse`, when queue is fulled, bagpipe will refuse the new async call and execute the callback with a `TooMuchAsyncCallError` exception. default `false`.
- `timeout`, setting global ansyn call timeout. If async call doesn't complete in time, will execute the callback with `BagpipeTimeoutError` exception. default `null`.
## Principles
Bagpipe delivers invoke into inner queue through `push`. If active invoke amount is less than max concurrent, it will be popped and executed directly, or it will stay in the queue. When an asynchronous invoke ends, a invoke in the head of the queue will be popped and executed, such that assures active asynchronous invoke amount no larger than restricted value.
When the queue length is larger than 1, Bagpipe object will fire its `full` event, which delivers the queue length value. The value helps to assess business performance. For example:
```
bagpipe.on('full', function (length) {
console.warn('Button system cannot deal on time, queue jam, current queue length is:’+ length);
});
```
If queue length more than limit, you can set the `refuse` option to decide continue in queue or refuse call. The `refuse` default `false`. If set as `true`, the `TooMuchAsyncCallError` exception will pass to callback directly:
```
var bagpipe = new BagPipe(10, {
refuse: true
});
```
If complete the async call is unexpected, the queue will not balanced. Set the timeout, let the callback executed with the `BagpipeTimeoutError` exception:
```
var bagpipe = new BagPipe(10, {
timeout: 1000
});
```
## Module status
The unit testing status: [![Build Status](https://secure.travis-ci.org/JacksonTian/bagpipe.png)](http://travis-ci.org/JacksonTian/bagpipe). Unit test coverage [100%](http://html5ify.com/bagpipe/coverage.html).
## Best Practices
- Ensure that the last parameter of the asynchronous invoke is callback.
- Listen to the `full` event, adding your business performance assessment.
- Current asynchronous method has not supported context yet. Ensure that there is no `this` reference in asynchronous method. If there is `this` reference in asynchronous method, please use `bind` pass into correct context.
- Asynchronous invoke should process method to deal with timeout, it should ensure the invoke will return in a certain time no matter whether the business has been finished or not.
## Real case
When you want to traverse file directories, asynchrony can ensure `full` use of IO. You can invoke thousands of file reading easily. But, system file descriptors are limited. If disobedient, read this article again when occurring errors as follows.
```
Error: EMFILE, too many open files
```
Someone may consider dealing it with synchronous method. But, when synchronous, CPU and IO cannot be used concurrently, performance is an indefeasible index under certain condition. You can enjoy concurrent easily, as well as limit concurrent with Bagpipe.
```
var bagpipe = new Bagpipe(10);
var files = ['Here are many files'];
for (var i = 0; i < files.length; i++) {
// fs.readFile(files[i], 'utf-8', function (err, data) {
bagpipe.push(fs.readFile, files[i], 'utf-8', function (err, data) {
// won’t occur error because of too many file descriptors
// well done
});
}
```
## License
Released under the license of [MIT](https://github.com/JacksonTian/bagpipe/blob/master/MIT-License), welcome to enjoy open source.
Bagpipe(风笛) [English](https://github.com/JacksonTian/bagpipe/blob/master/README.md)
=======
You are the bagpiper.
## 起源
在Node中我们可以十分方便利用异步和并行来提升我们的业务速度。但是,如果并发量过大,我们的服务器却可能吃不消,我们需要限制并发量。尽管`http`模块自身有[http.Agent](http://nodejs.org/docs/latest/api/http.html#http_class_http_agent)这样的玩意,用于控制socket的数量,但是通常我们的异步API早就封装好了。改动API的内部agent是不现实的,那么我们自己在逻辑层实现吧。
## 安装
```
npm install bagpipe
```
## API
`Bagpipe`暴露的API只有构造器和实例方法`push`
在原始状态下,我们执行并发可能是如下这样的,这会形成100个并发异步调用。
```
for (var i = 0; i < 100; i++) {
async(function () {
// 异步调用
});
}
```
如果需要限制并发,你的方案会是怎样?
`Bagpipe`的方案是如下这样的:
```
var Bagpipe = require('bagpipe');
// 设定最大并发数为10
var bagpipe = new Bagpipe(10);
for (var i = 0; i < 100; i++) {
bagpipe.push(async, function () {
// 异步回调执行
});
}
```
是的,调用方式仅仅是将方法、参数、回调分拆一下通过`push`交给`bagpipe`即可。
这个方案与你预想的方案相比,如何?
### 选项
- `refuse`,当队列填满时,拒绝新到来的异步调用。执行异步调用的回调函数,传递一个`TooMuchAsyncCallError`异常。默认为`false`
- `timeout`,设置全局异步调用超时时间,经过`push`后执行的异步调用,如果在超时时间内没有返回执行,将会执行异步调用的回调函数,传递一个`BagpipeTimeoutError`异常。默认为`null`不开启。
## 原理
`Bagpipe`通过`push`将调用传入内部队列。如果活跃调用小于最大并发数,将会被取出直接执行,反之则继续呆在队列中。当一个异步调用结束的时候,会从队列前取出调用执行。以此来保证异步调用的活跃量不高于限定值。
当队列的长度大于1时,Bagpipe对象将会触发它的`full`事件,该事件传递队列长度值。该值有助于评估业务性能参数。示例如下:
```
bagpipe.on('full', function (length) {
console.warn('底层系统处理不能及时完成,排队中,目前队列长度为:' + length);
});
```
如果队列的长度也超过限制值,这里可以通过`refuse`选项控制,是直接传递异常拒绝服务还是继续排队。默认情况`refuse``false`。如果设置为`true`,新的异步调用将会得到`TooMuchAsyncCallError`异常,而被拒绝服务。
```
var bagpipe = new BagPipe(10, {
refuse: true
});
```
如果异步调用的超时不可预期,可能存在等待队列不均衡的情况,为此可以全局设置一个超时时间,对于过长的响应时间,提前返回超时状态。
```
var bagpipe = new BagPipe(10, {
timeout: 1000
});
```
## 模块状态
单元测试通过状态:[![Build Status](https://secure.travis-ci.org/JacksonTian/bagpipe.png)](http://travis-ci.org/JacksonTian/bagpipe)。单元测试覆盖率[100%](http://html5ify.com/bagpipe/coverage.html)
## 最佳实践
- 确保异步调用的最后一个参数为回调参数
- 监听`full`事件,以增加你对业务性能的评估
- 目前异步方法未支持上下文。确保异步方法内部没有`this`引用。如果存在`this`引用,请用`bind`方法传递正确的`this`上下文
- 异步调用应当具备timeout的业务处理,无论业务是否完成,总在一定的时间内保证返回
## 实际案例
当你需要遍历文件目录的时候,异步可以确保充分利用IO。你可以轻松发起成千上万个文件的读取。但是,系统文件描述符是有限的。不服的话,遇见下面这个错误再来重读此文。
```
Error: EMFILE, too many open files
```
也有人会考虑用同步方法来进行处理。但是,同步时,CPU与IO并不能并行利用,一定情况下,性能是不可弃的一项指标。用上`Bagpipe`,可以轻松享受并发,也能限制并发。
```
var bagpipe = new Bagpipe(10);
var files = ['这里有很多很多文件'];
for (var i = 0; i < files.length; i++) {
// fs.readFile(files[i], 'utf-8', function (err, data) {
bagpipe.push(fs.readFile, files[i], 'utf-8', function (err, data) {
// 不会因为文件描述符过多出错
// 妥妥的
});
}
```
## License
[MIT](https://github.com/JacksonTian/bagpipe/blob/master/MIT-License)许可证下发布,欢迎享受开源
module.exports = require('./lib/bagpipe');
var util = require("util");
var events = require("events");
/**
* 构造器,传入限流值,设置异步调用最大并发数
* Examples:
* ```
* var bagpipe = new Bagpipe(100);
* bagpipe.push(fs.readFile, 'path', 'utf-8', function (err, data) {
* // TODO
* });
* ```
* Events:
* - `full`, 当活动异步达到限制值时,后续异步调用将被暂存于队列中。当队列的长度大于限制值的2倍或100的时候时候,触发`full`事件。事件传递队列长度值。
* - `outdated`, 超时后的异步调用异常返回。
* Options:
* - `disabled`, 禁用限流,测试时用
* - `refuse`, 拒绝模式,排队超过限制值时,新来的调用将会得到`TooMuchAsyncCallError`异常
* - `timeout`, 设置异步调用的时间上线,保证异步调用能够恒定的结束,不至于花费太长时间
* @param {Number} limit 并发数限制值
* @param {Object} options Options
*/
var Bagpipe = function (limit, options) {
events.EventEmitter.call(this);
this.limit = limit;
this.active = 0;
this.queue = [];
this.options = {
disabled: false,
refuse: false,
ratio: 1,
timeout: null
};
if (typeof options === 'boolean') {
options = {
disabled: options
};
}
options = options || {};
for (var key in this.options) {
if (options.hasOwnProperty(key)) {
this.options[key] = options[key];
}
}
// queue length
this.queueLength = Math.round(this.limit * (this.options.ratio || 1));
};
util.inherits(Bagpipe, events.EventEmitter);
/**
* 推入方法,参数。最后一个参数为回调函数
* @param {Function} method 异步方法
* @param {Mix} args 参数列表,最后一个参数为回调函数。
*/
Bagpipe.prototype.push = function (method) {
var args = [].slice.call(arguments, 1);
var callback = args[args.length - 1];
if (typeof callback !== 'function') {
args.push(function () {});
}
if (this.options.disabled || this.limit < 1) {
method.apply(null, args);
return this;
}
// 队列长度也超过限制值时
if (this.queue.length < this.queueLength || !this.options.refuse) {
this.queue.push({
method: method,
args: args
});
} else {
var err = new Error('Too much async call in queue');
err.name = 'TooMuchAsyncCallError';
callback(err);
}
if (this.queue.length > 1) {
this.emit('full', this.queue.length);
}
this.next();
return this;
};
/*!
* 继续执行队列中的后续动作
*/
Bagpipe.prototype.next = function () {
var that = this;
if (that.active < that.limit && that.queue.length) {
var req = that.queue.shift();
that.run(req.method, req.args);
}
};
Bagpipe.prototype._next = function () {
this.active--;
this.next();
};
/*!
* 执行队列中的方法
*/
Bagpipe.prototype.run = function (method, args) {
var that = this;
that.active++;
var callback = args[args.length - 1];
var timer = null;
var called = false;
// inject logic
args[args.length - 1] = function (err) {
// anyway, clear the timer
if (timer) {
clearTimeout(timer);
timer = null;
}
// if timeout, don't execute
if (!called) {
that._next();
callback.apply(null, arguments);
} else {
// pass the outdated error
if (err) {
that.emit('outdated', err);
}
}
};
var timeout = that.options.timeout;
if (timeout) {
timer = setTimeout(function () {
// set called as true
called = true;
that._next();
// pass the exception
var err = new Error(timeout + 'ms timeout');
err.name = 'BagpipeTimeoutError';
err.data = {
name: method.name,
method: method.toString(),
args: args.slice(0, -1)
};
callback(err);
}, timeout);
}
method.apply(null, args);
};
module.exports = Bagpipe;
{
"name": "bagpipe",
"version": "0.3.5",
"description": "Concurrency limit",
"keywords": [
"limiter",
"concurrency limit",
"parallel limit"
],
"main": "index.js",
"scripts": {
"test": "make test-all",
"blanket": {
"pattern": "bagpipe/lib"
},
"travis-cov": {
"threshold": 99
}
},
"author": "Jackson Tian",
"license": "MIT",
"readmeFilename": "README.md",
"gitHead": "75269c60fdbb897be14575e70a2b86cf43999500",
"directories": {
"doc": "doc",
"test": "test"
},
"devDependencies": {
"pedding": "*",
"should": "*",
"blanket": "*",
"mocha": "*",
"travis-cov": "*"
},
"repository": {
"type": "git",
"url": "git://github.com/JacksonTian/bagpipe.git"
},
"bugs": {
"url": "https://github.com/JacksonTian/bagpipe/issues"
}
}
var should = require('should');
var pedding = require('pedding');
var Bagpipe = require('../');
describe('bagpipe', function () {
var async = function (ms, callback) {
setTimeout(function () {
callback(null, {});
}, ms);
};
it('constructor', function () {
var bagpipe = new Bagpipe(10);
bagpipe.limit.should.be.equal(10);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
bagpipe.options.disabled.should.be.equal(false);
});
it('constructor disabled', function () {
var bagpipe = new Bagpipe(10, true);
bagpipe.limit.should.be.equal(10);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
bagpipe.options.disabled.should.be.equal(true);
});
it('constructor limit less than 1', function (done) {
var bagpipe = new Bagpipe(0);
bagpipe.push(async, 10, function () {
bagpipe.active.should.be.equal(0);
done();
});
bagpipe.active.should.be.equal(0);
});
it('constructor limit less than 1 for nextTick', function (done) {
var bagpipe = new Bagpipe(0);
bagpipe.push(process.nextTick, function () {
bagpipe.active.should.be.equal(0);
done();
});
bagpipe.active.should.be.equal(0);
});
it('constructor disabled is true', function (done) {
var bagpipe = new Bagpipe(10, true);
bagpipe.push(async, 10, function () {
bagpipe.active.should.be.equal(0);
done();
});
bagpipe.active.should.be.equal(0);
});
it('push', function (done) {
var bagpipe = new Bagpipe(5);
bagpipe.limit.should.be.equal(5);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
bagpipe.push(async, 10, function () {
bagpipe.active.should.be.equal(0);
done();
});
bagpipe.active.should.be.equal(1);
});
it('push, async with this', function (done) {
var bagpipe = new Bagpipe(5);
bagpipe.limit.should.be.equal(5);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
var context = {value: 10};
context.async = function (callback) {
this.value--;
var that = this;
process.nextTick(function() {
callback(that.value);
});
};
bagpipe.push(context.async.bind(context), function () {
bagpipe.active.should.be.equal(0);
done();
});
bagpipe.active.should.be.equal(1);
});
it('push, active should not be above limit', function (done) {
var limit = 5;
var bagpipe = new Bagpipe(limit);
bagpipe.limit.should.be.equal(limit);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
var counter = 10;
for (var i = 0; i < counter; i++) {
bagpipe.push(async, 1 + Math.round(Math.random() * 10), function () {
bagpipe.active.should.not.be.above(limit);
counter--;
if (counter === 0) {
done();
}
});
bagpipe.active.should.not.be.above(limit);
}
});
it('push, disabled, active should not be above limit', function (done) {
var limit = 5;
var bagpipe = new Bagpipe(limit, true);
bagpipe.limit.should.be.equal(limit);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
var counter = 10;
for (var i = 0; i < counter; i++) {
bagpipe.push(async, 10 + Math.round(Math.random() * 10), function () {
bagpipe.active.should.be.equal(0);
counter--;
if (counter === 0) {
done();
}
});
bagpipe.active.should.be.equal(0);
}
});
it('full event should fired when above limit', function (done) {
var limit = 5;
var bagpipe = new Bagpipe(limit);
bagpipe.limit.should.be.equal(limit);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
var counter = 0;
bagpipe.on('full', function (length) {
length.should.above(1);
counter++;
});
var noop = function () {};
for (var i = 0; i < 100; i++) {
bagpipe.push(async, 10, noop);
}
counter.should.above(0);
done();
});
it('should support without callback', function (done) {
var limit = 5;
var bagpipe = new Bagpipe(limit);
bagpipe.limit.should.be.equal(limit);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
bagpipe.push(async, 10);
bagpipe.active.should.be.equal(1);
done();
});
it('should get TooMuchAsyncCallError', function (done) {
done = pedding(5, done);
var limit = 2;
var bagpipe = new Bagpipe(limit, {
refuse: true
});
bagpipe.limit.should.be.equal(limit);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
for (var i = 0; i < 4; i++) {
bagpipe.push(async, 10, function (err) {
should.not.exist(err);
done();
});
}
bagpipe.push(async, 10, function (err) {
should.exist(err);
done();
});
bagpipe.active.should.be.equal(2);
});
it('should get TooMuchAsyncCallError with ratio', function (done) {
done = pedding(7, done);
var limit = 2;
var bagpipe = new Bagpipe(limit, {
refuse: true,
ratio: 2
});
bagpipe.limit.should.be.equal(limit);
bagpipe.queue.should.have.length(0);
bagpipe.active.should.be.equal(0);
for (var i = 0; i < 2; i++) {
bagpipe.push(async, 10, function (err) {
should.not.exist(err);
done();
});
}
bagpipe.queue.should.have.length(0);
for (i = 0; i < 4; i++) {
bagpipe.push(async, 10, function (err) {
should.not.exist(err);
done();
});
}
bagpipe.queue.should.have.length(4);
bagpipe.push(async, 10, function (err) {
should.exist(err);
done();
});
bagpipe.active.should.be.equal(2);
bagpipe.queue.should.have.length(4);
});
it('should get BagpipeTimeoutError', function (done) {
done = pedding(3, done);
var _async = function _async(ms, callback) {
setTimeout(function () {
callback(null, {ms: ms});
}, ms);
};
var _async2 = function _async(ms, callback) {
setTimeout(function () {
callback(new Error('Timeout'));
}, ms);
};
var bagpipe = new Bagpipe(10, {
timeout: 10
});
bagpipe.on('outdated', function (err) {
should.exist(err);
done();
});
bagpipe.push(_async, 5, function (err, data) {
should.not.exist(err);
should.exist(data);
data.should.have.property('ms', 5);
done();
});
bagpipe.push(_async2, 15, function (err) {
should.exist(err);
err.name.should.eql('BagpipeTimeoutError');
err.message.should.eql('10ms timeout');
err.data.should.have.property('name', '_async');
err.data.should.have.property('method');
err.data.args.should.eql([15]);
done();
});
});
});
Copyright Fedor Indutny, 2015.
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.
# <img src="./logo.png" alt="bn.js" width="160" height="160" />
> BigNum in pure javascript
[![Build Status](https://secure.travis-ci.org/indutny/bn.js.png)](http://travis-ci.org/indutny/bn.js)
## Install
`npm install --save bn.js`
## Usage
```js
const BN = require('bn.js');
var a = new BN('dead', 16);
var b = new BN('101010', 2);
var res = a.add(b);
console.log(res.toString(10)); // 57047
```
**Note**: decimals are not supported in this library.
## Notation
### Prefixes
There are several prefixes to instructions that affect the way the work. Here
is the list of them in the order of appearance in the function name:
* `i` - perform operation in-place, storing the result in the host object (on
which the method was invoked). Might be used to avoid number allocation costs
* `u` - unsigned, ignore the sign of operands when performing operation, or
always return positive value. Second case applies to reduction operations
like `mod()`. In such cases if the result will be negative - modulo will be
added to the result to make it positive
### Postfixes
The only available postfix at the moment is:
* `n` - which means that the argument of the function must be a plain JavaScript
Number. Decimals are not supported.
### Examples
* `a.iadd(b)` - perform addition on `a` and `b`, storing the result in `a`
* `a.umod(b)` - reduce `a` modulo `b`, returning positive value
* `a.iushln(13)` - shift bits of `a` left by 13
## Instructions
Prefixes/postfixes are put in parens at the of the line. `endian` - could be
either `le` (little-endian) or `be` (big-endian).
### Utilities
* `a.clone()` - clone number
* `a.toString(base, length)` - convert to base-string and pad with zeroes
* `a.toNumber()` - convert to Javascript Number (limited to 53 bits)
* `a.toJSON()` - convert to JSON compatible hex string (alias of `toString(16)`)
* `a.toArray(endian, length)` - convert to byte `Array`, and optionally zero
pad to length, throwing if already exceeding
* `a.toArrayLike(type, endian, length)` - convert to an instance of `type`,
which must behave like an `Array`
* `a.toBuffer(endian, length)` - convert to Node.js Buffer (if available). For
compatibility with browserify and similar tools, use this instead:
`a.toArrayLike(Buffer, endian, length)`
* `a.bitLength()` - get number of bits occupied
* `a.zeroBits()` - return number of less-significant consequent zero bits
(example: `1010000` has 4 zero bits)
* `a.byteLength()` - return number of bytes occupied
* `a.isNeg()` - true if the number is negative
* `a.isEven()` - no comments
* `a.isOdd()` - no comments
* `a.isZero()` - no comments
* `a.cmp(b)` - compare numbers and return `-1` (a `<` b), `0` (a `==` b), or `1` (a `>` b)
depending on the comparison result (`ucmp`, `cmpn`)
* `a.lt(b)` - `a` less than `b` (`n`)
* `a.lte(b)` - `a` less than or equals `b` (`n`)
* `a.gt(b)` - `a` greater than `b` (`n`)
* `a.gte(b)` - `a` greater than or equals `b` (`n`)
* `a.eq(b)` - `a` equals `b` (`n`)
* `a.toTwos(width)` - convert to two's complement representation, where `width` is bit width
* `a.fromTwos(width)` - convert from two's complement representation, where `width` is the bit width
* `BN.isBN(object)` - returns true if the supplied `object` is a BN.js instance
### Arithmetics
* `a.neg()` - negate sign (`i`)
* `a.abs()` - absolute value (`i`)
* `a.add(b)` - addition (`i`, `n`, `in`)
* `a.sub(b)` - subtraction (`i`, `n`, `in`)
* `a.mul(b)` - multiply (`i`, `n`, `in`)
* `a.sqr()` - square (`i`)
* `a.pow(b)` - raise `a` to the power of `b`
* `a.div(b)` - divide (`divn`, `idivn`)
* `a.mod(b)` - reduct (`u`, `n`) (but no `umodn`)
* `a.divRound(b)` - rounded division
### Bit operations
* `a.or(b)` - or (`i`, `u`, `iu`)
* `a.and(b)` - and (`i`, `u`, `iu`, `andln`) (NOTE: `andln` is going to be replaced
with `andn` in future)
* `a.xor(b)` - xor (`i`, `u`, `iu`)
* `a.setn(b)` - set specified bit to `1`
* `a.shln(b)` - shift left (`i`, `u`, `iu`)
* `a.shrn(b)` - shift right (`i`, `u`, `iu`)
* `a.testn(b)` - test if specified bit is set
* `a.maskn(b)` - clear bits with indexes higher or equal to `b` (`i`)
* `a.bincn(b)` - add `1 << b` to the number
* `a.notn(w)` - not (for the width specified by `w`) (`i`)
### Reduction
* `a.gcd(b)` - GCD
* `a.egcd(b)` - Extended GCD results (`{ a: ..., b: ..., gcd: ... }`)
* `a.invm(b)` - inverse `a` modulo `b`
## Fast reduction
When doing lots of reductions using the same modulo, it might be beneficial to
use some tricks: like [Montgomery multiplication][0], or using special algorithm
for [Mersenne Prime][1].
### Reduction context
To enable this tricks one should create a reduction context:
```js
var red = BN.red(num);
```
where `num` is just a BN instance.
Or:
```js
var red = BN.red(primeName);
```
Where `primeName` is either of these [Mersenne Primes][1]:
* `'k256'`
* `'p224'`
* `'p192'`
* `'p25519'`
Or:
```js
var red = BN.mont(num);
```
To reduce numbers with [Montgomery trick][0]. `.mont()` is generally faster than
`.red(num)`, but slower than `BN.red(primeName)`.
### Converting numbers
Before performing anything in reduction context - numbers should be converted
to it. Usually, this means that one should:
* Convert inputs to reducted ones
* Operate on them in reduction context
* Convert outputs back from the reduction context
Here is how one may convert numbers to `red`:
```js
var redA = a.toRed(red);
```
Where `red` is a reduction context created using instructions above
Here is how to convert them back:
```js
var a = redA.fromRed();
```
### Red instructions
Most of the instructions from the very start of this readme have their
counterparts in red context:
* `a.redAdd(b)`, `a.redIAdd(b)`
* `a.redSub(b)`, `a.redISub(b)`
* `a.redShl(num)`
* `a.redMul(b)`, `a.redIMul(b)`
* `a.redSqr()`, `a.redISqr()`
* `a.redSqrt()` - square root modulo reduction context's prime
* `a.redInvm()` - modular inverse of the number
* `a.redNeg()`
* `a.redPow(b)` - modular exponentiation
## LICENSE
This software is licensed under the MIT License.
[0]: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
[1]: https://en.wikipedia.org/wiki/Mersenne_prime
This diff is collapsed. Click to expand it.
{
"name": "bn.js",
"version": "4.12.0",
"description": "Big number implementation in pure javascript",
"main": "lib/bn.js",
"scripts": {
"lint": "semistandard",
"unit": "mocha --reporter=spec test/*-test.js",
"test": "npm run lint && npm run unit"
},
"repository": {
"type": "git",
"url": "git@github.com:indutny/bn.js"
},
"keywords": [
"BN",
"BigNum",
"Big number",
"Modulo",
"Montgomery"
],
"author": "Fedor Indutny <fedor@indutny.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/indutny/bn.js/issues"
},
"homepage": "https://github.com/indutny/bn.js",
"browser": {
"buffer": false
},
"devDependencies": {
"istanbul": "^0.3.5",
"mocha": "^2.1.0",
"semistandard": "^7.0.4"
}
}
This diff is collapsed. Click to expand it.
(The MIT License)
Copyright (c) 2011-2017 JP Richardson
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.
Node.js: fs-extra
=================
`fs-extra` adds file system methods that aren't included in the native `fs` module and adds promise support to the `fs` methods. It also uses [`graceful-fs`](https://github.com/isaacs/node-graceful-fs) to prevent `EMFILE` errors. It should be a drop in replacement for `fs`.
[![npm Package](https://img.shields.io/npm/v/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![License](https://img.shields.io/npm/l/express.svg)](https://github.com/jprichardson/node-fs-extra/blob/master/LICENSE)
[![build status](https://img.shields.io/travis/jprichardson/node-fs-extra/master.svg)](http://travis-ci.org/jprichardson/node-fs-extra)
[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-fs-extra/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-fs-extra/branch/master)
[![downloads per month](http://img.shields.io/npm/dm/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![Coverage Status](https://img.shields.io/coveralls/github/jprichardson/node-fs-extra/master.svg)](https://coveralls.io/github/jprichardson/node-fs-extra)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
Why?
----
I got tired of including `mkdirp`, `rimraf`, and `ncp` in most of my projects.
Installation
------------
npm install fs-extra
Usage
-----
`fs-extra` is a drop in replacement for native `fs`. All methods in `fs` are attached to `fs-extra`. All `fs` methods return promises if the callback isn't passed.
You don't ever need to include the original `fs` module again:
```js
const fs = require('fs') // this is no longer necessary
```
you can now do this:
```js
const fs = require('fs-extra')
```
or if you prefer to make it clear that you're using `fs-extra` and not `fs`, you may want
to name your `fs` variable `fse` like so:
```js
const fse = require('fs-extra')
```
you can also keep both, but it's redundant:
```js
const fs = require('fs')
const fse = require('fs-extra')
```
Sync vs Async vs Async/Await
-------------
Most methods are async by default. All async methods will return a promise if the callback isn't passed.
Sync methods on the other hand will throw if an error occurs.
Also Async/Await will throw an error if one occurs.
Example:
```js
const fs = require('fs-extra')
// Async with promises:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
.catch(err => console.error(err))
// Async with callbacks:
fs.copy('/tmp/myfile', '/tmp/mynewfile', err => {
if (err) return console.error(err)
console.log('success!')
})
// Sync:
try {
fs.copySync('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
// Async/Await:
async function copyFiles () {
try {
await fs.copy('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
}
copyFiles()
```
Methods
-------
### Async
- [copy](docs/copy.md)
- [emptyDir](docs/emptyDir.md)
- [ensureFile](docs/ensureFile.md)
- [ensureDir](docs/ensureDir.md)
- [ensureLink](docs/ensureLink.md)
- [ensureSymlink](docs/ensureSymlink.md)
- [mkdirp](docs/ensureDir.md)
- [mkdirs](docs/ensureDir.md)
- [move](docs/move.md)
- [outputFile](docs/outputFile.md)
- [outputJson](docs/outputJson.md)
- [pathExists](docs/pathExists.md)
- [readJson](docs/readJson.md)
- [remove](docs/remove.md)
- [writeJson](docs/writeJson.md)
### Sync
- [copySync](docs/copy-sync.md)
- [emptyDirSync](docs/emptyDir-sync.md)
- [ensureFileSync](docs/ensureFile-sync.md)
- [ensureDirSync](docs/ensureDir-sync.md)
- [ensureLinkSync](docs/ensureLink-sync.md)
- [ensureSymlinkSync](docs/ensureSymlink-sync.md)
- [mkdirpSync](docs/ensureDir-sync.md)
- [mkdirsSync](docs/ensureDir-sync.md)
- [moveSync](docs/move-sync.md)
- [outputFileSync](docs/outputFile-sync.md)
- [outputJsonSync](docs/outputJson-sync.md)
- [pathExistsSync](docs/pathExists-sync.md)
- [readJsonSync](docs/readJson-sync.md)
- [removeSync](docs/remove-sync.md)
- [writeJsonSync](docs/writeJson-sync.md)
**NOTE:** You can still use the native Node.js methods. They are promisified and copied over to `fs-extra`. See [notes on `fs.read()` & `fs.write()`](docs/fs-read-write.md)
### What happened to `walk()` and `walkSync()`?
They were removed from `fs-extra` in v2.0.0. If you need the functionality, `walk` and `walkSync` are available as separate packages, [`klaw`](https://github.com/jprichardson/node-klaw) and [`klaw-sync`](https://github.com/manidlou/node-klaw-sync).
Third Party
-----------
### TypeScript
If you like TypeScript, you can use `fs-extra` with it: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/fs-extra
### File / Directory Watching
If you want to watch for changes to files or directories, then you should use [chokidar](https://github.com/paulmillr/chokidar).
### Obtain Filesystem (Devices, Partitions) Information
[fs-filesystem](https://github.com/arthurintelligence/node-fs-filesystem) allows you to read the state of the filesystem of the host on which it is run. It returns information about both the devices and the partitions (volumes) of the system.
### Misc.
- [fs-extra-debug](https://github.com/jdxcode/fs-extra-debug) - Send your fs-extra calls to [debug](https://npmjs.org/package/debug).
- [mfs](https://github.com/cadorn/mfs) - Monitor your fs-extra calls.
Hacking on fs-extra
-------------------
Wanna hack on `fs-extra`? Great! Your help is needed! [fs-extra is one of the most depended upon Node.js packages](http://nodei.co/npm/fs-extra.png?downloads=true&downloadRank=true&stars=true). This project
uses [JavaScript Standard Style](https://github.com/feross/standard) - if the name or style choices bother you,
you're gonna have to get over it :) If `standard` is good enough for `npm`, it's good enough for `fs-extra`.
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
What's needed?
- First, take a look at existing issues. Those are probably going to be where the priority lies.
- More tests for edge cases. Specifically on different platforms. There can never be enough tests.
- Improve test coverage. See coveralls output for more info.
Note: If you make any big changes, **you should definitely file an issue for discussion first.**
### Running the Test Suite
fs-extra contains hundreds of tests.
- `npm run lint`: runs the linter ([standard](http://standardjs.com/))
- `npm run unit`: runs the unit tests
- `npm test`: runs both the linter and the tests
### Windows
If you run the tests on the Windows and receive a lot of symbolic link `EPERM` permission errors, it's
because on Windows you need elevated privilege to create symbolic links. You can add this to your Windows's
account by following the instructions here: http://superuser.com/questions/104845/permission-to-make-symbolic-links-in-windows-7
However, I didn't have much luck doing this.
Since I develop on Mac OS X, I use VMWare Fusion for Windows testing. I create a shared folder that I map to a drive on Windows.
I open the `Node.js command prompt` and run as `Administrator`. I then map the network drive running the following command:
net use z: "\\vmware-host\Shared Folders"
I can then navigate to my `fs-extra` directory and run the tests.
Naming
------
I put a lot of thought into the naming of these functions. Inspired by @coolaj86's request. So he deserves much of the credit for raising the issue. See discussion(s) here:
* https://github.com/jprichardson/node-fs-extra/issues/2
* https://github.com/flatiron/utile/issues/11
* https://github.com/ryanmcgrath/wrench-js/issues/29
* https://github.com/substack/node-mkdirp/issues/17
First, I believe that in as many cases as possible, the [Node.js naming schemes](http://nodejs.org/api/fs.html) should be chosen. However, there are problems with the Node.js own naming schemes.
For example, `fs.readFile()` and `fs.readdir()`: the **F** is capitalized in *File* and the **d** is not capitalized in *dir*. Perhaps a bit pedantic, but they should still be consistent. Also, Node.js has chosen a lot of POSIX naming schemes, which I believe is great. See: `fs.mkdir()`, `fs.rmdir()`, `fs.chown()`, etc.
We have a dilemma though. How do you consistently name methods that perform the following POSIX commands: `cp`, `cp -r`, `mkdir -p`, and `rm -rf`?
My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too.
So, if you want to remove a file or a directory regardless of whether it has contents, just call `fs.remove(path)`. If you want to copy a file or a directory whether it has contents, just call `fs.copy(source, destination)`. If you want to create a directory regardless of whether its parent directories exist, just call `fs.mkdirs(path)` or `fs.mkdirp(path)`.
Credit
------
`fs-extra` wouldn't be possible without using the modules from the following authors:
- [Isaac Shlueter](https://github.com/isaacs)
- [Charlie McConnel](https://github.com/avianflu)
- [James Halliday](https://github.com/substack)
- [Andrew Kelley](https://github.com/andrewrk)
License
-------
Licensed under MIT
Copyright (c) 2011-2017 [JP Richardson](https://github.com/jprichardson)
[1]: http://nodejs.org/docs/latest/api/fs.html
[jsonfile]: https://github.com/jprichardson/node-jsonfile
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirpSync = require('../mkdirs').mkdirsSync
const utimesSync = require('../util/utimes.js').utimesMillisSync
const stat = require('../util/stat')
function copySync (src, dest, opts) {
if (typeof opts === 'function') {
opts = { filter: opts }
}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`)
}
const { srcStat, destStat } = stat.checkPathsSync(src, dest, 'copy')
stat.checkParentPathsSync(src, srcStat, dest, 'copy')
return handleFilterAndCopy(destStat, src, dest, opts)
}
function handleFilterAndCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
const destParent = path.dirname(dest)
if (!fs.existsSync(destParent)) mkdirpSync(destParent)
return startCopy(destStat, src, dest, opts)
}
function startCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
return getStats(destStat, src, dest, opts)
}
function getStats (destStat, src, dest, opts) {
const statSync = opts.dereference ? fs.statSync : fs.lstatSync
const srcStat = statSync(src)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts)
}
function onFile (srcStat, destStat, src, dest, opts) {
if (!destStat) return copyFile(srcStat, src, dest, opts)
return mayCopyFile(srcStat, src, dest, opts)
}
function mayCopyFile (srcStat, src, dest, opts) {
if (opts.overwrite) {
fs.unlinkSync(dest)
return copyFile(srcStat, src, dest, opts)
} else if (opts.errorOnExist) {
throw new Error(`'${dest}' already exists`)
}
}
function copyFile (srcStat, src, dest, opts) {
if (typeof fs.copyFileSync === 'function') {
fs.copyFileSync(src, dest)
fs.chmodSync(dest, srcStat.mode)
if (opts.preserveTimestamps) {
return utimesSync(dest, srcStat.atime, srcStat.mtime)
}
return
}
return copyFileFallback(srcStat, src, dest, opts)
}
function copyFileFallback (srcStat, src, dest, opts) {
const BUF_LENGTH = 64 * 1024
const _buff = require('../util/buffer')(BUF_LENGTH)
const fdr = fs.openSync(src, 'r')
const fdw = fs.openSync(dest, 'w', srcStat.mode)
let pos = 0
while (pos < srcStat.size) {
const bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
fs.writeSync(fdw, _buff, 0, bytesRead)
pos += bytesRead
}
if (opts.preserveTimestamps) fs.futimesSync(fdw, srcStat.atime, srcStat.mtime)
fs.closeSync(fdr)
fs.closeSync(fdw)
}
function onDir (srcStat, destStat, src, dest, opts) {
if (!destStat) return mkDirAndCopy(srcStat, src, dest, opts)
if (destStat && !destStat.isDirectory()) {
throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
}
return copyDir(src, dest, opts)
}
function mkDirAndCopy (srcStat, src, dest, opts) {
fs.mkdirSync(dest)
copyDir(src, dest, opts)
return fs.chmodSync(dest, srcStat.mode)
}
function copyDir (src, dest, opts) {
fs.readdirSync(src).forEach(item => copyDirItem(item, src, dest, opts))
}
function copyDirItem (item, src, dest, opts) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
const { destStat } = stat.checkPathsSync(srcItem, destItem, 'copy')
return startCopy(destStat, srcItem, destItem, opts)
}
function onLink (destStat, src, dest, opts) {
let resolvedSrc = fs.readlinkSync(src)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlinkSync(resolvedSrc, dest)
} else {
let resolvedDest
try {
resolvedDest = fs.readlinkSync(dest)
} catch (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlinkSync(resolvedSrc, dest)
throw err
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)
}
// prevent copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (fs.statSync(dest).isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)
}
return copyLink(resolvedSrc, dest)
}
}
function copyLink (resolvedSrc, dest) {
fs.unlinkSync(dest)
return fs.symlinkSync(resolvedSrc, dest)
}
module.exports = copySync
'use strict'
module.exports = {
copySync: require('./copy-sync')
}
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirp = require('../mkdirs').mkdirs
const pathExists = require('../path-exists').pathExists
const utimes = require('../util/utimes').utimesMillis
const stat = require('../util/stat')
function copy (src, dest, opts, cb) {
if (typeof opts === 'function' && !cb) {
cb = opts
opts = {}
} else if (typeof opts === 'function') {
opts = { filter: opts }
}
cb = cb || function () {}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`)
}
stat.checkPaths(src, dest, 'copy', (err, stats) => {
if (err) return cb(err)
const { srcStat, destStat } = stats
stat.checkParentPaths(src, srcStat, dest, 'copy', err => {
if (err) return cb(err)
if (opts.filter) return handleFilter(checkParentDir, destStat, src, dest, opts, cb)
return checkParentDir(destStat, src, dest, opts, cb)
})
})
}
function checkParentDir (destStat, src, dest, opts, cb) {
const destParent = path.dirname(dest)
pathExists(destParent, (err, dirExists) => {
if (err) return cb(err)
if (dirExists) return startCopy(destStat, src, dest, opts, cb)
mkdirp(destParent, err => {
if (err) return cb(err)
return startCopy(destStat, src, dest, opts, cb)
})
})
}
function handleFilter (onInclude, destStat, src, dest, opts, cb) {
Promise.resolve(opts.filter(src, dest)).then(include => {
if (include) return onInclude(destStat, src, dest, opts, cb)
return cb()
}, error => cb(error))
}
function startCopy (destStat, src, dest, opts, cb) {
if (opts.filter) return handleFilter(getStats, destStat, src, dest, opts, cb)
return getStats(destStat, src, dest, opts, cb)
}
function getStats (destStat, src, dest, opts, cb) {
const stat = opts.dereference ? fs.stat : fs.lstat
stat(src, (err, srcStat) => {
if (err) return cb(err)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts, cb)
})
}
function onFile (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return copyFile(srcStat, src, dest, opts, cb)
return mayCopyFile(srcStat, src, dest, opts, cb)
}
function mayCopyFile (srcStat, src, dest, opts, cb) {
if (opts.overwrite) {
fs.unlink(dest, err => {
if (err) return cb(err)
return copyFile(srcStat, src, dest, opts, cb)
})
} else if (opts.errorOnExist) {
return cb(new Error(`'${dest}' already exists`))
} else return cb()
}
function copyFile (srcStat, src, dest, opts, cb) {
if (typeof fs.copyFile === 'function') {
return fs.copyFile(src, dest, err => {
if (err) return cb(err)
return setDestModeAndTimestamps(srcStat, dest, opts, cb)
})
}
return copyFileFallback(srcStat, src, dest, opts, cb)
}
function copyFileFallback (srcStat, src, dest, opts, cb) {
const rs = fs.createReadStream(src)
rs.on('error', err => cb(err)).once('open', () => {
const ws = fs.createWriteStream(dest, { mode: srcStat.mode })
ws.on('error', err => cb(err))
.on('open', () => rs.pipe(ws))
.once('close', () => setDestModeAndTimestamps(srcStat, dest, opts, cb))
})
}
function setDestModeAndTimestamps (srcStat, dest, opts, cb) {
fs.chmod(dest, srcStat.mode, err => {
if (err) return cb(err)
if (opts.preserveTimestamps) {
return utimes(dest, srcStat.atime, srcStat.mtime, cb)
}
return cb()
})
}
function onDir (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return mkDirAndCopy(srcStat, src, dest, opts, cb)
if (destStat && !destStat.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}
return copyDir(src, dest, opts, cb)
}
function mkDirAndCopy (srcStat, src, dest, opts, cb) {
fs.mkdir(dest, err => {
if (err) return cb(err)
copyDir(src, dest, opts, err => {
if (err) return cb(err)
return fs.chmod(dest, srcStat.mode, cb)
})
})
}
function copyDir (src, dest, opts, cb) {
fs.readdir(src, (err, items) => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
}
function copyDirItems (items, src, dest, opts, cb) {
const item = items.pop()
if (!item) return cb()
return copyDirItem(items, item, src, dest, opts, cb)
}
function copyDirItem (items, item, src, dest, opts, cb) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
stat.checkPaths(srcItem, destItem, 'copy', (err, stats) => {
if (err) return cb(err)
const { destStat } = stats
startCopy(destStat, srcItem, destItem, opts, err => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
})
}
function onLink (destStat, src, dest, opts, cb) {
fs.readlink(src, (err, resolvedSrc) => {
if (err) return cb(err)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlink(resolvedSrc, dest, cb)
} else {
fs.readlink(dest, (err, resolvedDest) => {
if (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest, cb)
return cb(err)
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`))
}
// do not copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (destStat.isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`))
}
return copyLink(resolvedSrc, dest, cb)
})
}
})
}
function copyLink (resolvedSrc, dest, cb) {
fs.unlink(dest, err => {
if (err) return cb(err)
return fs.symlink(resolvedSrc, dest, cb)
})
}
module.exports = copy
'use strict'
const u = require('universalify').fromCallback
module.exports = {
copy: u(require('./copy'))
}
'use strict'
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const remove = require('../remove')
const emptyDir = u(function emptyDir (dir, callback) {
callback = callback || function () {}
fs.readdir(dir, (err, items) => {
if (err) return mkdir.mkdirs(dir, callback)
items = items.map(item => path.join(dir, item))
deleteItem()
function deleteItem () {
const item = items.pop()
if (!item) return callback()
remove.remove(item, err => {
if (err) return callback(err)
deleteItem()
})
}
})
})
function emptyDirSync (dir) {
let items
try {
items = fs.readdirSync(dir)
} catch (err) {
return mkdir.mkdirsSync(dir)
}
items.forEach(item => {
item = path.join(dir, item)
remove.removeSync(item)
})
}
module.exports = {
emptyDirSync,
emptydirSync: emptyDirSync,
emptyDir,
emptydir: emptyDir
}
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function createFile (file, callback) {
function makeFile () {
fs.writeFile(file, '', err => {
if (err) return callback(err)
callback()
})
}
fs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err
if (!err && stats.isFile()) return callback()
const dir = path.dirname(file)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return makeFile()
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeFile()
})
})
})
}
function createFileSync (file) {
let stats
try {
stats = fs.statSync(file)
} catch (e) {}
if (stats && stats.isFile()) return
const dir = path.dirname(file)
if (!fs.existsSync(dir)) {
mkdir.mkdirsSync(dir)
}
fs.writeFileSync(file, '')
}
module.exports = {
createFile: u(createFile),
createFileSync
}
'use strict'
const file = require('./file')
const link = require('./link')
const symlink = require('./symlink')
module.exports = {
// file
createFile: file.createFile,
createFileSync: file.createFileSync,
ensureFile: file.createFile,
ensureFileSync: file.createFileSync,
// link
createLink: link.createLink,
createLinkSync: link.createLinkSync,
ensureLink: link.createLink,
ensureLinkSync: link.createLinkSync,
// symlink
createSymlink: symlink.createSymlink,
createSymlinkSync: symlink.createSymlinkSync,
ensureSymlink: symlink.createSymlink,
ensureSymlinkSync: symlink.createSymlinkSync
}
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function createLink (srcpath, dstpath, callback) {
function makeLink (srcpath, dstpath) {
fs.link(srcpath, dstpath, err => {
if (err) return callback(err)
callback(null)
})
}
pathExists(dstpath, (err, destinationExists) => {
if (err) return callback(err)
if (destinationExists) return callback(null)
fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureLink')
return callback(err)
}
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return makeLink(srcpath, dstpath)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeLink(srcpath, dstpath)
})
})
})
})
}
function createLinkSync (srcpath, dstpath) {
const destinationExists = fs.existsSync(dstpath)
if (destinationExists) return undefined
try {
fs.lstatSync(srcpath)
} catch (err) {
err.message = err.message.replace('lstat', 'ensureLink')
throw err
}
const dir = path.dirname(dstpath)
const dirExists = fs.existsSync(dir)
if (dirExists) return fs.linkSync(srcpath, dstpath)
mkdir.mkdirsSync(dir)
return fs.linkSync(srcpath, dstpath)
}
module.exports = {
createLink: u(createLink),
createLinkSync
}
'use strict'
const path = require('path')
const fs = require('graceful-fs')
const pathExists = require('../path-exists').pathExists
/**
* Function that returns two types of paths, one relative to symlink, and one
* relative to the current working directory. Checks if path is absolute or
* relative. If the path is relative, this function checks if the path is
* relative to symlink or relative to current working directory. This is an
* initiative to find a smarter `srcpath` to supply when building symlinks.
* This allows you to determine which path to use out of one of three possible
* types of source paths. The first is an absolute path. This is detected by
* `path.isAbsolute()`. When an absolute path is provided, it is checked to
* see if it exists. If it does it's used, if not an error is returned
* (callback)/ thrown (sync). The other two options for `srcpath` are a
* relative url. By default Node's `fs.symlink` works by creating a symlink
* using `dstpath` and expects the `srcpath` to be relative to the newly
* created symlink. If you provide a `srcpath` that does not exist on the file
* system it results in a broken symlink. To minimize this, the function
* checks to see if the 'relative to symlink' source file exists, and if it
* does it will use it. If it does not, it checks if there's a file that
* exists that is relative to the current working directory, if does its used.
* This preserves the expectations of the original fs.symlink spec and adds
* the ability to pass in `relative to current working direcotry` paths.
*/
function symlinkPaths (srcpath, dstpath, callback) {
if (path.isAbsolute(srcpath)) {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
'toCwd': srcpath,
'toDst': srcpath
})
})
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
return pathExists(relativeToDst, (err, exists) => {
if (err) return callback(err)
if (exists) {
return callback(null, {
'toCwd': relativeToDst,
'toDst': srcpath
})
} else {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
'toCwd': srcpath,
'toDst': path.relative(dstdir, srcpath)
})
})
}
})
}
}
function symlinkPathsSync (srcpath, dstpath) {
let exists
if (path.isAbsolute(srcpath)) {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('absolute srcpath does not exist')
return {
'toCwd': srcpath,
'toDst': srcpath
}
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
exists = fs.existsSync(relativeToDst)
if (exists) {
return {
'toCwd': relativeToDst,
'toDst': srcpath
}
} else {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('relative srcpath does not exist')
return {
'toCwd': srcpath,
'toDst': path.relative(dstdir, srcpath)
}
}
}
}
module.exports = {
symlinkPaths,
symlinkPathsSync
}
'use strict'
const fs = require('graceful-fs')
function symlinkType (srcpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
if (type) return callback(null, type)
fs.lstat(srcpath, (err, stats) => {
if (err) return callback(null, 'file')
type = (stats && stats.isDirectory()) ? 'dir' : 'file'
callback(null, type)
})
}
function symlinkTypeSync (srcpath, type) {
let stats
if (type) return type
try {
stats = fs.lstatSync(srcpath)
} catch (e) {
return 'file'
}
return (stats && stats.isDirectory()) ? 'dir' : 'file'
}
module.exports = {
symlinkType,
symlinkTypeSync
}
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const _mkdirs = require('../mkdirs')
const mkdirs = _mkdirs.mkdirs
const mkdirsSync = _mkdirs.mkdirsSync
const _symlinkPaths = require('./symlink-paths')
const symlinkPaths = _symlinkPaths.symlinkPaths
const symlinkPathsSync = _symlinkPaths.symlinkPathsSync
const _symlinkType = require('./symlink-type')
const symlinkType = _symlinkType.symlinkType
const symlinkTypeSync = _symlinkType.symlinkTypeSync
const pathExists = require('../path-exists').pathExists
function createSymlink (srcpath, dstpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
pathExists(dstpath, (err, destinationExists) => {
if (err) return callback(err)
if (destinationExists) return callback(null)
symlinkPaths(srcpath, dstpath, (err, relative) => {
if (err) return callback(err)
srcpath = relative.toDst
symlinkType(relative.toCwd, type, (err, type) => {
if (err) return callback(err)
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return fs.symlink(srcpath, dstpath, type, callback)
mkdirs(dir, err => {
if (err) return callback(err)
fs.symlink(srcpath, dstpath, type, callback)
})
})
})
})
})
}
function createSymlinkSync (srcpath, dstpath, type) {
const destinationExists = fs.existsSync(dstpath)
if (destinationExists) return undefined
const relative = symlinkPathsSync(srcpath, dstpath)
srcpath = relative.toDst
type = symlinkTypeSync(relative.toCwd, type)
const dir = path.dirname(dstpath)
const exists = fs.existsSync(dir)
if (exists) return fs.symlinkSync(srcpath, dstpath, type)
mkdirsSync(dir)
return fs.symlinkSync(srcpath, dstpath, type)
}
module.exports = {
createSymlink: u(createSymlink),
createSymlinkSync
}
'use strict'
// This is adapted from https://github.com/normalize/mz
// Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const api = [
'access',
'appendFile',
'chmod',
'chown',
'close',
'copyFile',
'fchmod',
'fchown',
'fdatasync',
'fstat',
'fsync',
'ftruncate',
'futimes',
'lchown',
'lchmod',
'link',
'lstat',
'mkdir',
'mkdtemp',
'open',
'readFile',
'readdir',
'readlink',
'realpath',
'rename',
'rmdir',
'stat',
'symlink',
'truncate',
'unlink',
'utimes',
'writeFile'
].filter(key => {
// Some commands are not available on some systems. Ex:
// fs.copyFile was added in Node.js v8.5.0
// fs.mkdtemp was added in Node.js v5.10.0
// fs.lchown is not available on at least some Linux
return typeof fs[key] === 'function'
})
// Export all keys:
Object.keys(fs).forEach(key => {
if (key === 'promises') {
// fs.promises is a getter property that triggers ExperimentalWarning
// Don't re-export it here, the getter is defined in "lib/index.js"
return
}
exports[key] = fs[key]
})
// Universalify async methods:
api.forEach(method => {
exports[method] = u(fs[method])
})
// We differ from mz/fs in that we still ship the old, broken, fs.exists()
// since we are a drop-in replacement for the native module
exports.exists = function (filename, callback) {
if (typeof callback === 'function') {
return fs.exists(filename, callback)
}
return new Promise(resolve => {
return fs.exists(filename, resolve)
})
}
// fs.read() & fs.write need special treatment due to multiple callback args
exports.read = function (fd, buffer, offset, length, position, callback) {
if (typeof callback === 'function') {
return fs.read(fd, buffer, offset, length, position, callback)
}
return new Promise((resolve, reject) => {
fs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
if (err) return reject(err)
resolve({ bytesRead, buffer })
})
})
}
// Function signature can be
// fs.write(fd, buffer[, offset[, length[, position]]], callback)
// OR
// fs.write(fd, string[, position[, encoding]], callback)
// We need to handle both cases, so we use ...args
exports.write = function (fd, buffer, ...args) {
if (typeof args[args.length - 1] === 'function') {
return fs.write(fd, buffer, ...args)
}
return new Promise((resolve, reject) => {
fs.write(fd, buffer, ...args, (err, bytesWritten, buffer) => {
if (err) return reject(err)
resolve({ bytesWritten, buffer })
})
})
}
// fs.realpath.native only available in Node v9.2+
if (typeof fs.realpath.native === 'function') {
exports.realpath.native = u(fs.realpath.native)
}
'use strict'
module.exports = Object.assign(
{},
// Export promiseified graceful-fs:
require('./fs'),
// Export extra methods:
require('./copy-sync'),
require('./copy'),
require('./empty'),
require('./ensure'),
require('./json'),
require('./mkdirs'),
require('./move-sync'),
require('./move'),
require('./output'),
require('./path-exists'),
require('./remove')
)
// Export fs.promises as a getter property so that we don't trigger
// ExperimentalWarning before fs.promises is actually accessed.
const fs = require('fs')
if (Object.getOwnPropertyDescriptor(fs, 'promises')) {
Object.defineProperty(module.exports, 'promises', {
get () { return fs.promises }
})
}
'use strict'
const u = require('universalify').fromCallback
const jsonFile = require('./jsonfile')
jsonFile.outputJson = u(require('./output-json'))
jsonFile.outputJsonSync = require('./output-json-sync')
// aliases
jsonFile.outputJSON = jsonFile.outputJson
jsonFile.outputJSONSync = jsonFile.outputJsonSync
jsonFile.writeJSON = jsonFile.writeJson
jsonFile.writeJSONSync = jsonFile.writeJsonSync
jsonFile.readJSON = jsonFile.readJson
jsonFile.readJSONSync = jsonFile.readJsonSync
module.exports = jsonFile
'use strict'
const u = require('universalify').fromCallback
const jsonFile = require('jsonfile')
module.exports = {
// jsonfile exports
readJson: u(jsonFile.readFile),
readJsonSync: jsonFile.readFileSync,
writeJson: u(jsonFile.writeFile),
writeJsonSync: jsonFile.writeFileSync
}
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const jsonFile = require('./jsonfile')
function outputJsonSync (file, data, options) {
const dir = path.dirname(file)
if (!fs.existsSync(dir)) {
mkdir.mkdirsSync(dir)
}
jsonFile.writeJsonSync(file, data, options)
}
module.exports = outputJsonSync
'use strict'
const path = require('path')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
const jsonFile = require('./jsonfile')
function outputJson (file, data, options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
const dir = path.dirname(file)
pathExists(dir, (err, itDoes) => {
if (err) return callback(err)
if (itDoes) return jsonFile.writeJson(file, data, options, callback)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
jsonFile.writeJson(file, data, options, callback)
})
})
}
module.exports = outputJson
'use strict'
const u = require('universalify').fromCallback
const mkdirs = u(require('./mkdirs'))
const mkdirsSync = require('./mkdirs-sync')
module.exports = {
mkdirs,
mkdirsSync,
// alias
mkdirp: mkdirs,
mkdirpSync: mkdirsSync,
ensureDir: mkdirs,
ensureDirSync: mkdirsSync
}
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const invalidWin32Path = require('./win32').invalidWin32Path
const o777 = parseInt('0777', 8)
function mkdirsSync (p, opts, made) {
if (!opts || typeof opts !== 'object') {
opts = { mode: opts }
}
let mode = opts.mode
const xfs = opts.fs || fs
if (process.platform === 'win32' && invalidWin32Path(p)) {
const errInval = new Error(p + ' contains invalid WIN32 path characters.')
errInval.code = 'EINVAL'
throw errInval
}
if (mode === undefined) {
mode = o777 & (~process.umask())
}
if (!made) made = null
p = path.resolve(p)
try {
xfs.mkdirSync(p, mode)
made = made || p
} catch (err0) {
if (err0.code === 'ENOENT') {
if (path.dirname(p) === p) throw err0
made = mkdirsSync(path.dirname(p), opts, made)
mkdirsSync(p, opts, made)
} else {
// In the case of any other error, just see if there's a dir there
// already. If so, then hooray! If not, then something is borked.
let stat
try {
stat = xfs.statSync(p)
} catch (err1) {
throw err0
}
if (!stat.isDirectory()) throw err0
}
}
return made
}
module.exports = mkdirsSync
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const invalidWin32Path = require('./win32').invalidWin32Path
const o777 = parseInt('0777', 8)
function mkdirs (p, opts, callback, made) {
if (typeof opts === 'function') {
callback = opts
opts = {}
} else if (!opts || typeof opts !== 'object') {
opts = { mode: opts }
}
if (process.platform === 'win32' && invalidWin32Path(p)) {
const errInval = new Error(p + ' contains invalid WIN32 path characters.')
errInval.code = 'EINVAL'
return callback(errInval)
}
let mode = opts.mode
const xfs = opts.fs || fs
if (mode === undefined) {
mode = o777 & (~process.umask())
}
if (!made) made = null
callback = callback || function () {}
p = path.resolve(p)
xfs.mkdir(p, mode, er => {
if (!er) {
made = made || p
return callback(null, made)
}
switch (er.code) {
case 'ENOENT':
if (path.dirname(p) === p) return callback(er)
mkdirs(path.dirname(p), opts, (er, made) => {
if (er) callback(er, made)
else mkdirs(p, opts, callback, made)
})
break
// In the case of any other error, just see if there's a dir
// there already. If so, then hooray! If not, then something
// is borked.
default:
xfs.stat(p, (er2, stat) => {
// if the stat fails, then that's super weird.
// let the original error be the failure reason.
if (er2 || !stat.isDirectory()) callback(er, made)
else callback(null, made)
})
break
}
})
}
module.exports = mkdirs
'use strict'
const path = require('path')
// get drive on windows
function getRootPath (p) {
p = path.normalize(path.resolve(p)).split(path.sep)
if (p.length > 0) return p[0]
return null
}
// http://stackoverflow.com/a/62888/10333 contains more accurate
// TODO: expand to include the rest
const INVALID_PATH_CHARS = /[<>:"|?*]/
function invalidWin32Path (p) {
const rp = getRootPath(p)
p = p.replace(rp, '')
return INVALID_PATH_CHARS.test(p)
}
module.exports = {
getRootPath,
invalidWin32Path
}
'use strict'
module.exports = {
moveSync: require('./move-sync')
}
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copySync = require('../copy-sync').copySync
const removeSync = require('../remove').removeSync
const mkdirpSync = require('../mkdirs').mkdirpSync
const stat = require('../util/stat')
function moveSync (src, dest, opts) {
opts = opts || {}
const overwrite = opts.overwrite || opts.clobber || false
const { srcStat } = stat.checkPathsSync(src, dest, 'move')
stat.checkParentPathsSync(src, srcStat, dest, 'move')
mkdirpSync(path.dirname(dest))
return doRename(src, dest, overwrite)
}
function doRename (src, dest, overwrite) {
if (overwrite) {
removeSync(dest)
return rename(src, dest, overwrite)
}
if (fs.existsSync(dest)) throw new Error('dest already exists.')
return rename(src, dest, overwrite)
}
function rename (src, dest, overwrite) {
try {
fs.renameSync(src, dest)
} catch (err) {
if (err.code !== 'EXDEV') throw err
return moveAcrossDevice(src, dest, overwrite)
}
}
function moveAcrossDevice (src, dest, overwrite) {
const opts = {
overwrite,
errorOnExist: true
}
copySync(src, dest, opts)
return removeSync(src)
}
module.exports = moveSync
'use strict'
const u = require('universalify').fromCallback
module.exports = {
move: u(require('./move'))
}
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copy = require('../copy').copy
const remove = require('../remove').remove
const mkdirp = require('../mkdirs').mkdirp
const pathExists = require('../path-exists').pathExists
const stat = require('../util/stat')
function move (src, dest, opts, cb) {
if (typeof opts === 'function') {
cb = opts
opts = {}
}
const overwrite = opts.overwrite || opts.clobber || false
stat.checkPaths(src, dest, 'move', (err, stats) => {
if (err) return cb(err)
const { srcStat } = stats
stat.checkParentPaths(src, srcStat, dest, 'move', err => {
if (err) return cb(err)
mkdirp(path.dirname(dest), err => {
if (err) return cb(err)
return doRename(src, dest, overwrite, cb)
})
})
})
}
function doRename (src, dest, overwrite, cb) {
if (overwrite) {
return remove(dest, err => {
if (err) return cb(err)
return rename(src, dest, overwrite, cb)
})
}
pathExists(dest, (err, destExists) => {
if (err) return cb(err)
if (destExists) return cb(new Error('dest already exists.'))
return rename(src, dest, overwrite, cb)
})
}
function rename (src, dest, overwrite, cb) {
fs.rename(src, dest, err => {
if (!err) return cb()
if (err.code !== 'EXDEV') return cb(err)
return moveAcrossDevice(src, dest, overwrite, cb)
})
}
function moveAcrossDevice (src, dest, overwrite, cb) {
const opts = {
overwrite,
errorOnExist: true
}
copy(src, dest, opts, err => {
if (err) return cb(err)
return remove(src, cb)
})
}
module.exports = move
'use strict'
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function outputFile (file, data, encoding, callback) {
if (typeof encoding === 'function') {
callback = encoding
encoding = 'utf8'
}
const dir = path.dirname(file)
pathExists(dir, (err, itDoes) => {
if (err) return callback(err)
if (itDoes) return fs.writeFile(file, data, encoding, callback)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
fs.writeFile(file, data, encoding, callback)
})
})
}
function outputFileSync (file, ...args) {
const dir = path.dirname(file)
if (fs.existsSync(dir)) {
return fs.writeFileSync(file, ...args)
}
mkdir.mkdirsSync(dir)
fs.writeFileSync(file, ...args)
}
module.exports = {
outputFile: u(outputFile),
outputFileSync
}
'use strict'
const u = require('universalify').fromPromise
const fs = require('../fs')
function pathExists (path) {
return fs.access(path).then(() => true).catch(() => false)
}
module.exports = {
pathExists: u(pathExists),
pathExistsSync: fs.existsSync
}
'use strict'
const u = require('universalify').fromCallback
const rimraf = require('./rimraf')
module.exports = {
remove: u(rimraf),
removeSync: rimraf.sync
}
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const assert = require('assert')
const isWindows = (process.platform === 'win32')
function defaults (options) {
const methods = [
'unlink',
'chmod',
'stat',
'lstat',
'rmdir',
'readdir'
]
methods.forEach(m => {
options[m] = options[m] || fs[m]
m = m + 'Sync'
options[m] = options[m] || fs[m]
})
options.maxBusyTries = options.maxBusyTries || 3
}
function rimraf (p, options, cb) {
let busyTries = 0
if (typeof options === 'function') {
cb = options
options = {}
}
assert(p, 'rimraf: missing path')
assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')
assert.strictEqual(typeof cb, 'function', 'rimraf: callback function required')
assert(options, 'rimraf: invalid options argument provided')
assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')
defaults(options)
rimraf_(p, options, function CB (er) {
if (er) {
if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
busyTries < options.maxBusyTries) {
busyTries++
const time = busyTries * 100
// try again, with the same exact callback as this one.
return setTimeout(() => rimraf_(p, options, CB), time)
}
// already gone
if (er.code === 'ENOENT') er = null
}
cb(er)
})
}
// Two possible strategies.
// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
//
// Both result in an extra syscall when you guess wrong. However, there
// are likely far more normal files in the world than directories. This
// is based on the assumption that a the average number of files per
// directory is >= 1.
//
// If anyone ever complains about this, then I guess the strategy could
// be made configurable somehow. But until then, YAGNI.
function rimraf_ (p, options, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
// sunos lets the root user unlink directories, which is... weird.
// so we have to lstat here and make sure it's not a dir.
options.lstat(p, (er, st) => {
if (er && er.code === 'ENOENT') {
return cb(null)
}
// Windows can EPERM on stat. Life is suffering.
if (er && er.code === 'EPERM' && isWindows) {
return fixWinEPERM(p, options, er, cb)
}
if (st && st.isDirectory()) {
return rmdir(p, options, er, cb)
}
options.unlink(p, er => {
if (er) {
if (er.code === 'ENOENT') {
return cb(null)
}
if (er.code === 'EPERM') {
return (isWindows)
? fixWinEPERM(p, options, er, cb)
: rmdir(p, options, er, cb)
}
if (er.code === 'EISDIR') {
return rmdir(p, options, er, cb)
}
}
return cb(er)
})
})
}
function fixWinEPERM (p, options, er, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
if (er) {
assert(er instanceof Error)
}
options.chmod(p, 0o666, er2 => {
if (er2) {
cb(er2.code === 'ENOENT' ? null : er)
} else {
options.stat(p, (er3, stats) => {
if (er3) {
cb(er3.code === 'ENOENT' ? null : er)
} else if (stats.isDirectory()) {
rmdir(p, options, er, cb)
} else {
options.unlink(p, cb)
}
})
}
})
}
function fixWinEPERMSync (p, options, er) {
let stats
assert(p)
assert(options)
if (er) {
assert(er instanceof Error)
}
try {
options.chmodSync(p, 0o666)
} catch (er2) {
if (er2.code === 'ENOENT') {
return
} else {
throw er
}
}
try {
stats = options.statSync(p)
} catch (er3) {
if (er3.code === 'ENOENT') {
return
} else {
throw er
}
}
if (stats.isDirectory()) {
rmdirSync(p, options, er)
} else {
options.unlinkSync(p)
}
}
function rmdir (p, options, originalEr, cb) {
assert(p)
assert(options)
if (originalEr) {
assert(originalEr instanceof Error)
}
assert(typeof cb === 'function')
// try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
// if we guessed wrong, and it's not a directory, then
// raise the original error.
options.rmdir(p, er => {
if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {
rmkids(p, options, cb)
} else if (er && er.code === 'ENOTDIR') {
cb(originalEr)
} else {
cb(er)
}
})
}
function rmkids (p, options, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
options.readdir(p, (er, files) => {
if (er) return cb(er)
let n = files.length
let errState
if (n === 0) return options.rmdir(p, cb)
files.forEach(f => {
rimraf(path.join(p, f), options, er => {
if (errState) {
return
}
if (er) return cb(errState = er)
if (--n === 0) {
options.rmdir(p, cb)
}
})
})
})
}
// this looks simpler, and is strictly *faster*, but will
// tie up the JavaScript thread and fail on excessively
// deep directory trees.
function rimrafSync (p, options) {
let st
options = options || {}
defaults(options)
assert(p, 'rimraf: missing path')
assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')
assert(options, 'rimraf: missing options')
assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')
try {
st = options.lstatSync(p)
} catch (er) {
if (er.code === 'ENOENT') {
return
}
// Windows can EPERM on stat. Life is suffering.
if (er.code === 'EPERM' && isWindows) {
fixWinEPERMSync(p, options, er)
}
}
try {
// sunos lets the root user unlink directories, which is... weird.
if (st && st.isDirectory()) {
rmdirSync(p, options, null)
} else {
options.unlinkSync(p)
}
} catch (er) {
if (er.code === 'ENOENT') {
return
} else if (er.code === 'EPERM') {
return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
} else if (er.code !== 'EISDIR') {
throw er
}
rmdirSync(p, options, er)
}
}
function rmdirSync (p, options, originalEr) {
assert(p)
assert(options)
if (originalEr) {
assert(originalEr instanceof Error)
}
try {
options.rmdirSync(p)
} catch (er) {
if (er.code === 'ENOTDIR') {
throw originalEr
} else if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {
rmkidsSync(p, options)
} else if (er.code !== 'ENOENT') {
throw er
}
}
}
function rmkidsSync (p, options) {
assert(p)
assert(options)
options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))
if (isWindows) {
// We only end up here once we got ENOTEMPTY at least once, and
// at this point, we are guaranteed to have removed all the kids.
// So, we know that it won't be ENOENT or ENOTDIR or anything else.
// try really hard to delete stuff on windows, because it has a
// PROFOUNDLY annoying habit of not closing handles promptly when
// files are deleted, resulting in spurious ENOTEMPTY errors.
const startTime = Date.now()
do {
try {
const ret = options.rmdirSync(p, options)
return ret
} catch (er) { }
} while (Date.now() - startTime < 500) // give up after 500ms
} else {
const ret = options.rmdirSync(p, options)
return ret
}
}
module.exports = rimraf
rimraf.sync = rimrafSync
'use strict'
/* eslint-disable node/no-deprecated-api */
module.exports = function (size) {
if (typeof Buffer.allocUnsafe === 'function') {
try {
return Buffer.allocUnsafe(size)
} catch (e) {
return new Buffer(size)
}
}
return new Buffer(size)
}
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const NODE_VERSION_MAJOR_WITH_BIGINT = 10
const NODE_VERSION_MINOR_WITH_BIGINT = 5
const NODE_VERSION_PATCH_WITH_BIGINT = 0
const nodeVersion = process.versions.node.split('.')
const nodeVersionMajor = Number.parseInt(nodeVersion[0], 10)
const nodeVersionMinor = Number.parseInt(nodeVersion[1], 10)
const nodeVersionPatch = Number.parseInt(nodeVersion[2], 10)
function nodeSupportsBigInt () {
if (nodeVersionMajor > NODE_VERSION_MAJOR_WITH_BIGINT) {
return true
} else if (nodeVersionMajor === NODE_VERSION_MAJOR_WITH_BIGINT) {
if (nodeVersionMinor > NODE_VERSION_MINOR_WITH_BIGINT) {
return true
} else if (nodeVersionMinor === NODE_VERSION_MINOR_WITH_BIGINT) {
if (nodeVersionPatch >= NODE_VERSION_PATCH_WITH_BIGINT) {
return true
}
}
}
return false
}
function getStats (src, dest, cb) {
if (nodeSupportsBigInt()) {
fs.stat(src, { bigint: true }, (err, srcStat) => {
if (err) return cb(err)
fs.stat(dest, { bigint: true }, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb(null, { srcStat, destStat: null })
return cb(err)
}
return cb(null, { srcStat, destStat })
})
})
} else {
fs.stat(src, (err, srcStat) => {
if (err) return cb(err)
fs.stat(dest, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb(null, { srcStat, destStat: null })
return cb(err)
}
return cb(null, { srcStat, destStat })
})
})
}
}
function getStatsSync (src, dest) {
let srcStat, destStat
if (nodeSupportsBigInt()) {
srcStat = fs.statSync(src, { bigint: true })
} else {
srcStat = fs.statSync(src)
}
try {
if (nodeSupportsBigInt()) {
destStat = fs.statSync(dest, { bigint: true })
} else {
destStat = fs.statSync(dest)
}
} catch (err) {
if (err.code === 'ENOENT') return { srcStat, destStat: null }
throw err
}
return { srcStat, destStat }
}
function checkPaths (src, dest, funcName, cb) {
getStats(src, dest, (err, stats) => {
if (err) return cb(err)
const { srcStat, destStat } = stats
if (destStat && destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
return cb(new Error('Source and destination must not be the same.'))
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return cb(null, { srcStat, destStat })
})
}
function checkPathsSync (src, dest, funcName) {
const { srcStat, destStat } = getStatsSync(src, dest)
if (destStat && destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
throw new Error('Source and destination must not be the same.')
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
throw new Error(errMsg(src, dest, funcName))
}
return { srcStat, destStat }
}
// recursively check if dest parent is a subdirectory of src.
// It works for all file types including symlinks since it
// checks the src and dest inodes. It starts from the deepest
// parent and stops once it reaches the src parent or the root path.
function checkParentPaths (src, srcStat, dest, funcName, cb) {
const srcParent = path.resolve(path.dirname(src))
const destParent = path.resolve(path.dirname(dest))
if (destParent === srcParent || destParent === path.parse(destParent).root) return cb()
if (nodeSupportsBigInt()) {
fs.stat(destParent, { bigint: true }, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb()
return cb(err)
}
if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return checkParentPaths(src, srcStat, destParent, funcName, cb)
})
} else {
fs.stat(destParent, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb()
return cb(err)
}
if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return checkParentPaths(src, srcStat, destParent, funcName, cb)
})
}
}
function checkParentPathsSync (src, srcStat, dest, funcName) {
const srcParent = path.resolve(path.dirname(src))
const destParent = path.resolve(path.dirname(dest))
if (destParent === srcParent || destParent === path.parse(destParent).root) return
let destStat
try {
if (nodeSupportsBigInt()) {
destStat = fs.statSync(destParent, { bigint: true })
} else {
destStat = fs.statSync(destParent)
}
} catch (err) {
if (err.code === 'ENOENT') return
throw err
}
if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
throw new Error(errMsg(src, dest, funcName))
}
return checkParentPathsSync(src, srcStat, destParent, funcName)
}
// return true if dest is a subdir of src, otherwise false.
// It only checks the path strings.
function isSrcSubdir (src, dest) {
const srcArr = path.resolve(src).split(path.sep).filter(i => i)
const destArr = path.resolve(dest).split(path.sep).filter(i => i)
return srcArr.reduce((acc, cur, i) => acc && destArr[i] === cur, true)
}
function errMsg (src, dest, funcName) {
return `Cannot ${funcName} '${src}' to a subdirectory of itself, '${dest}'.`
}
module.exports = {
checkPaths,
checkPathsSync,
checkParentPaths,
checkParentPathsSync,
isSrcSubdir
}
'use strict'
const fs = require('graceful-fs')
const os = require('os')
const path = require('path')
// HFS, ext{2,3}, FAT do not, Node.js v0.10 does not
function hasMillisResSync () {
let tmpfile = path.join('millis-test-sync' + Date.now().toString() + Math.random().toString().slice(2))
tmpfile = path.join(os.tmpdir(), tmpfile)
// 550 millis past UNIX epoch
const d = new Date(1435410243862)
fs.writeFileSync(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141')
const fd = fs.openSync(tmpfile, 'r+')
fs.futimesSync(fd, d, d)
fs.closeSync(fd)
return fs.statSync(tmpfile).mtime > 1435410243000
}
function hasMillisRes (callback) {
let tmpfile = path.join('millis-test' + Date.now().toString() + Math.random().toString().slice(2))
tmpfile = path.join(os.tmpdir(), tmpfile)
// 550 millis past UNIX epoch
const d = new Date(1435410243862)
fs.writeFile(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141', err => {
if (err) return callback(err)
fs.open(tmpfile, 'r+', (err, fd) => {
if (err) return callback(err)
fs.futimes(fd, d, d, err => {
if (err) return callback(err)
fs.close(fd, err => {
if (err) return callback(err)
fs.stat(tmpfile, (err, stats) => {
if (err) return callback(err)
callback(null, stats.mtime > 1435410243000)
})
})
})
})
})
}
function timeRemoveMillis (timestamp) {
if (typeof timestamp === 'number') {
return Math.floor(timestamp / 1000) * 1000
} else if (timestamp instanceof Date) {
return new Date(Math.floor(timestamp.getTime() / 1000) * 1000)
} else {
throw new Error('fs-extra: timeRemoveMillis() unknown parameter type')
}
}
function utimesMillis (path, atime, mtime, callback) {
// if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
fs.open(path, 'r+', (err, fd) => {
if (err) return callback(err)
fs.futimes(fd, atime, mtime, futimesErr => {
fs.close(fd, closeErr => {
if (callback) callback(futimesErr || closeErr)
})
})
})
}
function utimesMillisSync (path, atime, mtime) {
const fd = fs.openSync(path, 'r+')
fs.futimesSync(fd, atime, mtime)
return fs.closeSync(fd)
}
module.exports = {
hasMillisRes,
hasMillisResSync,
timeRemoveMillis,
utimesMillis,
utimesMillisSync
}
{
"name": "fs-extra",
"version": "8.1.0",
"description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf.",
"engines": {
"node": ">=6 <7 || >=8"
},
"homepage": "https://github.com/jprichardson/node-fs-extra",
"repository": {
"type": "git",
"url": "https://github.com/jprichardson/node-fs-extra"
},
"keywords": [
"fs",
"file",
"file system",
"copy",
"directory",
"extra",
"mkdirp",
"mkdir",
"mkdirs",
"recursive",
"json",
"read",
"write",
"extra",
"delete",
"remove",
"touch",
"create",
"text",
"output",
"move"
],
"author": "JP Richardson <jprichardson@gmail.com>",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"devDependencies": {
"coveralls": "^3.0.0",
"istanbul": "^0.4.5",
"klaw": "^2.1.1",
"klaw-sync": "^3.0.2",
"minimist": "^1.1.1",
"mocha": "^5.0.5",
"proxyquire": "^2.0.1",
"read-dir-files": "^0.1.1",
"semver": "^5.3.0",
"standard": "^12.0.1"
},
"main": "./lib/index.js",
"files": [
"lib/",
"!lib/**/__tests__/"
],
"scripts": {
"full-ci": "npm run lint && npm run coverage",
"coverage": "istanbul cover -i 'lib/**' -x '**/__tests__/**' test.js",
"coveralls": "coveralls < coverage/lcov.info",
"lint": "standard",
"test-find": "find ./lib/**/__tests__ -name *.test.js | xargs mocha",
"test": "npm run lint && npm run unit",
"unit": "node test.js"
}
}
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.