Heeyeon

Add member_join function and Modify overall structure

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