UNPKG

23.4 kBJavaScriptView Raw
1'use strict';
2
3var path = require('path');
4
5var File = require('./file');
6var FileDescriptor = require('./descriptor');
7var Directory = require('./directory');
8var SymbolicLink = require('./symlink');
9var FSError = require('./error');
10
11var constants = process.binding('constants');
12
13
14/**
15 * Call the provided function and either return the result or call the callback
16 * with it (depending on if a callback is provided).
17 * @param {function()} callback Optional callback.
18 * @param {Object} thisArg This argument for the following function.
19 * @param {function()} func Function to call.
20 * @return {*} Return (if callback is not provided).
21 */
22function maybeCallback(callback, thisArg, func) {
23 if (callback) {
24 var err = null;
25 var val;
26 try {
27 val = func.call(thisArg);
28 } catch (e) {
29 err = e;
30 }
31 // Unpack callback from FSReqWrap
32 callback = callback.oncomplete || callback;
33 process.nextTick(function() {
34 if (val === undefined) {
35 callback(err);
36 } else {
37 callback(err, val);
38 }
39 });
40 } else {
41 return func.call(thisArg);
42 }
43}
44
45function notImplemented() {
46 throw new Error('Method not implemented');
47}
48
49
50/**
51 * Create a new stats object.
52 * @param {Object} config Stats properties.
53 * @constructor
54 */
55function Stats(config) {
56 for (var key in config) {
57 this[key] = config[key];
58 }
59}
60
61
62/**
63 * Check if mode indicates property.
64 * @param {number} property Property to check.
65 * @return {boolean} Property matches mode.
66 */
67Stats.prototype._checkModeProperty = function(property) {
68 return ((this.mode & constants.S_IFMT) === property);
69};
70
71
72/**
73 * @return {Boolean} Is a directory.
74 */
75Stats.prototype.isDirectory = function() {
76 return this._checkModeProperty(constants.S_IFDIR);
77};
78
79
80/**
81 * @return {Boolean} Is a regular file.
82 */
83Stats.prototype.isFile = function() {
84 return this._checkModeProperty(constants.S_IFREG);
85};
86
87
88/**
89 * @return {Boolean} Is a block device.
90 */
91Stats.prototype.isBlockDevice = function() {
92 return this._checkModeProperty(constants.S_IFBLK);
93};
94
95
96/**
97 * @return {Boolean} Is a character device.
98 */
99Stats.prototype.isCharacterDevice = function() {
100 return this._checkModeProperty(constants.S_IFCHR);
101};
102
103
104/**
105 * @return {Boolean} Is a symbolic link.
106 */
107Stats.prototype.isSymbolicLink = function() {
108 return this._checkModeProperty(constants.S_IFLNK);
109};
110
111
112/**
113 * @return {Boolean} Is a named pipe.
114 */
115Stats.prototype.isFIFO = function() {
116 return this._checkModeProperty(constants.S_IFIFO);
117};
118
119
120/**
121 * @return {Boolean} Is a socket.
122 */
123Stats.prototype.isSocket = function() {
124 return this._checkModeProperty(constants.S_IFSOCK);
125};
126
127
128/**
129 * Create a new binding with the given file system.
130 * @param {FileSystem} system Mock file system.
131 * @constructor
132 */
133function Binding(system) {
134
135 /**
136 * Mock file system.
137 * @type {FileSystem}
138 */
139 this._system = system;
140
141 /**
142 * Stats constructor.
143 * @type {function}
144 */
145 this.Stats = Stats;
146
147 /**
148 * Lookup of open files.
149 * @type {Object.<number, FileDescriptor>}
150 */
151 this._openFiles = {};
152
153 /**
154 * Counter for file descriptors.
155 * @type {number}
156 */
157 this._counter = 0;
158
159}
160
161
162/**
163 * Get the file system underlying this binding.
164 * @return {FileSystem} The underlying file system.
165 */
166Binding.prototype.getSystem = function() {
167 return this._system;
168};
169
170
171/**
172 * Reset the file system underlying this binding.
173 * @param {FileSystem} system The new file system.
174 */
175Binding.prototype.setSystem = function(system) {
176 this._system = system;
177};
178
179
180/**
181 * Get a file descriptor.
182 * @param {number} fd File descriptor identifier.
183 * @return {FileDescriptor} File descriptor.
184 */
185Binding.prototype._getDescriptorById = function(fd) {
186 if (!this._openFiles.hasOwnProperty(fd)) {
187 throw new FSError('EBADF');
188 }
189 return this._openFiles[fd];
190};
191
192
193/**
194 * Keep track of a file descriptor as open.
195 * @param {FileDescriptor} descriptor The file descriptor.
196 * @return {number} Identifier for file descriptor.
197 */
198Binding.prototype._trackDescriptor = function(descriptor) {
199 var fd = ++this._counter;
200 this._openFiles[fd] = descriptor;
201 return fd;
202};
203
204
205/**
206 * Stop tracking a file descriptor as open.
207 * @param {number} fd Identifier for file descriptor.
208 */
209Binding.prototype._untrackDescriptorById = function(fd) {
210 if (!this._openFiles.hasOwnProperty(fd)) {
211 throw new FSError('EBADF');
212 }
213 delete this._openFiles[fd];
214};
215
216
217/**
218 * Stat an item.
219 * @param {string} filepath Path.
220 * @param {function(Error, Stats)} callback Callback (optional).
221 * @return {Stats|undefined} Stats or undefined (if sync).
222 */
223Binding.prototype.stat = function(filepath, callback) {
224 return maybeCallback(callback, this, function() {
225 var item = this._system.getItem(filepath);
226 if (item instanceof SymbolicLink) {
227 item = this._system.getItem(
228 path.resolve(path.dirname(filepath), item.getPath()));
229 }
230 if (!item) {
231 throw new FSError('ENOENT', filepath);
232 }
233 return new Stats(item.getStats());
234 });
235};
236
237
238/**
239 * Stat an item.
240 * @param {number} fd File descriptor.
241 * @param {function(Error, Stats)} callback Callback (optional).
242 * @return {Stats|undefined} Stats or undefined (if sync).
243 */
244Binding.prototype.fstat = function(fd, callback) {
245 return maybeCallback(callback, this, function() {
246 var descriptor = this._getDescriptorById(fd);
247 var item = descriptor.getItem();
248 return new Stats(item.getStats());
249 });
250};
251
252
253/**
254 * Close a file descriptor.
255 * @param {number} fd File descriptor.
256 * @param {function(Error)} callback Callback (optional).
257 */
258Binding.prototype.close = function(fd, callback) {
259 maybeCallback(callback, this, function() {
260 this._untrackDescriptorById(fd);
261 });
262};
263
264
265/**
266 * Open and possibly create a file.
267 * @param {string} pathname File path.
268 * @param {number} flags Flags.
269 * @param {number} mode Mode.
270 * @param {function(Error, string)} callback Callback (optional).
271 * @return {string} File descriptor (if sync).
272 */
273Binding.prototype.open = function(pathname, flags, mode, callback) {
274 return maybeCallback(callback, this, function() {
275 var descriptor = new FileDescriptor(flags);
276 var item = this._system.getItem(pathname);
277 if (item instanceof SymbolicLink) {
278 item = this._system.getItem(
279 path.resolve(path.dirname(pathname), item.getPath()));
280 }
281 if (descriptor.isExclusive() && item) {
282 throw new FSError('EEXIST', pathname);
283 }
284 if (descriptor.isCreate() && !item) {
285 var parent = this._system.getItem(path.dirname(pathname));
286 if (!parent) {
287 throw new FSError('ENOENT', pathname);
288 }
289 if (!(parent instanceof Directory)) {
290 throw new FSError('ENOTDIR', pathname);
291 }
292 item = new File();
293 if (mode) {
294 item.setMode(mode);
295 }
296 parent.addItem(path.basename(pathname), item);
297 }
298 if (descriptor.isRead()) {
299 if (!item) {
300 throw new FSError('ENOENT', pathname);
301 }
302 if (!item.canRead()) {
303 throw new FSError('EACCES', pathname);
304 }
305 }
306 if (descriptor.isWrite() && !item.canWrite()) {
307 throw new FSError('EACCES', pathname);
308 }
309 if (descriptor.isTruncate()) {
310 item.setContent('');
311 }
312 if (descriptor.isTruncate() || descriptor.isAppend()) {
313 descriptor.setPosition(item.getContent().length);
314 }
315 descriptor.setItem(item);
316 return this._trackDescriptor(descriptor);
317 });
318};
319
320
321/**
322 * Read from a file descriptor.
323 * @param {string} fd File descriptor.
324 * @param {Buffer} buffer Buffer that the contents will be written to.
325 * @param {number} offset Offset in the buffer to start writing to.
326 * @param {number} length Number of bytes to read.
327 * @param {?number} position Where to begin reading in the file. If null,
328 * data will be read from the current file position.
329 * @param {function(Error, number, Buffer)} callback Callback (optional) called
330 * with any error, number of bytes read, and the buffer.
331 * @return {number} Number of bytes read (if sync).
332 */
333Binding.prototype.read = function(fd, buffer, offset, length, position,
334 callback) {
335 return maybeCallback(callback, this, function() {
336 var descriptor = this._getDescriptorById(fd);
337 if (!descriptor.isRead()) {
338 throw new FSError('EBADF');
339 }
340 var file = descriptor.getItem();
341 if (!(file instanceof File)) {
342 // deleted or not a regular file
343 throw new FSError('EBADF');
344 }
345 if (typeof position !== 'number' || position < 0) {
346 position = descriptor.getPosition();
347 }
348 var content = file.getContent();
349 var start = Math.min(position, content.length);
350 var end = Math.min(position + length, content.length);
351 var read = (start < end) ? content.copy(buffer, offset, start, end) : 0;
352 descriptor.setPosition(position + read);
353 return read;
354 });
355};
356
357
358/**
359 * Write to a file descriptor given a buffer.
360 * @param {string} fd File descriptor.
361 * @param {Buffer} buffer Buffer with contents to write.
362 * @param {number} offset Offset in the buffer to start writing from.
363 * @param {number} length Number of bytes to write.
364 * @param {?number} position Where to begin writing in the file. If null,
365 * data will be written to the current file position.
366 * @param {function(Error, number, Buffer)} callback Callback (optional) called
367 * with any error, number of bytes written, and the buffer.
368 * @return {number} Number of bytes written (if sync).
369 */
370Binding.prototype.writeBuffer = function(fd, buffer, offset, length, position,
371 callback) {
372 return maybeCallback(callback, this, function() {
373 var descriptor = this._getDescriptorById(fd);
374 if (!descriptor.isWrite()) {
375 throw new FSError('EBADF');
376 }
377 var file = descriptor.getItem();
378 if (!(file instanceof File)) {
379 // not a regular file
380 throw new FSError('EBADF');
381 }
382 if (typeof position !== 'number' || position < 0) {
383 position = descriptor.getPosition();
384 }
385 var content = file.getContent();
386 var newLength = position + length;
387 if (content.length < newLength) {
388 var newContent = new Buffer(newLength);
389 content.copy(newContent);
390 content = newContent;
391 }
392 var sourceEnd = Math.min(offset + length, buffer.length);
393 var written = buffer.copy(content, position, offset, sourceEnd);
394 file.setContent(content);
395 descriptor.setPosition(newLength);
396 return written;
397 });
398};
399
400
401/**
402 * Alias for writeBuffer (used in Node <= 0.10).
403 * @param {string} fd File descriptor.
404 * @param {Buffer} buffer Buffer with contents to write.
405 * @param {number} offset Offset in the buffer to start writing from.
406 * @param {number} length Number of bytes to write.
407 * @param {?number} position Where to begin writing in the file. If null,
408 * data will be written to the current file position.
409 * @param {function(Error, number, Buffer)} callback Callback (optional) called
410 * with any error, number of bytes written, and the buffer.
411 * @return {number} Number of bytes written (if sync).
412 */
413Binding.prototype.write = Binding.prototype.writeBuffer;
414
415
416/**
417 * Write to a file descriptor given a string.
418 * @param {string} fd File descriptor.
419 * @param {string} string String with contents to write.
420 * @param {number} position Where to begin writing in the file. If null,
421 * data will be written to the current file position.
422 * @param {string} encoding String encoding.
423 * @param {function(Error, number, string)} callback Callback (optional) called
424 * with any error, number of bytes written, and the string.
425 * @return {number} Number of bytes written (if sync).
426 */
427Binding.prototype.writeString = function(fd, string, position, encoding,
428 callback) {
429 var buffer = new Buffer(string, encoding);
430 var wrapper;
431 if (callback) {
432 callback = callback.oncomplete || callback;
433 wrapper = function(err, written, returned) {
434 callback(err, written, returned && string);
435 };
436 }
437 return this.writeBuffer(fd, buffer, 0, string.length, position, wrapper);
438};
439
440
441/**
442 * Rename a file.
443 * @param {string} oldPath Old pathname.
444 * @param {string} newPath New pathname.
445 * @param {function(Error)} callback Callback (optional).
446 * @return {undefined}
447 */
448Binding.prototype.rename = function(oldPath, newPath, callback) {
449 return maybeCallback(callback, this, function() {
450 var oldItem = this._system.getItem(oldPath);
451 if (!oldItem) {
452 throw new FSError('ENOENT', oldPath);
453 }
454 var oldParent = this._system.getItem(path.dirname(oldPath));
455 var oldName = path.basename(oldPath);
456 var newItem = this._system.getItem(newPath);
457 var newParent = this._system.getItem(path.dirname(newPath));
458 var newName = path.basename(newPath);
459 if (newItem) {
460 // make sure they are the same type
461 if (oldItem instanceof File) {
462 if (newItem instanceof Directory) {
463 throw new FSError('EISDIR', newPath);
464 }
465 } else if (oldItem instanceof Directory) {
466 if (!(newItem instanceof Directory)) {
467 throw new FSError('ENOTDIR', newPath);
468 }
469 if (newItem.list().length > 0) {
470 throw new FSError('ENOTEMPTY', newPath);
471 }
472 }
473 newParent.removeItem(newName);
474 } else {
475 if (!newParent) {
476 throw new FSError('ENOENT', newPath);
477 }
478 if (!(newParent instanceof Directory)) {
479 throw new FSError('ENOTDIR', newPath);
480 }
481 }
482 oldParent.removeItem(oldName);
483 newParent.addItem(newName, oldItem);
484 });
485};
486
487
488/**
489 * Read a directory.
490 * @param {string} dirpath Path to directory.
491 * @param {function(Error, Array.<string>)} callback Callback (optional) called
492 * with any error or array of items in the directory.
493 * @return {Array.<string>} Array of items in directory (if sync).
494 */
495Binding.prototype.readdir = function(dirpath, callback) {
496 return maybeCallback(callback, this, function() {
497 var dpath = dirpath;
498 var dir = this._system.getItem(dirpath);
499 while (dir instanceof SymbolicLink) {
500 dpath = path.resolve(path.dirname(dpath), dir.getPath());
501 dir = this._system.getItem(dpath);
502 }
503 if (!dir) {
504 throw new FSError('ENOENT', dirpath);
505 }
506 if (!(dir instanceof Directory)) {
507 throw new FSError('ENOTDIR', dirpath);
508 }
509 return dir.list();
510 });
511};
512
513
514/**
515 * Create a directory.
516 * @param {string} pathname Path to new directory.
517 * @param {number} mode Permissions.
518 * @param {function(Error)} callback Optional callback.
519 */
520Binding.prototype.mkdir = function(pathname, mode, callback) {
521 maybeCallback(callback, this, function() {
522 var item = this._system.getItem(pathname);
523 if (item) {
524 throw new FSError('EEXIST', pathname);
525 }
526 var parent = this._system.getItem(path.dirname(pathname));
527 if (!parent) {
528 throw new FSError('ENOENT', pathname);
529 }
530 var dir = new Directory();
531 if (mode) {
532 dir.setMode(mode);
533 }
534 parent.addItem(path.basename(pathname), dir);
535 });
536};
537
538
539/**
540 * Remove a directory.
541 * @param {string} pathname Path to directory.
542 * @param {function(Error)} callback Optional callback.
543 */
544Binding.prototype.rmdir = function(pathname, callback) {
545 maybeCallback(callback, this, function() {
546 var item = this._system.getItem(pathname);
547 if (!item) {
548 throw new FSError('ENOENT', pathname);
549 }
550 if (!(item instanceof Directory)) {
551 throw new FSError('ENOTDIR', pathname);
552 }
553 if (item.list().length > 0) {
554 throw new FSError('ENOTEMPTY', pathname);
555 }
556 var parent = this._system.getItem(path.dirname(pathname));
557 parent.removeItem(path.basename(pathname));
558 });
559};
560
561
562/**
563 * Truncate a file.
564 * @param {number} fd File descriptor.
565 * @param {number} len Number of bytes.
566 * @param {function(Error)} callback Optional callback.
567 */
568Binding.prototype.ftruncate = function(fd, len, callback) {
569 maybeCallback(callback, this, function() {
570 var descriptor = this._getDescriptorById(fd);
571 if (!descriptor.isWrite()) {
572 throw new FSError('EINVAL');
573 }
574 var file = descriptor.getItem();
575 if (!(file instanceof File)) {
576 throw new FSError('EINVAL');
577 }
578 var content = file.getContent();
579 var newContent = new Buffer(len);
580 content.copy(newContent);
581 file.setContent(newContent);
582 });
583};
584
585
586/**
587 * Legacy support.
588 * @param {number} fd File descriptor.
589 * @param {number} len Number of bytes.
590 * @param {function(Error)} callback Optional callback.
591 */
592Binding.prototype.truncate = Binding.prototype.ftruncate;
593
594
595/**
596 * Change user and group owner.
597 * @param {string} pathname Path.
598 * @param {number} uid User id.
599 * @param {number} gid Group id.
600 * @param {function(Error)} callback Optional callback.
601 */
602Binding.prototype.chown = function(pathname, uid, gid, callback) {
603 maybeCallback(callback, this, function() {
604 var item = this._system.getItem(pathname);
605 if (!item) {
606 throw new FSError('ENOENT', pathname);
607 }
608 item.setUid(uid);
609 item.setGid(gid);
610 });
611};
612
613
614/**
615 * Change user and group owner.
616 * @param {number} fd File descriptor.
617 * @param {number} uid User id.
618 * @param {number} gid Group id.
619 * @param {function(Error)} callback Optional callback.
620 */
621Binding.prototype.fchown = function(fd, uid, gid, callback) {
622 maybeCallback(callback, this, function() {
623 var descriptor = this._getDescriptorById(fd);
624 var item = descriptor.getItem();
625 item.setUid(uid);
626 item.setGid(gid);
627 });
628};
629
630
631/**
632 * Change permissions.
633 * @param {string} pathname Path.
634 * @param {number} mode Mode.
635 * @param {function(Error)} callback Optional callback.
636 */
637Binding.prototype.chmod = function(pathname, mode, callback) {
638 maybeCallback(callback, this, function() {
639 var item = this._system.getItem(pathname);
640 if (!item) {
641 throw new FSError('ENOENT', pathname);
642 }
643 item.setMode(mode);
644 });
645};
646
647
648/**
649 * Change permissions.
650 * @param {number} fd File descriptor.
651 * @param {number} mode Mode.
652 * @param {function(Error)} callback Optional callback.
653 */
654Binding.prototype.fchmod = function(fd, mode, callback) {
655 maybeCallback(callback, this, function() {
656 var descriptor = this._getDescriptorById(fd);
657 var item = descriptor.getItem();
658 item.setMode(mode);
659 });
660};
661
662
663/**
664 * Delete a named item.
665 * @param {string} pathname Path to item.
666 * @param {function(Error)} callback Optional callback.
667 */
668Binding.prototype.unlink = function(pathname, callback) {
669 maybeCallback(callback, this, function() {
670 var item = this._system.getItem(pathname);
671 if (!item) {
672 throw new FSError('ENOENT', pathname);
673 }
674 if (item instanceof Directory) {
675 throw new FSError('EPERM', pathname);
676 }
677 var parent = this._system.getItem(path.dirname(pathname));
678 parent.removeItem(path.basename(pathname));
679 });
680};
681
682
683/**
684 * Update timestamps.
685 * @param {string} pathname Path to item.
686 * @param {number} atime Access time (in seconds).
687 * @param {number} mtime Modification time (in seconds).
688 * @param {function(Error)} callback Optional callback.
689 */
690Binding.prototype.utimes = function(pathname, atime, mtime, callback) {
691 maybeCallback(callback, this, function() {
692 var item = this._system.getItem(pathname);
693 if (!item) {
694 throw new FSError('ENOENT', pathname);
695 }
696 item.setATime(new Date(atime * 1000));
697 item.setMTime(new Date(mtime * 1000));
698 });
699};
700
701
702/**
703 * Update timestamps.
704 * @param {number} fd File descriptor.
705 * @param {number} atime Access time (in seconds).
706 * @param {number} mtime Modification time (in seconds).
707 * @param {function(Error)} callback Optional callback.
708 */
709Binding.prototype.futimes = function(fd, atime, mtime, callback) {
710 maybeCallback(callback, this, function() {
711 var descriptor = this._getDescriptorById(fd);
712 var item = descriptor.getItem();
713 item.setATime(new Date(atime * 1000));
714 item.setMTime(new Date(mtime * 1000));
715 });
716};
717
718
719/**
720 * Synchronize in-core state with storage device.
721 * @param {number} fd File descriptor.
722 * @param {function(Error)} callback Optional callback.
723 */
724Binding.prototype.fsync = function(fd, callback) {
725 maybeCallback(callback, this, function() {
726 this._getDescriptorById(fd);
727 });
728};
729
730
731/**
732 * Synchronize in-core metadata state with storage device.
733 * @param {number} fd File descriptor.
734 * @param {function(Error)} callback Optional callback.
735 */
736Binding.prototype.fdatasync = function(fd, callback) {
737 maybeCallback(callback, this, function() {
738 this._getDescriptorById(fd);
739 });
740};
741
742
743/**
744 * Create a hard link.
745 * @param {string} srcPath The existing file.
746 * @param {string} destPath The new link to create.
747 * @param {function(Error)} callback Optional callback.
748 */
749Binding.prototype.link = function(srcPath, destPath, callback) {
750 maybeCallback(callback, this, function() {
751 var item = this._system.getItem(srcPath);
752 if (!item) {
753 throw new FSError('ENOENT', srcPath);
754 }
755 if (item instanceof Directory) {
756 throw new FSError('EPERM', srcPath);
757 }
758 if (this._system.getItem(destPath)) {
759 throw new FSError('EEXIST', destPath);
760 }
761 var parent = this._system.getItem(path.dirname(destPath));
762 if (!parent) {
763 throw new FSError('ENOENT', destPath);
764 }
765 if (!(parent instanceof Directory)) {
766 throw new FSError('ENOTDIR', destPath);
767 }
768 parent.addItem(path.basename(destPath), item);
769 });
770};
771
772
773/**
774 * Create a symbolic link.
775 * @param {string} srcPath Path from link to the source file.
776 * @param {string} destPath Path for the generated link.
777 * @param {string} type Ignored (used for Windows only).
778 * @param {function(Error)} callback Optional callback.
779 */
780Binding.prototype.symlink = function(srcPath, destPath, type, callback) {
781 maybeCallback(callback, this, function() {
782 if (this._system.getItem(destPath)) {
783 throw new FSError('EEXIST', destPath);
784 }
785 var parent = this._system.getItem(path.dirname(destPath));
786 if (!parent) {
787 throw new FSError('ENOENT', destPath);
788 }
789 if (!(parent instanceof Directory)) {
790 throw new FSError('ENOTDIR', destPath);
791 }
792 var link = new SymbolicLink();
793 link.setPath(srcPath);
794 parent.addItem(path.basename(destPath), link);
795 });
796};
797
798
799/**
800 * Read the contents of a symbolic link.
801 * @param {string} pathname Path to symbolic link.
802 * @param {function(Error, string)} callback Optional callback.
803 * @return {string} Symbolic link contents (path to source).
804 */
805Binding.prototype.readlink = function(pathname, callback) {
806 return maybeCallback(callback, this, function() {
807 var link = this._system.getItem(pathname);
808 if (!(link instanceof SymbolicLink)) {
809 throw new FSError('EINVAL', pathname);
810 }
811 return link.getPath();
812 });
813};
814
815
816/**
817 * Stat an item.
818 * @param {string} filepath Path.
819 * @param {function(Error, Stats)} callback Callback (optional).
820 * @return {Stats|undefined} Stats or undefined (if sync).
821 */
822Binding.prototype.lstat = function(filepath, callback) {
823 return maybeCallback(callback, this, function() {
824 var item = this._system.getItem(filepath);
825 if (!item) {
826 throw new FSError('ENOENT', filepath);
827 }
828 return new Stats(item.getStats());
829 });
830};
831
832
833/**
834 * Not yet implemented.
835 * @type {function()}
836 */
837Binding.prototype.StatWatcher = notImplemented;
838
839
840/**
841 * Export the binding constructor.
842 * @type {function()}
843 */
844exports = module.exports = Binding;