Initial commit
- Partial re-write of some of the core filer implementation. - Makes use of ES6/ES7. - Core support for VFS and block storage.
This commit is contained in:
commit
47676be805
|
@ -0,0 +1,5 @@
|
||||||
|
node_modules
|
||||||
|
bower_components
|
||||||
|
.env
|
||||||
|
*~
|
||||||
|
dist/filer-issue225.js
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,459 @@
|
||||||
|
{
|
||||||
|
"name": "filer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": {
|
||||||
|
"version": "7.0.0-beta.49",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.49.tgz",
|
||||||
|
"integrity": "sha1-vs2AVIJzREDJ0TfkbXc0DmTX9Rs=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/highlight": "7.0.0-beta.49"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/highlight": {
|
||||||
|
"version": "7.0.0-beta.49",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.49.tgz",
|
||||||
|
"integrity": "sha1-lr3GtD4TSCASumaRsQGEktOWIsw=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"chalk": "^2.0.0",
|
||||||
|
"esutils": "^2.0.2",
|
||||||
|
"js-tokens": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/estree": {
|
||||||
|
"version": "0.0.39",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||||
|
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "10.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.0.tgz",
|
||||||
|
"integrity": "sha512-hWzNviaVFIr1TqcRA8ou49JaSHp+Rfabmnqg2kNvusKqLhPU0rIsGPUj5WJJ7ld4Bb7qdgLmIhLfCD1qS08IVA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"balanced-match": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||||
|
},
|
||||||
|
"base-x": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-QtPXF0dPnqAiB/bRqh9CaRPut6w="
|
||||||
|
},
|
||||||
|
"base64-arraybuffer": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
|
||||||
|
},
|
||||||
|
"base64-js": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
|
||||||
|
},
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browser-stdout": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
|
||||||
|
},
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-YkIRgwsZwJWTnyQrsBTWefizHh+8GYj3kbL1BTiAQ/9pwpino0G7B2gp5tx/FUBqUlvtxV85KNR3mwfAtv15Yw==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.0.2",
|
||||||
|
"ieee754": "^1.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buffer-from": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ=="
|
||||||
|
},
|
||||||
|
"builtin-modules": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "2.15.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
||||||
|
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
|
||||||
|
},
|
||||||
|
"concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff": {
|
||||||
|
"version": "3.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||||
|
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
|
||||||
|
},
|
||||||
|
"escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||||
|
},
|
||||||
|
"esutils": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||||
|
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||||
|
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"growl": {
|
||||||
|
"version": "1.10.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
|
||||||
|
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA=="
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||||
|
},
|
||||||
|
"he": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
|
||||||
|
},
|
||||||
|
"ieee754": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
|
||||||
|
"integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA=="
|
||||||
|
},
|
||||||
|
"inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"is-module": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"js-tokens": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||||
|
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "0.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mocha": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
|
||||||
|
"requires": {
|
||||||
|
"browser-stdout": "1.3.1",
|
||||||
|
"commander": "2.15.1",
|
||||||
|
"debug": "3.1.0",
|
||||||
|
"diff": "3.5.0",
|
||||||
|
"escape-string-regexp": "1.0.5",
|
||||||
|
"glob": "7.1.2",
|
||||||
|
"growl": "1.10.5",
|
||||||
|
"he": "1.1.1",
|
||||||
|
"minimatch": "3.0.4",
|
||||||
|
"mkdirp": "0.5.1",
|
||||||
|
"supports-color": "5.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"node-uuid": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
|
||||||
|
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
|
||||||
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||||
|
},
|
||||||
|
"path-parse": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"querystringify": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw=="
|
||||||
|
},
|
||||||
|
"requires-port": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||||
|
},
|
||||||
|
"resolve": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"path-parse": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rollup": {
|
||||||
|
"version": "0.59.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-0.59.4.tgz",
|
||||||
|
"integrity": "sha512-ISiMqq/aJa+57QxX2MRcvLESHdJ7wSavmr6U1euMr+6UgFe6KM+3QANrYy8LQofwhTC1I7BcAdlLnDiaODs1BA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/estree": "0.0.39",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rollup-plugin-node-resolve": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-9zHGr3oUJq6G+X0oRMYlzid9fXicBdiydhwGChdyeNRGPcN/majtegApRKHLR5drboUvEWU+QeUmGTyEZQs3WA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"builtin-modules": "^2.0.0",
|
||||||
|
"is-module": "^1.0.0",
|
||||||
|
"resolve": "^1.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rollup-plugin-uglify": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-f6W31EQLzxSEYfN3x6/lyljHqXSoCjXKcTsnwz3evQvHgU1+qTzU2SE0SIG7tbAvaCewp2UaZ5x3k6nYsxOP9A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.0.0-beta.47",
|
||||||
|
"uglify-js": "^3.3.25"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"should": {
|
||||||
|
"version": "13.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/should/-/should-13.2.1.tgz",
|
||||||
|
"integrity": "sha512-l+/NwEMO+DcstsHEwPHRHzC9j4UOE3VQwJGcMWSsD/vqpqHbnQ+1iSHy64Ihmmjx1uiRPD9pFadTSc3MJtXAgw==",
|
||||||
|
"requires": {
|
||||||
|
"should-equal": "^2.0.0",
|
||||||
|
"should-format": "^3.0.3",
|
||||||
|
"should-type": "^1.4.0",
|
||||||
|
"should-type-adaptors": "^1.0.1",
|
||||||
|
"should-util": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"should-equal": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
|
||||||
|
"requires": {
|
||||||
|
"should-type": "^1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"should-format": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
|
||||||
|
"integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
|
||||||
|
"requires": {
|
||||||
|
"should-type": "^1.3.0",
|
||||||
|
"should-type-adaptors": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"should-type": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM="
|
||||||
|
},
|
||||||
|
"should-type-adaptors": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
|
||||||
|
"requires": {
|
||||||
|
"should-type": "^1.3.0",
|
||||||
|
"should-util": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"should-util": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM="
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||||
|
},
|
||||||
|
"source-map-support": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
|
||||||
|
"requires": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
|
||||||
|
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uglify-js": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-Jcf5naPkX3rVPSQpRn9Vm6Rr572I1gTtR9LnqKgXjmOgfYQ/QS0V2WRStFR53Bdj520M66aCZqt9uzYXgtGrJQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"commander": "~2.15.0",
|
||||||
|
"source-map": "~0.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"url-parse": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==",
|
||||||
|
"requires": {
|
||||||
|
"querystringify": "^2.0.0",
|
||||||
|
"requires-port": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid-base62": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid-base62/-/uuid-base62-0.1.0.tgz",
|
||||||
|
"integrity": "sha1-oqhTuYvguq7k917kG8PY5aFcD34=",
|
||||||
|
"requires": {
|
||||||
|
"base-x": "^1.0.0",
|
||||||
|
"node-uuid": "^1.4.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid-parse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-9GV3F2JLDkuIrzb5jYlYmlu+5Wk="
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"name": "filer",
|
||||||
|
"description": "Node-like file system for browsers",
|
||||||
|
"keywords": [
|
||||||
|
"fs",
|
||||||
|
"node",
|
||||||
|
"file",
|
||||||
|
"system",
|
||||||
|
"browser",
|
||||||
|
"indexeddb",
|
||||||
|
"idb",
|
||||||
|
"websql"
|
||||||
|
],
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Alan K <ack@modeswitch.org> (http://blog.modeswitch.org)",
|
||||||
|
"homepage": "http://filerjs.github.io/filer",
|
||||||
|
"bugs": "https://github.com/filerjs/filer/issues",
|
||||||
|
"license": "BSD",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup -c --environment=development",
|
||||||
|
"test": "node node_modules/mocha/bin/mocha test/"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/filerjs/filer.git"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"base64-arraybuffer": "^0.1.2",
|
||||||
|
"buffer": "^5.1.0",
|
||||||
|
"debug": "^3.1.0",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"mocha": "^5.2.0",
|
||||||
|
"should": "^13.2.1",
|
||||||
|
"source-map-support": "^0.5.6",
|
||||||
|
"url-parse": "^1.4.1",
|
||||||
|
"uuid-base62": "^0.1.0",
|
||||||
|
"uuid-parse": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"rollup": "^0.59.4",
|
||||||
|
"rollup-plugin-node-resolve": "^3.3.0",
|
||||||
|
"rollup-plugin-uglify": "^4.0.0",
|
||||||
|
"semver": "^5.5.0"
|
||||||
|
},
|
||||||
|
"main": "dist/filer.js",
|
||||||
|
"module": "src/index.js"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// import pkg from "./package.json";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
input: "src/index.js",
|
||||||
|
output: {
|
||||||
|
name: "Filer",
|
||||||
|
file: "dist/filer.js",
|
||||||
|
format: "umd",
|
||||||
|
sourcemap: "inline",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,76 @@
|
||||||
|
const INSPECT_MAX_BYTES = 50;
|
||||||
|
const K_MAX_LENGTH = 0x7fffffff;
|
||||||
|
|
||||||
|
class Buffer extends Uint8Array
|
||||||
|
{
|
||||||
|
constructor(arg, encodingOrOffset, length)
|
||||||
|
{
|
||||||
|
if (typeof arg === "number") {
|
||||||
|
if (typeof encodingOrOffset === "string") {
|
||||||
|
throw new TypeError(`The "string" argument must be of type string. Received type number`);
|
||||||
|
}
|
||||||
|
return allocUnsafe(arg);
|
||||||
|
}
|
||||||
|
return from(arg, encodingOrOffset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get INSPECT_MAX_BYTES() { return 50 }
|
||||||
|
|
||||||
|
static get K_MAX_LENGTH() { return 0x7fffffff }
|
||||||
|
|
||||||
|
static isSupported()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
var arr = new Uint8Array(1)
|
||||||
|
arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
|
||||||
|
return arr.foo() === 42
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static from(value, encodingOrOffset, length)
|
||||||
|
{
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return fromString(value, encodingOrOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ArrayBuffer.isView(value)) {
|
||||||
|
return fromArrayLike(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
throw TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ${typeof value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInstance(value, ArrayBuffer) ||
|
||||||
|
(value && isInstance(value.buffer, ArrayBuffer))) {
|
||||||
|
return fromArrayBuffer(value, encodingOrOffset, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
throw new TypeError(
|
||||||
|
'The "value" argument must not be of type number. Received type number'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueOf = value.valueOf && value.valueOf();
|
||||||
|
if(valueOf != null && valueOf !== value) {
|
||||||
|
return Buffer.from(valueOf, encodingOrOffset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var b = fromObject(value);
|
||||||
|
if(b) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof Symbol !== "undefined" && Symbol.toPrimitive != null &&
|
||||||
|
typeof value[Symbol.toPrimitive] === "function") {
|
||||||
|
return Buffer.from(value[Symbol.toPrimitive]("string"), encodingOrOffset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError(`The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type ${typeof value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Buffer;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import Buffer from "./buffer";
|
||||||
|
|
||||||
|
export const SUPER_NODE_ID = "0000000000000000000000";
|
||||||
|
|
||||||
|
export const MODE_FILE = "FILE";
|
||||||
|
export const MODE_DIRECTORY = "DIRECTORY";
|
||||||
|
export const MODE_SYMBOLIC_LINK = "MODE_SYMBOLIC_LINK";
|
||||||
|
export const MODE_META = "META";
|
||||||
|
export const MODE_SOCKET = "SOCKET";
|
||||||
|
export const MODE_FIFO = "FIFO";
|
||||||
|
export const MODE_CHARACTER_DEVICE = "CHARACTER_DEVICE";
|
||||||
|
export const MODE_BLOCK_DEVICE = "BLOCK_DEVICE";
|
||||||
|
|
||||||
|
export const ROOT_DIRECTORY_NAME = "/"; // basename(normalize(path))
|
||||||
|
|
||||||
|
export const STDIN = 0;
|
||||||
|
export const STDOUT = 1;
|
||||||
|
export const STDERR = 2;
|
||||||
|
|
||||||
|
export const FIRST_DESCRIPTOR = 3;
|
||||||
|
|
||||||
|
export const N_VFS_DESCRIPTORS = 1024;
|
||||||
|
|
||||||
|
export const DATA_BLOCK_SEPARATOR = "#";
|
||||||
|
|
||||||
|
export const MNT_READ_ONLY = "READ_ONLY";
|
||||||
|
|
||||||
|
export const SYMLOOP_MAX = 10;
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Platform from "./platform";
|
||||||
|
import E from "./errors";
|
||||||
|
|
||||||
|
let Crypto;
|
||||||
|
if(Platform.supportsWebCrypto()) {
|
||||||
|
Crypto = class Crypto
|
||||||
|
{
|
||||||
|
static randomBytes(arrayBuffer)
|
||||||
|
{
|
||||||
|
return window.crypto.getRandomValues(arrayBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(Platform.supportsNodeCrypto()) {
|
||||||
|
let nodeCrypto = require("crypto");
|
||||||
|
Crypto = class Crypto
|
||||||
|
{
|
||||||
|
static randomBytes(arrayBuffer)
|
||||||
|
{
|
||||||
|
return nodeCrypto.randomFillSync(arrayBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new E.ENOTSUPPORTED("crypto support is not available on this platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Crypto;
|
|
@ -0,0 +1,68 @@
|
||||||
|
class FilerError extends Error
|
||||||
|
{
|
||||||
|
constructor(message, path = null)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors = {};
|
||||||
|
const errorDefinitions =
|
||||||
|
[
|
||||||
|
{ errno: -1, name: "UNKNOWN", text: "unknown error" },
|
||||||
|
{ errno: 0, name: "OK", text: "success" },
|
||||||
|
{ errno: 1, name: "EOF", text: "end of file" },
|
||||||
|
|
||||||
|
{ errno: 9, name: "EBADF", text: "bad file descriptor" },
|
||||||
|
{ errno: 10, name: "EBUSY", text: "resource busy or locked" },
|
||||||
|
|
||||||
|
{ errno: 18, name: "EINVAL", text: "invalid argument" },
|
||||||
|
|
||||||
|
{ errno: 27, name: "ENOTDIR", text: "not a directory" },
|
||||||
|
{ errno: 28, name: "EISDIR", text: "illegal operation on directory" },
|
||||||
|
|
||||||
|
{ errno: 34, name: "ENOENT", text: "no such file or directory" },
|
||||||
|
|
||||||
|
{ errno: 47, name: "EEXIST", text: "file already exists" },
|
||||||
|
|
||||||
|
{ errno: 50, name: "EPERM", text: "operation not permitted" },
|
||||||
|
{ errno: 51, name: "ELOOP", text: "too many symbolic links encountered" },
|
||||||
|
|
||||||
|
{ errno: 53, name: "ENOTEMPTY", text: "directory not empty" },
|
||||||
|
|
||||||
|
{ errno: 55, name: "EIO", text: "i/o error" },
|
||||||
|
{ errno: 56, name: "EROFS", text: "read-only file system" },
|
||||||
|
{ errno: 57, name: "ENODEV", text: "no such device" },
|
||||||
|
|
||||||
|
{ errno: 58, name: "ECANCELED", text: "operation canceled" },
|
||||||
|
|
||||||
|
{ errno: 1000, name: "ENOTSUPPORTED", text: "platform is not supported" },
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let error of errorDefinitions) {
|
||||||
|
errors[error.errno] = errors[error.name] = class extends FilerError {
|
||||||
|
constructor(message, path)
|
||||||
|
{
|
||||||
|
super(message || error.text, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() { return error.name }
|
||||||
|
|
||||||
|
get code() { return error.name }
|
||||||
|
|
||||||
|
get errno() { return error.errno }
|
||||||
|
|
||||||
|
get message() { return this.message }
|
||||||
|
|
||||||
|
get stack() { return (new Error(this.message)).stack }
|
||||||
|
|
||||||
|
get toString() {
|
||||||
|
pathInfo = this.path ? (', \'' + this.path + '\'') : '';
|
||||||
|
return `${this.name}: ${this.message}${pathInfo}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default errors;
|
|
@ -0,0 +1,204 @@
|
||||||
|
import E from "./errors";
|
||||||
|
|
||||||
|
function normalizeArray(parts, allowAboveRoot) {
|
||||||
|
// if the path tries to go above the root, `up` ends up > 0
|
||||||
|
var up = 0;
|
||||||
|
for (var i = parts.length - 1; i >= 0; i--) {
|
||||||
|
var last = parts[i];
|
||||||
|
if (last === '.') {
|
||||||
|
parts.splice(i, 1);
|
||||||
|
} else if (last === '..') {
|
||||||
|
parts.splice(i, 1);
|
||||||
|
up++;
|
||||||
|
} else if (up) {
|
||||||
|
parts.splice(i, 1);
|
||||||
|
up--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the path is allowed to go above the root, restore leading ..s
|
||||||
|
if (allowAboveRoot) {
|
||||||
|
for (; up--; up) {
|
||||||
|
parts.unshift('..');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split a filename into [root, dir, basename, ext], unix version
|
||||||
|
// 'root' is just a slash, or nothing.
|
||||||
|
var splitPathRe =
|
||||||
|
/^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/;
|
||||||
|
var splitPath = function(filename) {
|
||||||
|
var result = splitPathRe.exec(filename);
|
||||||
|
return [result[1] || '', result[2] || '', result[3] || '', result[4] || ''];
|
||||||
|
};
|
||||||
|
|
||||||
|
// path.resolve([from ...], to)
|
||||||
|
export function resolve() {
|
||||||
|
var resolvedPath = '',
|
||||||
|
resolvedAbsolute = false;
|
||||||
|
|
||||||
|
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
||||||
|
// XXXfiler: we don't have process.cwd() so we use '/' as a fallback
|
||||||
|
var path = (i >= 0) ? arguments[i] : '/';
|
||||||
|
|
||||||
|
// Skip empty and invalid entries
|
||||||
|
if (typeof path !== 'string' || !path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedPath = path + '/' + resolvedPath;
|
||||||
|
resolvedAbsolute = path.charAt(0) === '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point the path should be resolved to a full absolute path, but
|
||||||
|
// handle relative paths to be safe (might happen when process.cwd() fails)
|
||||||
|
|
||||||
|
// Normalize the path
|
||||||
|
resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) {
|
||||||
|
return !!p;
|
||||||
|
}), !resolvedAbsolute).join('/');
|
||||||
|
|
||||||
|
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.normalize(path)
|
||||||
|
export function normalize(path) {
|
||||||
|
var isAbsolute = path.charAt(0) === '/',
|
||||||
|
trailingSlash = path.substr(-1) === '/';
|
||||||
|
|
||||||
|
// Normalize the path
|
||||||
|
path = normalizeArray(path.split('/').filter(function(p) {
|
||||||
|
return !!p;
|
||||||
|
}), !isAbsolute).join('/');
|
||||||
|
|
||||||
|
if (!path && !isAbsolute) {
|
||||||
|
path = '.';
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (path && trailingSlash) {
|
||||||
|
path += '/';
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return (isAbsolute ? '/' : '') + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function join() {
|
||||||
|
var paths = Array.prototype.slice.call(arguments, 0);
|
||||||
|
return normalize(paths.filter(function(p, index) {
|
||||||
|
return p && typeof p === 'string';
|
||||||
|
}).join('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// path.relative(from, to)
|
||||||
|
export function relative(from, to) {
|
||||||
|
from = resolve(from).substr(1);
|
||||||
|
to = resolve(to).substr(1);
|
||||||
|
|
||||||
|
function trim(arr) {
|
||||||
|
var start = 0;
|
||||||
|
for (; start < arr.length; start++) {
|
||||||
|
if (arr[start] !== '') break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = arr.length - 1;
|
||||||
|
for (; end >= 0; end--) {
|
||||||
|
if (arr[end] !== '') break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start > end) return [];
|
||||||
|
return arr.slice(start, end - start + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fromParts = trim(from.split('/'));
|
||||||
|
var toParts = trim(to.split('/'));
|
||||||
|
|
||||||
|
var length = Math.min(fromParts.length, toParts.length);
|
||||||
|
var samePartsLength = length;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
if (fromParts[i] !== toParts[i]) {
|
||||||
|
samePartsLength = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputParts = [];
|
||||||
|
for (var i = samePartsLength; i < fromParts.length; i++) {
|
||||||
|
outputParts.push('..');
|
||||||
|
}
|
||||||
|
|
||||||
|
outputParts = outputParts.concat(toParts.slice(samePartsLength));
|
||||||
|
|
||||||
|
return outputParts.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dirname(path) {
|
||||||
|
var result = splitPath(path),
|
||||||
|
root = result[0],
|
||||||
|
dir = result[1];
|
||||||
|
|
||||||
|
if (!root && !dir) {
|
||||||
|
// No dirname whatsoever
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir) {
|
||||||
|
// It has a dirname, strip trailing slash
|
||||||
|
dir = dir.substr(0, dir.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return root + dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function basename(path, ext) {
|
||||||
|
var f = splitPath(path)[2];
|
||||||
|
// TODO: make this comparison case-insensitive on windows?
|
||||||
|
if (ext && f.substr(-1 * ext.length) === ext) {
|
||||||
|
f = f.substr(0, f.length - ext.length);
|
||||||
|
}
|
||||||
|
// XXXfiler: node.js just does `return f`
|
||||||
|
return f === "" ? "/" : f;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extname(path) {
|
||||||
|
return splitPath(path)[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAbsolute(path) {
|
||||||
|
if(path.charAt(0) === '/') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNull(path) {
|
||||||
|
if (('' + path).indexOf('\u0000') !== -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we don't double-add a trailing slash (e.g., '/' -> '//')
|
||||||
|
export function addTrailing(path) {
|
||||||
|
return path.replace(/\/*$/, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deal with multiple slashes at the end, one, or none
|
||||||
|
// and make sure we don't return the empty string.
|
||||||
|
export function removeTrailing(path) {
|
||||||
|
path = path.replace(/\/*$/, '');
|
||||||
|
return path === '' ? '/' : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function check(path) {
|
||||||
|
if(!path) {
|
||||||
|
throw new E.EINVAL('path must be a string', path);
|
||||||
|
} else if(isNull(path)) {
|
||||||
|
throw new E.EINVAL('path must be a string without null bytes', path);
|
||||||
|
} else if(!isAbsolute(path)) {
|
||||||
|
throw new E.EINVAL('path must be absolute', path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
class Platform
|
||||||
|
{
|
||||||
|
static supportsWebCrypto()
|
||||||
|
{
|
||||||
|
return ("undefined" !== typeof window &&
|
||||||
|
"undefined" !== typeof window.crypto &&
|
||||||
|
"function" === typeof window.crypto.getRandomValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
static supportsNodeCrypto()
|
||||||
|
{
|
||||||
|
if("undefined" !== typeof process) {
|
||||||
|
try {
|
||||||
|
require.resolve("crypto");
|
||||||
|
return true;
|
||||||
|
} catch(e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Platform;
|
|
@ -0,0 +1,117 @@
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
const URL_REGEX = /^((\w+)\+(\w+):)?(\/\/((\w+)?(:(\w+))?@)?([^\/\?:]+)(:(\d+))?)?(\/?([^\/\?#][^\?#]*)?)?(\?([^#]+))?(#(\w*))?/i;
|
||||||
|
|
||||||
|
class URL
|
||||||
|
{
|
||||||
|
constructor(urlString)
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
|
||||||
|
});
|
||||||
|
const self = __.get(this);
|
||||||
|
|
||||||
|
let match = urlString.match(URL_REGEX);
|
||||||
|
|
||||||
|
self.originalURL = match[0];
|
||||||
|
|
||||||
|
if(match[2]) {
|
||||||
|
self.protocol = match[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[3]) {
|
||||||
|
self.subprotocol = match[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[6]) {
|
||||||
|
self.username = match[6];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[8]) {
|
||||||
|
self.password = match[8];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[9]) {
|
||||||
|
self.host = match[9];
|
||||||
|
} else {
|
||||||
|
self.host = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[11]) {
|
||||||
|
self.port = match[11];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[12]) {
|
||||||
|
self.path = match[12];
|
||||||
|
} else {
|
||||||
|
self.path = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[15]) {
|
||||||
|
let queryList = match[15].split("&");
|
||||||
|
let query = {};
|
||||||
|
for(let item of queryList) {
|
||||||
|
let [key, value] = item.split("=");
|
||||||
|
if(!(query.hasOwnProperty(key))) {
|
||||||
|
query[key] = [];
|
||||||
|
}
|
||||||
|
if(value) {
|
||||||
|
query[key].push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.query = query;
|
||||||
|
} else {
|
||||||
|
self.query = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match[17]) {
|
||||||
|
self.fragment = match[17];
|
||||||
|
} else {
|
||||||
|
self.fragment = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get protocol() { return __.get(this).protocol }
|
||||||
|
set protocol(value) { return __.get(this).protocol = value }
|
||||||
|
|
||||||
|
get subprotocol() { return __.get(this).subprotocol }
|
||||||
|
set subprotocol(value) { return __.get(this).subprotocol = value }
|
||||||
|
|
||||||
|
get username() { return __.get(this).username }
|
||||||
|
set username(value) { return __.get(this).username = value }
|
||||||
|
|
||||||
|
get password() { return __.get(this).password }
|
||||||
|
set password(value) { return __.get(this).password = value }
|
||||||
|
|
||||||
|
get host() { return __.get(this).host }
|
||||||
|
set host(value) { return __.get(this).host = value }
|
||||||
|
|
||||||
|
get port() { return __.get(this).port }
|
||||||
|
set port(value) { return __.get(this).port = value }
|
||||||
|
|
||||||
|
get path() { return __.get(this).path }
|
||||||
|
set path(value) { return __.get(this).path = value }
|
||||||
|
|
||||||
|
get query() { return __.get(this).query }
|
||||||
|
set query(value) { return __.get(this).query = value }
|
||||||
|
|
||||||
|
get fragment() { return __.get(this).fragment }
|
||||||
|
set fragment(value) { return __.get(this).fragment = value }
|
||||||
|
|
||||||
|
toJSON()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
protocol: this.protocol,
|
||||||
|
subprotocol: this.subprotocol,
|
||||||
|
username: this.username,
|
||||||
|
password: this.password,
|
||||||
|
host: this.host,
|
||||||
|
port: this.port,
|
||||||
|
path: this.path,
|
||||||
|
query: this.query,
|
||||||
|
fragment: this.fragment,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default URL;
|
|
@ -0,0 +1,63 @@
|
||||||
|
import Crypto from "./crypto";
|
||||||
|
|
||||||
|
const UUID_SHORT_REGEX = /^[0-9a-zA-Z]{22}$/;
|
||||||
|
const BASE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split('');
|
||||||
|
const BASE_MAP = {};
|
||||||
|
|
||||||
|
for (var z = 0; z < BASE.length; z += 1) {
|
||||||
|
var x = BASE[z];
|
||||||
|
|
||||||
|
if (BASE_MAP[x] !== undefined) throw new TypeError(`${x} is ambiguous`)
|
||||||
|
BASE_MAP[x] = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encode(source) {
|
||||||
|
if (source.length === 0) return ''
|
||||||
|
|
||||||
|
var digits = [0]
|
||||||
|
for (var i = 0; i < source.length; ++i) {
|
||||||
|
for (var j = 0, carry = source[i]; j < digits.length; ++j) {
|
||||||
|
carry += digits[j] << 8
|
||||||
|
digits[j] = carry % BASE.length
|
||||||
|
carry = (carry / BASE.length) | 0
|
||||||
|
}
|
||||||
|
|
||||||
|
while (carry > 0) {
|
||||||
|
digits.push(carry % BASE.length)
|
||||||
|
carry = (carry / BASE.length) | 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var string = "";
|
||||||
|
|
||||||
|
for (var k = 0; source[k] === 0 && k < source.length - 1; ++k)
|
||||||
|
string += BASE[0];
|
||||||
|
|
||||||
|
for (var q = digits.length - 1; q >= 0; --q)
|
||||||
|
string += BASE[digits[q]];
|
||||||
|
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
|
||||||
|
class UUID {
|
||||||
|
static v4()
|
||||||
|
{
|
||||||
|
let buffer = new Uint8Array(16);
|
||||||
|
Crypto.randomBytes(buffer);
|
||||||
|
|
||||||
|
buffer[6] &= 0b00001111;
|
||||||
|
buffer[6] |= 0b01000000;
|
||||||
|
|
||||||
|
buffer[8] &= 0b00111111;
|
||||||
|
buffer[8] |= 0b10000000;
|
||||||
|
|
||||||
|
return encode(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static short()
|
||||||
|
{
|
||||||
|
return this.v4();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UUID;
|
|
@ -0,0 +1,29 @@
|
||||||
|
const { EBADF } = require("../common/errors");
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
/* An open file. */
|
||||||
|
class File
|
||||||
|
{
|
||||||
|
constructor(path, id, flags, position)
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
path: path,
|
||||||
|
id: id,
|
||||||
|
flags: flags,
|
||||||
|
position: position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
read()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
write()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default File;
|
|
@ -0,0 +1,40 @@
|
||||||
|
import UUID from "../common/uuid";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class FS
|
||||||
|
{
|
||||||
|
constructor(superNode, options)
|
||||||
|
{
|
||||||
|
let { proxy, revoke } = Proxy.revocable(this, {});
|
||||||
|
|
||||||
|
__.set(proxy, {
|
||||||
|
id: UUID.short(), // instance ID
|
||||||
|
revoke: revoke,
|
||||||
|
});
|
||||||
|
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
get id()
|
||||||
|
{
|
||||||
|
return __.get(this).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async mount(dev, flags=[], options={})
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async umount()
|
||||||
|
{
|
||||||
|
__.get(this).revoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
toString()
|
||||||
|
{
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FS;
|
|
@ -0,0 +1,3 @@
|
||||||
|
import FS from "./fs";
|
||||||
|
|
||||||
|
export default FS;
|
|
@ -0,0 +1,230 @@
|
||||||
|
import UUID from "../common/uuid";
|
||||||
|
import { MODE_FILE, MODE_DIRECTORY, MODE_SYMBOLIC_LINK } from "../common/constants";
|
||||||
|
import Buffer from "../common/buffer";
|
||||||
|
import E from "../common/errors";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class NodeData
|
||||||
|
{
|
||||||
|
constructor({ mode, size = 0, atime, mtime, ctime, version = UUID.short(), flags, xattrs, nlinks, blksize, nblocks, blkid = UUID.short() })
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
mode: mode, // node type (file, directory, etc)
|
||||||
|
size: size,
|
||||||
|
atime: atime || Date.now(), // access time (will mirror ctime after creation)
|
||||||
|
mtime: mtime || Date.now(), // creation/change time
|
||||||
|
ctime: ctime || Date.now(), // modified time
|
||||||
|
version: version || UUID.short(),
|
||||||
|
flags: flags || [],
|
||||||
|
xattrs: xattrs || {},
|
||||||
|
nlinks: nlinks || 0,
|
||||||
|
blksize: blksize || 4096,
|
||||||
|
nblocks: nblocks || 0,
|
||||||
|
blkid: blkid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get mode() { return __.get(this).mode }
|
||||||
|
|
||||||
|
get atime() { return __.get(this).atime }
|
||||||
|
set atime(value) { return __.get(this).atime = value }
|
||||||
|
|
||||||
|
get mtime() { return __.get(this).mtime }
|
||||||
|
set mtime(value) { return __.get(this).mtime = value }
|
||||||
|
|
||||||
|
get ctime() { return __.get(this).ctime }
|
||||||
|
set ctime(value) { return __.get(this).ctime = value }
|
||||||
|
|
||||||
|
get version() { return __.get(this).version }
|
||||||
|
set version(value) { return __.get(this).version = value }
|
||||||
|
|
||||||
|
get flags() { return __.get(this).flags }
|
||||||
|
|
||||||
|
get xattrs() { return __.get(this).xattrs }
|
||||||
|
|
||||||
|
get nlinks() { return __.get(this).nlinks }
|
||||||
|
set nlinks(value) { return __.get(this).nlinks = value }
|
||||||
|
|
||||||
|
get blksize() { return __.get(this).blksize }
|
||||||
|
|
||||||
|
get nblocks() { return __.get(this).nblocks }
|
||||||
|
set nblocks(value) { return __.get(this).nblocks = value }
|
||||||
|
|
||||||
|
get blkid() { return __.get(this).blkid }
|
||||||
|
set blkid(value) { return __.get(this).blkid = value }
|
||||||
|
|
||||||
|
get size() { return __.get(this).size }
|
||||||
|
set size(value) { return __.get(this).size = value }
|
||||||
|
|
||||||
|
toJSON()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
mode: this.mode,
|
||||||
|
size: this.size,
|
||||||
|
atime: this.atime,
|
||||||
|
mtime: this.mtime,
|
||||||
|
ctime: this.ctime,
|
||||||
|
nlinks: this.nlinks,
|
||||||
|
version: this.version,
|
||||||
|
blksize: this.blksize,
|
||||||
|
nblocks: this.nblocks,
|
||||||
|
blkid: this.blkid,
|
||||||
|
flags: this.flags,
|
||||||
|
xattrs: this.xattrs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
constructor({ fs, id = UUID.short(), data } = {})
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
fs: fs,
|
||||||
|
id: id,
|
||||||
|
|
||||||
|
data: new NodeData(data),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get fs() { return __.get(this).fs }
|
||||||
|
|
||||||
|
get id() { return __.get(this).id }
|
||||||
|
|
||||||
|
get size() { return __.get(this).data.size }
|
||||||
|
set size(value) { return __.get(this).data.size = value }
|
||||||
|
|
||||||
|
get nlinks() { return __.get(this).data.nlinks }
|
||||||
|
set nlinks(value) { return __.get(this).data.nlinks = value }
|
||||||
|
|
||||||
|
get version() { return __.get(this).data.version }
|
||||||
|
set version(value) { return __.get(this).data.version = value }
|
||||||
|
|
||||||
|
get blksize() { return __.get(this).data.blksize }
|
||||||
|
|
||||||
|
get nblocks() { return __.get(this).data.nblocks }
|
||||||
|
set nblocks(value) { return __.get(this).data.nblocks = value }
|
||||||
|
|
||||||
|
get atime() { return __.get(this).data.atime }
|
||||||
|
set atime(value) { return __.get(this).data.atime = value }
|
||||||
|
|
||||||
|
get mtime() { return __.get(this).data.mtime }
|
||||||
|
set mtime(value) { return __.get(this).data.mtime = value }
|
||||||
|
|
||||||
|
get ctime() { return __.get(this).data.ctime }
|
||||||
|
set ctime(value) { return __.get(this).data.ctime = value }
|
||||||
|
|
||||||
|
get mode() { return __.get(this).data.mode }
|
||||||
|
|
||||||
|
get blkid() { return __.get(this).data.blkid }
|
||||||
|
set blkid(value) { return __.get(this).data.blkid = value }
|
||||||
|
|
||||||
|
get flags() { return __.get(this).data.flags }
|
||||||
|
|
||||||
|
get xattrs() { return __.get(this).data.xattrs }
|
||||||
|
|
||||||
|
get data() { return __.get(this).data.toJSON() }
|
||||||
|
|
||||||
|
isFile()
|
||||||
|
{
|
||||||
|
return MODE_FILE == this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirectory()
|
||||||
|
{
|
||||||
|
return MODE_DIRECTORY == this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSymbolicLink()
|
||||||
|
{
|
||||||
|
return MODE_SYMBOLIC_LINK == this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSocket()
|
||||||
|
{
|
||||||
|
return MODE_SOCKET == this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFIFO()
|
||||||
|
{
|
||||||
|
return MODE_FIFO == this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
isCharacterDevice()
|
||||||
|
{
|
||||||
|
return MODE_CHARACTER_DEVICE == this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBlockDevice()
|
||||||
|
{
|
||||||
|
return MODE_BLOCK_DEVICE == this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString()
|
||||||
|
{
|
||||||
|
return JSON.stringify(this.toJSON());
|
||||||
|
}
|
||||||
|
|
||||||
|
static hash(fs, id)
|
||||||
|
{
|
||||||
|
return `${fs.id}${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash()
|
||||||
|
{
|
||||||
|
return Node.hash(this.fs, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async read(fs, id)
|
||||||
|
{
|
||||||
|
let data = await fs.readNode(id);
|
||||||
|
return new Node({ fs: fs, id: id, data: data });
|
||||||
|
}
|
||||||
|
|
||||||
|
async read()
|
||||||
|
{
|
||||||
|
let data = await this.fs.readNode(this.id);
|
||||||
|
__.get(this).data = new NodeData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async write()
|
||||||
|
{
|
||||||
|
this.version = UUID.short();
|
||||||
|
return await this.fs.writeNode(this.id, this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readData(block=0)
|
||||||
|
{
|
||||||
|
let data = await this.fs.readData(this.blkid, block);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeData(block=0, data)
|
||||||
|
{
|
||||||
|
this.nblocks = block + 1;
|
||||||
|
await this.fs.writeData(this.blkid, block, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
fs: this.fs.id,
|
||||||
|
id: this.id,
|
||||||
|
data: __.get(this).data.toJSON(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toString()
|
||||||
|
{
|
||||||
|
return JSON.stringify(this.toJSON());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Node;
|
|
@ -0,0 +1,7 @@
|
||||||
|
import RootFS from "./root-fs";
|
||||||
|
import MemFS from "./mem-fs";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
[RootFS.type]: RootFS,
|
||||||
|
[MemFS.type]: MemFS,
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
import FS from "../index";
|
||||||
|
import SuperNode from "../super-node";
|
||||||
|
import Node from "../node";
|
||||||
|
import UUID from "../../common/uuid";
|
||||||
|
import E from "../../common/errors";
|
||||||
|
import { MODE_FILE, MODE_DIRECTORY, DATA_BLOCK_SEPARATOR } from "../../common/constants";
|
||||||
|
import Buffer from "../../common/buffer";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class IDBFS extends FS
|
||||||
|
{
|
||||||
|
constructor(superNode, options={})
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static get type()
|
||||||
|
{
|
||||||
|
return "idbfs";
|
||||||
|
}
|
||||||
|
|
||||||
|
static async mount(dev=UUID.short(), flags=[], options={})
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async umount()
|
||||||
|
{
|
||||||
|
super.umount();
|
||||||
|
}
|
||||||
|
|
||||||
|
async readNode(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeNode(id, node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async readData(id, block=0, mode=MODE_FILE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeData(id, block, data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async fsync()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IDBFS;
|
|
@ -0,0 +1,92 @@
|
||||||
|
import FS from "../index";
|
||||||
|
import SuperNode from "../super-node";
|
||||||
|
import Node from "../node";
|
||||||
|
import UUID from "../../common/uuid";
|
||||||
|
import E from "../../common/errors";
|
||||||
|
import { MODE_FILE, MODE_DIRECTORY, DATA_BLOCK_SEPARATOR } from "../../common/constants";
|
||||||
|
import Buffer from "../../common/buffer";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class MemFS extends FS
|
||||||
|
{
|
||||||
|
constructor(options={})
|
||||||
|
{
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
let storage = new Map();
|
||||||
|
|
||||||
|
let superNode = new SuperNode({ fs: this });
|
||||||
|
storage.set(superNode.id, superNode);
|
||||||
|
|
||||||
|
let rootNode = new Node({ fs: this, data: { mode: MODE_DIRECTORY } });
|
||||||
|
storage.set(rootNode.id, rootNode);
|
||||||
|
|
||||||
|
superNode.rnode = rootNode.id;
|
||||||
|
|
||||||
|
__.set(this, {
|
||||||
|
storage: storage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get type()
|
||||||
|
{
|
||||||
|
return "memfs";
|
||||||
|
}
|
||||||
|
|
||||||
|
static async mount(dev=UUID.short(), flags=[], options={})
|
||||||
|
{
|
||||||
|
let fs = new MemFS();
|
||||||
|
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
async umount()
|
||||||
|
{
|
||||||
|
super.umount();
|
||||||
|
}
|
||||||
|
|
||||||
|
async readNode(id)
|
||||||
|
{
|
||||||
|
let node = __.get(this).storage.get(id);
|
||||||
|
|
||||||
|
if(!node) {
|
||||||
|
throw new E.ENOENT();
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeNode(id, node)
|
||||||
|
{
|
||||||
|
__.get(this).storage.set(id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readData(id, block=0)
|
||||||
|
{
|
||||||
|
let data = __.get(this).storage.get(`${id}${DATA_BLOCK_SEPARATOR}${block}`);
|
||||||
|
|
||||||
|
if(!data) {
|
||||||
|
throw new E.EIO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeData(id, block, data)
|
||||||
|
{
|
||||||
|
__.get(this).storage.set(`${id}${DATA_BLOCK_SEPARATOR}${block}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fsync()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MemFS;
|
|
@ -0,0 +1,96 @@
|
||||||
|
import FS from "../fs";
|
||||||
|
import SuperNode from "../super-node";
|
||||||
|
import Node from "../node";
|
||||||
|
import UUID from "../../common/uuid";
|
||||||
|
import { MODE_DIRECTORY, MODE_FILE, DATA_BLOCK_SEPARATOR } from "../../common/constants";
|
||||||
|
import { SUPER_NODE_ID } from "../../common/constants";
|
||||||
|
import E from "../../common/errors";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
/*
|
||||||
|
RootFS is a read-only file system containing exactly one
|
||||||
|
directory node. It is created automatically by the VFS
|
||||||
|
layer. Its only purpose is to allow the VFS to mount another
|
||||||
|
file system on top of its only node.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class RootFS extends FS
|
||||||
|
{
|
||||||
|
constructor(options={})
|
||||||
|
{
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
let superNode = new SuperNode({ fs: this, data: { dev: UUID.short() } });
|
||||||
|
|
||||||
|
let rootNode = new Node({ fs: this, data: { mode: MODE_DIRECTORY, nlinks: 1 } });
|
||||||
|
superNode.rnode = rootNode.id;
|
||||||
|
|
||||||
|
let storage = new Map();
|
||||||
|
storage.set(superNode.id, superNode.data);
|
||||||
|
storage.set(rootNode.id, rootNode.data);
|
||||||
|
|
||||||
|
__.set(this, {
|
||||||
|
storage: storage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get type()
|
||||||
|
{
|
||||||
|
return "rootfs";
|
||||||
|
}
|
||||||
|
|
||||||
|
static async mount()
|
||||||
|
{
|
||||||
|
throw new E.UNKNOWN("mount operation not available for rootfs");
|
||||||
|
}
|
||||||
|
|
||||||
|
async umount()
|
||||||
|
{
|
||||||
|
throw new E.UNKNOWN("umount operation not available for rootfs");
|
||||||
|
}
|
||||||
|
|
||||||
|
async readNode(id)
|
||||||
|
{
|
||||||
|
let node = __.get(this).storage.get(id);
|
||||||
|
|
||||||
|
if(!node) {
|
||||||
|
throw new E.ENOENT();
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeNode(id, node)
|
||||||
|
{
|
||||||
|
throw new E.EROFS();
|
||||||
|
}
|
||||||
|
|
||||||
|
async readData(id, block=0)
|
||||||
|
{
|
||||||
|
let data = __.get(this).storage.get(`${id}${DATA_BLOCK_SEPARATOR}${block}`);
|
||||||
|
|
||||||
|
if(!data) {
|
||||||
|
throw new E.EIO();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeData(id, block, data)
|
||||||
|
{
|
||||||
|
throw new E.EROFS();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fsync()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RootFS;
|
|
@ -0,0 +1,97 @@
|
||||||
|
const { MODE_FILE, MODE_DIRECTORY, MODE_SYMBOLIC_LINK } = require(`../common/constants`);
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class Stats
|
||||||
|
{
|
||||||
|
constructor(fileNode, deviceName)
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
node: fileNode.id,
|
||||||
|
dev: deviceName,
|
||||||
|
size: fileNode.size,
|
||||||
|
nlinks: fileNode.nlinks,
|
||||||
|
atime: fileNode.atime,
|
||||||
|
mtime: fileNode.mtime,
|
||||||
|
ctime: fileNode.ctime,
|
||||||
|
type: fileNode.mode,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get node()
|
||||||
|
{
|
||||||
|
return __.get(this).node;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dev()
|
||||||
|
{
|
||||||
|
return __.get(this).dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size()
|
||||||
|
{
|
||||||
|
return __.get(this).size;
|
||||||
|
}
|
||||||
|
|
||||||
|
get nlinks()
|
||||||
|
{
|
||||||
|
return __.get(this).nlinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
get atime()
|
||||||
|
{
|
||||||
|
return __.get(this).atime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get mtime()
|
||||||
|
{
|
||||||
|
return __.get(this).mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get ctime()
|
||||||
|
{
|
||||||
|
return __.get(this).ctime;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type()
|
||||||
|
{
|
||||||
|
return __.get(this).type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isFile()
|
||||||
|
{
|
||||||
|
return MODE_FILE == this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isDirectory()
|
||||||
|
{
|
||||||
|
return MODE_DIRECTORY == this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isSymbolicLink()
|
||||||
|
{
|
||||||
|
return MODE_SYMBOLIC_LINK == this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isSocket()
|
||||||
|
{
|
||||||
|
return MODE_SOCKET == this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isFIFO()
|
||||||
|
{
|
||||||
|
return MODE_FIFO == this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCharacterDevice()
|
||||||
|
{
|
||||||
|
return MODE_CHARACTER_DEVICE == this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isBlockDevice()
|
||||||
|
{
|
||||||
|
return MODE_BLOCK_DEVICE == this.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Stats;
|
|
@ -0,0 +1,124 @@
|
||||||
|
import { MODE_META } from "../common/constants";
|
||||||
|
import { SUPER_NODE_ID } from "../common/constants";
|
||||||
|
import UUID from "../common/uuid";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class SuperNodeData
|
||||||
|
{
|
||||||
|
constructor({ dev, atime = Date.now(), mtime = Date.now(), ctime = Date.now(), rnode, version = UUID.short() } = {})
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
dev: dev,
|
||||||
|
mode: MODE_META,
|
||||||
|
atime: atime || Date.now(), // access time (will mirror ctime after creation)
|
||||||
|
mtime: mtime || Date.now(), // creation/change time
|
||||||
|
ctime: ctime || Date.now(), // modified time
|
||||||
|
rnode: rnode, // root node
|
||||||
|
version: version,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get dev() { return __.get(this).dev }
|
||||||
|
|
||||||
|
get mode() { return __.get(this).mode }
|
||||||
|
|
||||||
|
get atime() { return __.get(this).atime }
|
||||||
|
set atime(value) { return __.get(this).atime = value }
|
||||||
|
|
||||||
|
get mtime() { return __.get(this).mtime }
|
||||||
|
set mtime(value) { return __.get(this).mtime = value }
|
||||||
|
|
||||||
|
get ctime() { return __.get(this).ctime }
|
||||||
|
set ctime(value) { return __.get(this).ctime = value }
|
||||||
|
|
||||||
|
get version() { return __.get(this).version }
|
||||||
|
set version(value) { return __.get(this).version = value }
|
||||||
|
|
||||||
|
get rnode() { return __.get(this).rnode }
|
||||||
|
set rnode(value) { return __.get(this).rnode = value }
|
||||||
|
|
||||||
|
toJSON()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
dev: this.dev,
|
||||||
|
mode: this.mode,
|
||||||
|
atime: this.atime,
|
||||||
|
mtime: this.mtime,
|
||||||
|
ctime: this.ctime,
|
||||||
|
rnode: this.rnode,
|
||||||
|
version: this.version,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SuperNode
|
||||||
|
{
|
||||||
|
constructor({ fs, data } = {})
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
fs: fs,
|
||||||
|
id: SUPER_NODE_ID,
|
||||||
|
|
||||||
|
data: new SuperNodeData(data),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() { return __.get(this).id }
|
||||||
|
|
||||||
|
get fs() { return __.get(this).fs }
|
||||||
|
|
||||||
|
get dev() { return __.get(this).data.dev }
|
||||||
|
|
||||||
|
get mode() { return __.get(this).data.mode }
|
||||||
|
|
||||||
|
get atime() { return __.get(this).data.atime }
|
||||||
|
set atime(value) { return __.get(this).data.atime = value }
|
||||||
|
|
||||||
|
get mtime() { return __.get(this).data.mtime }
|
||||||
|
set mtime(value) { return __.get(this).data.mtime = value }
|
||||||
|
|
||||||
|
get ctime() { return __.get(this).data.ctime }
|
||||||
|
set ctime(value) { return __.get(this).data.ctime = value }
|
||||||
|
|
||||||
|
get rnode() { return __.get(this).data.rnode }
|
||||||
|
set rnode(value) { return __.get(this).data.rnode = value }
|
||||||
|
|
||||||
|
get version() { return __.get(this).data.version }
|
||||||
|
set version(value) { return __.get(this).data.version = value }
|
||||||
|
|
||||||
|
get data() { return __.get(this).data.toJSON() }
|
||||||
|
|
||||||
|
static async read(fs)
|
||||||
|
{
|
||||||
|
let data = await fs.readNode(SUPER_NODE_ID);
|
||||||
|
return new SuperNode({ fs: fs, data: data });
|
||||||
|
}
|
||||||
|
|
||||||
|
async read()
|
||||||
|
{
|
||||||
|
let data = await this.fs.readNode(this.id);
|
||||||
|
__.get(this).data = new SuperNodeData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async write()
|
||||||
|
{
|
||||||
|
this.version = UUID.short();
|
||||||
|
await fs.writeNode(this.id, this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
data: __.get(this).data.toJSON(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toString()
|
||||||
|
{
|
||||||
|
return JSON.stringify(this.toJSON());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SuperNode;
|
|
@ -0,0 +1,15 @@
|
||||||
|
import FS from "./fs/index";
|
||||||
|
import VFS from "./vfs/index";
|
||||||
|
import Providers from "./fs/providers/index";
|
||||||
|
import UUID from "./common/uuid";
|
||||||
|
import FilerBuffer from "./common/buffer";
|
||||||
|
import Crypto from "./common/crypto";
|
||||||
|
import URL from "./common/url";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
FS: FS,
|
||||||
|
VFS: VFS,
|
||||||
|
UUID: UUID,
|
||||||
|
Buffer: FilerBuffer,
|
||||||
|
Crypto: Crypto,
|
||||||
|
};
|
|
@ -0,0 +1,12 @@
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class Shell
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Shell;
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { MODE_FILE } from "../common/constants";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class DirectoryEntry
|
||||||
|
{
|
||||||
|
constructor({ id, type=MODE_FILE } = {})
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
id: id,
|
||||||
|
type: type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() { return __.get(this).id }
|
||||||
|
|
||||||
|
get type() { return __.get(this).type }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DirectoryEntry;
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { FIRST_DESCRIPTOR, N_VFS_DESCRIPTORS, STDIN, STDOUT, STDERR } from "../common/constants";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class FDMap
|
||||||
|
{
|
||||||
|
constructor(size=N_VFS_DESCRIPTORS)
|
||||||
|
{
|
||||||
|
const map = new Array(size).fill(0);
|
||||||
|
|
||||||
|
map[STDIN] = 1;
|
||||||
|
map[STDOUT] = 1;
|
||||||
|
map[STDERR] = 1;
|
||||||
|
|
||||||
|
__.set(this, {
|
||||||
|
map: map,
|
||||||
|
next: FIRST_DESCRIPTOR,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
claimUnused()
|
||||||
|
{
|
||||||
|
const map = __.get(this).map;
|
||||||
|
let next = __.get(this).next;
|
||||||
|
|
||||||
|
for(let i = 0; i < map.length; ++ i)
|
||||||
|
{
|
||||||
|
let fd = (next+i) % map.length;
|
||||||
|
if(0 == map[fd]) {
|
||||||
|
this.claim(fd);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`unable to allocate file descriptor`);
|
||||||
|
}
|
||||||
|
|
||||||
|
claim(fd)
|
||||||
|
{
|
||||||
|
__.get(this).map[fd] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
release(fd)
|
||||||
|
{
|
||||||
|
__.get(this).map[fd] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FDMap;
|
|
@ -0,0 +1,3 @@
|
||||||
|
import VFS from "./vfs";
|
||||||
|
|
||||||
|
export default VFS;
|
|
@ -0,0 +1,65 @@
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class LRUEntry
|
||||||
|
{
|
||||||
|
constructor(key, value)
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
key: key,
|
||||||
|
value: value,
|
||||||
|
older: null,
|
||||||
|
newer: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get key()
|
||||||
|
{
|
||||||
|
return __.get(this).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
get value()
|
||||||
|
{
|
||||||
|
return __.get(this).value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LRUMap
|
||||||
|
{
|
||||||
|
constructor(limit)
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
size: 0,
|
||||||
|
limit: limit,
|
||||||
|
oldest: null,
|
||||||
|
keyMap: new Map(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeCache
|
||||||
|
{
|
||||||
|
constructor(limit=1024)
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
nodes: {},
|
||||||
|
lru: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(node, hash)
|
||||||
|
{
|
||||||
|
__.get(this).nodes[hash] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(hash)
|
||||||
|
{
|
||||||
|
delete __.get(this).nodes[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
find(hash)
|
||||||
|
{
|
||||||
|
return __.get(this).nodes[hash] || null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NodeCache;
|
|
@ -0,0 +1,23 @@
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class VFSMountTable()
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
mounts: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
add(vfsmount)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
remove()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VFSMountTable;
|
|
@ -0,0 +1,386 @@
|
||||||
|
import FDMap from "./fdmap";
|
||||||
|
import VFSMount from "./vfsmount";
|
||||||
|
import RootFS from "../fs/providers/root-fs";
|
||||||
|
import { check as pathCheck, normalize, basename, dirname } from "../common/path";
|
||||||
|
import { ROOT_DIRECTORY_NAME } from "../common/constants";
|
||||||
|
import Providers from "../fs/providers/index";
|
||||||
|
import E from "../common/errors";
|
||||||
|
import { SUPER_NODE_ID } from "../common/constants";
|
||||||
|
import { MNT_READ_ONLY } from "../common/constants";
|
||||||
|
import { SYMLOOP_MAX } from "../common/constants";
|
||||||
|
import { MODE_FILE, MODE_DIRECTORY, MODE_SYMBOLIC_LINK } from "../common/constants";
|
||||||
|
import UUID from "../common/uuid";
|
||||||
|
import DirectoryEntry from "./directory-entry";
|
||||||
|
import Node from "../fs/node";
|
||||||
|
import SuperNode from "../fs/super-node";
|
||||||
|
import URL from "../common/url";
|
||||||
|
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class InternalVFS
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
const rootFS = new RootFS();
|
||||||
|
const rootFSVFSMount = new VFSMount({ fs: rootFS, flags: [ MNT_READ_ONLY ] })
|
||||||
|
const fsVFSMounts = new WeakMap();
|
||||||
|
fsVFSMounts.set(rootFS, rootFSVFSMount);
|
||||||
|
|
||||||
|
__.set(this, {
|
||||||
|
fdMap: new FDMap(),
|
||||||
|
|
||||||
|
vfsMountsRoot: rootFSVFSMount,
|
||||||
|
fsVFSMounts: fsVFSMounts,
|
||||||
|
vfsMounts: new Map(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findNode({path, followSymlinks = true} = {}, context)
|
||||||
|
{
|
||||||
|
const self = __.get(this);
|
||||||
|
|
||||||
|
if(!context) {
|
||||||
|
context = { symlinksFollowed: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
path = normalize(path);
|
||||||
|
if(!path) {
|
||||||
|
throw new E.ENOENT("path is an empty string");
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = basename(path);
|
||||||
|
let parentPath = dirname(path);
|
||||||
|
|
||||||
|
let fs;
|
||||||
|
let nodeId;
|
||||||
|
if(ROOT_DIRECTORY_NAME == name) {
|
||||||
|
fs = self.vfsMountsRoot.fs;
|
||||||
|
let superNode = await SuperNode.read(fs);
|
||||||
|
nodeId = superNode.rnode;
|
||||||
|
} else {
|
||||||
|
let parentDirectoryNode = await this.findNode({ path: parentPath }, context);
|
||||||
|
fs = parentDirectoryNode.fs;
|
||||||
|
|
||||||
|
if(parentDirectoryNode.mode !== MODE_DIRECTORY) {
|
||||||
|
throw new E.ENOTDIR("a component of the path prefix is not a directory", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parentDirectoryData;
|
||||||
|
try {
|
||||||
|
parentDirectoryData = await parentDirectoryNode.readData();
|
||||||
|
} catch(error) {
|
||||||
|
parentDirectoryData = new Object();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!parentDirectoryData.hasOwnProperty(name)) {
|
||||||
|
throw new E.ENOENT(null, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let directoryEntry = new DirectoryEntry(parentDirectoryData[name]);
|
||||||
|
nodeId = directoryEntry.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow all vfsMounts on this node.
|
||||||
|
let nodeHash = Node.hash(fs, nodeId);
|
||||||
|
while(self.vfsMounts.has(nodeHash)) {
|
||||||
|
let vfsMount = (self.vfsMounts.get(nodeHash))[0];
|
||||||
|
fs = vfsMount.fs;
|
||||||
|
|
||||||
|
if(vfsMount.rnode) {
|
||||||
|
nodeId = vfsMount.rnode;
|
||||||
|
} else {
|
||||||
|
let superNode = await SuperNode.read(fs);
|
||||||
|
nodeId = superNode.rnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeHash = Node.hash(fs, nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = await Node.read(fs, nodeId);
|
||||||
|
|
||||||
|
if(node.mode == MODE_SYMBOLIC_LINK) {
|
||||||
|
context.symlinksFollowed += 1;
|
||||||
|
|
||||||
|
if(context.symlinksFollowed > SYMLOOP_MAX) {
|
||||||
|
throw new E.ELOOP(null, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let symlinkPath = await node.readData();
|
||||||
|
node = await this.findNode({ path: symlinkPath }, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
async mount(fsURL, mountPath, flags, options)
|
||||||
|
{
|
||||||
|
const self = __.get(this);
|
||||||
|
|
||||||
|
let mountPoint = await this.findNode({ path: mountPath });
|
||||||
|
|
||||||
|
if(!mountPoint) {
|
||||||
|
throw new E.ENOENT("mount target does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = new URL(fsURL);
|
||||||
|
|
||||||
|
if("filer" !== url.protocol) {
|
||||||
|
throw new E.UNKNOWN("expecting filer protocol");
|
||||||
|
}
|
||||||
|
|
||||||
|
let dev = url.path.slice(1);
|
||||||
|
let type = url.subprotocol;
|
||||||
|
|
||||||
|
if(!(type in Providers)) {
|
||||||
|
throw new E.UNKNOWN("unknown file system type");
|
||||||
|
}
|
||||||
|
|
||||||
|
let fs = await Providers[type].mount(dev, flags, options);
|
||||||
|
let superNode = await fs.readNode(SUPER_NODE_ID);
|
||||||
|
let rootNode = await fs.readNode(superNode.rnode);
|
||||||
|
|
||||||
|
let vfsMount = new VFSMount({ parent: self.fsVFSMounts.get(mountPoint.fs), flags: flags, fs: fs });
|
||||||
|
self.fsVFSMounts.set(fs, vfsMount);
|
||||||
|
|
||||||
|
if(!self.vfsMounts.has(mountPoint.hash())) {
|
||||||
|
self.vfsMounts.set(mountPoint.hash(), new Array());
|
||||||
|
}
|
||||||
|
self.vfsMounts.get(mountPoint.hash()).unshift(vfsMount);
|
||||||
|
}
|
||||||
|
|
||||||
|
async umount(path)
|
||||||
|
{
|
||||||
|
const self = __.get(this);
|
||||||
|
|
||||||
|
let mountNode = await this.findNode({ path: path });
|
||||||
|
let fs = mountNode.fs;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open(path, flags, mode, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mknod(path, mode, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async mkdir(path, mode)
|
||||||
|
{
|
||||||
|
path = normalize(path);
|
||||||
|
|
||||||
|
let name = basename(path);
|
||||||
|
let parentPath = dirname(path);
|
||||||
|
|
||||||
|
let directoryNode;
|
||||||
|
try {
|
||||||
|
directoryNode = await this.findNode({ path: path });
|
||||||
|
} catch(error) {
|
||||||
|
directoryNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(directoryNode) {
|
||||||
|
console.log(directoryNode.toJSON());
|
||||||
|
throw new E.EEXIST(null, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parentDirectoryNode = await this.findNode({ path: parentPath });
|
||||||
|
let fs = parentDirectoryNode.fs;
|
||||||
|
|
||||||
|
let parentDirectoryData
|
||||||
|
try {
|
||||||
|
parentDirectoryData = await parentDirectoryNode.readData();
|
||||||
|
} catch(error) {
|
||||||
|
parentDirectoryData = new Object();
|
||||||
|
}
|
||||||
|
|
||||||
|
directoryNode = new Node({ fs: fs, data: { mode: MODE_DIRECTORY, nlinks: 1, data: UUID.short() } });
|
||||||
|
directoryNode.write();
|
||||||
|
|
||||||
|
let directoryData = new Object();
|
||||||
|
await directoryNode.writeData(0, directoryData);
|
||||||
|
|
||||||
|
// ! update node a/c/m times
|
||||||
|
|
||||||
|
parentDirectoryData[name] = new DirectoryEntry({ id: directoryNode.id, type: MODE_DIRECTORY });
|
||||||
|
await parentDirectoryNode.writeData(0, parentDirectoryData);
|
||||||
|
|
||||||
|
parentDirectoryNode.size = Object.keys(parentDirectoryData).length;
|
||||||
|
await parentDirectoryNode.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
async readdir(path)
|
||||||
|
{
|
||||||
|
pathCheck(path);
|
||||||
|
|
||||||
|
let directoryNode = await this.findNode({ path: path });
|
||||||
|
let directoryData;
|
||||||
|
try {
|
||||||
|
directoryData = await directoryNode.readData();
|
||||||
|
} catch(error) {
|
||||||
|
if(error instanceof E.EIO)
|
||||||
|
directoryData = new Object();
|
||||||
|
}
|
||||||
|
|
||||||
|
let files = Object.keys(directoryData);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmdir(path, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
stat(path, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fstat(fd, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
link(oldpath, newpath, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink(path, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
read(fd, buffer, offset, length, position, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile(path, options, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
write(fd, buffer, offset, length, position, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFile(path, data, options, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
appendFile(path, data, options, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exists(path, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getxattr(path, name, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fgetxattr(fd, name, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setxattr(path, name, value, flag, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fsetxattr(fd, name, value, flag, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
removexattr(path, name, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fremovexattr(fd, name, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lseek(fd, offset, whence, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
utimes(path, atime, mtime, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
futimes(fd, atime, mtime, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
rename(oldpath, newpath, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
symlink(srcpath, dstpath, type, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
readlink(path, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lstat(path, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
truncate(path, length, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ftruncate(fd, length, callback)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VFS
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
vfs: new InternalVFS(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async mount(...args) { return await __.get(this).vfs.mount(...args); }
|
||||||
|
|
||||||
|
async umount(...args) { return await __.get(this).vfs.umount(...args); }
|
||||||
|
|
||||||
|
async mkdir(...args) { return await __.get(this).vfs.mkdir(...args); }
|
||||||
|
|
||||||
|
async readdir(...args) { return await __.get(this).vfs.readdir(...args); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VFS;
|
|
@ -0,0 +1,53 @@
|
||||||
|
const __ = new WeakMap();
|
||||||
|
|
||||||
|
class VFSMount
|
||||||
|
{
|
||||||
|
constructor({ parentVFSMount = null, flags = [], fs, rnode = null } = {})
|
||||||
|
{
|
||||||
|
__.set(this, {
|
||||||
|
flags: flags,
|
||||||
|
fs: fs,
|
||||||
|
rnode: rnode,
|
||||||
|
|
||||||
|
parent: parentVFSMount,
|
||||||
|
children: new Set(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if(parentVFSMount) {
|
||||||
|
parentVFSMount.insertChild(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get fs() { return __.get(this).fs }
|
||||||
|
|
||||||
|
get rnode() { return __.get(this).rnode }
|
||||||
|
|
||||||
|
get flags() { return __.get(this).flags }
|
||||||
|
|
||||||
|
get parent() { return __.get(this).parent }
|
||||||
|
|
||||||
|
get children() { return __.get(this).children }
|
||||||
|
|
||||||
|
hasChildren()
|
||||||
|
{
|
||||||
|
const self = __.get(this);
|
||||||
|
|
||||||
|
return this.children.size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertChild(vfsMount)
|
||||||
|
{
|
||||||
|
const self = __.get(this);
|
||||||
|
|
||||||
|
self.children.add(vfsMount);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeChild(vfsMount)
|
||||||
|
{
|
||||||
|
const self = __.get(this);
|
||||||
|
|
||||||
|
self.children.delete(vfsMount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VFSMount;
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
</head>
|
||||||
|
<script>
|
||||||
|
const log = (text) => {
|
||||||
|
let pNode = document.createElement("p");
|
||||||
|
let textNode = document.createTextNode(text);
|
||||||
|
pNode.appendChild(textNode);
|
||||||
|
document.getElementById("log-container").appendChild(pNode);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="../dist/filer.js"></script>
|
||||||
|
<script>
|
||||||
|
let vfs = new Filer.VFS();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
let rootDirContents = await vfs.readdir("/");
|
||||||
|
log(`root directory contents: ${JSON.stringify(rootDirContents)}`);
|
||||||
|
|
||||||
|
await vfs.mount(`filer+memfs:///${Filer.UUID.v4()}`, "/");
|
||||||
|
rootDirContents = await vfs.readdir("/");
|
||||||
|
log(`root directory contents: ${JSON.stringify(rootDirContents)}`);
|
||||||
|
|
||||||
|
await vfs.mkdir("/test1");
|
||||||
|
rootDirContents = await vfs.readdir("/");
|
||||||
|
log(`root directory contents: ${JSON.stringify(rootDirContents)}`);
|
||||||
|
|
||||||
|
await vfs.mkdir("/test2");
|
||||||
|
rootDirContents = await vfs.readdir("/");
|
||||||
|
log(`root directory contents: ${JSON.stringify(rootDirContents)}`);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
<div id="log-container">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,19 @@
|
||||||
|
class X
|
||||||
|
{
|
||||||
|
constructor(a, b)
|
||||||
|
{
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
valueOf()
|
||||||
|
{
|
||||||
|
return `${a}${b}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj1 = new X(1, 2);
|
||||||
|
let obj2 = new X(1, 2);
|
||||||
|
let obj3 = new X(2, 3);
|
||||||
|
|
||||||
|
console.log(obj1 === obj2);
|
|
@ -0,0 +1,22 @@
|
||||||
|
const Filer = require("../dist/filer.js");
|
||||||
|
const should = require("should");
|
||||||
|
|
||||||
|
describe("Filer", () => {
|
||||||
|
it("should be an object", async () => {
|
||||||
|
(Filer).should.be.an.Object();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Filer.VFS", () => {
|
||||||
|
it("should be a constructor", async () => {
|
||||||
|
(Filer.VFS).should.be.a.Function();
|
||||||
|
let vfs = new Filer.VFS();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a basic root file system on construction", async () => {
|
||||||
|
let vfs = new Filer.VFS();
|
||||||
|
let rootDirectoryEntries = await vfs.readdir("/");
|
||||||
|
|
||||||
|
(rootDirectoryEntries).should.deepEqual([]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
const log = console.log;
|
||||||
|
const Filer = require("../dist/filer");
|
||||||
|
|
||||||
|
let vfs = new Filer.VFS();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
|
||||||
|
await vfs.mount(`filer+memfs:///${Filer.UUID.v4()}`, "/");
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
|
||||||
|
await vfs.mkdir("/test1");
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
|
||||||
|
await vfs.mkdir("/test2");
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
|
||||||
|
await vfs.mount(`filer+memfs:///${Filer.UUID.v4()}`, "/");
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
|
||||||
|
await vfs.mkdir("/test3");
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
|
||||||
|
await vfs.mkdir("/test4");
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
|
||||||
|
await vfs.umount("/");
|
||||||
|
log(`root directory contents: ${JSON.stringify(await vfs.readdir("/"))}`);
|
||||||
|
})();
|
|
@ -0,0 +1,15 @@
|
||||||
|
const crypto = require("crypto");
|
||||||
|
const uuidparse = require("uuid-parse");
|
||||||
|
|
||||||
|
let buffer = new Uint8Array(16);
|
||||||
|
crypto.randomFillSync(buffer);
|
||||||
|
console.log(buffer);
|
||||||
|
|
||||||
|
buffer[6] &= 0b00001111;
|
||||||
|
buffer[6] |= 0b01000000;
|
||||||
|
|
||||||
|
buffer[8] &= 0b00111111;
|
||||||
|
buffer[8] |= 0b10000000;
|
||||||
|
|
||||||
|
console.log(buffer);
|
||||||
|
console.log(uuidparse.unparse(buffer));
|
Loading…
Reference in New Issue