Compare commits

..
12 Commits
Author SHA1 Message Date
Jimmy Wärting 5b2bab5365 Update CHANGELOG.md 2020-07-28 17:48:58 +02:00
Jimmy Wärting b75fb395b4 bumped minor version to 1.2.0 2020-07-28 17:44:52 +02:00
Jimmy Wärting 3217da9bc7 Create blob backed up by filesystem (#55)
* create a blob backed up by filesystem
* added code coverage test for filesystem blobs
* updated readme for using filesystem
* install peer dep
* added from to files array in package.json
2020-07-28 17:42:31 +02:00
Jimmy Wärting ae6b565ae6 fix slice test
`added` bytes was calculated wrong
2020-07-24 01:56:18 +02:00
Irakli Gozalishvili 26ca0e39b4 create test illustrating a bug 2020-07-24 01:56:18 +02:00
Antoni Kepinski 99388a79bb update changelog 2020-06-11 16:31:21 +02:00
Antoni Kepinski cf379d10e0 2.0.1 2020-06-11 16:30:40 +02:00
Pim 1d1faebe18 fix: remove upper bound for node engine semver (#49) 2020-06-11 16:29:53 +02:00
Konstantin Vyatkin 903a4c1bb0 use Symbol.hasInstance (#46) 2020-06-11 10:00:58 -04:00
Konstantin Vyatkin 126b4ec697 Switch to GitHub Actions (#47)
* remove travis

* add codecov partial line coverage

* switch coverage to c8

* add GitHub Actions

* update badge

* import TextDecoder from util

* lint
2020-06-11 10:01:22 +02:00
Antoni Kepinski 38639ec4aa release 2.0.0 2020-06-10 21:09:47 +02:00
Antoni Kepinski 5608cef75c update changelog 2020-06-10 21:08:54 +02:00
11 changed files with 231 additions and 52 deletions
+6
View File
@@ -0,0 +1,6 @@
coverage:
round: up
precision: 2
parsers:
javascript:
enable_partials: yes
+57
View File
@@ -0,0 +1,57 @@
name: CI
on:
push:
branches: [master]
pull_request:
paths:
- "**.js"
- "package.json"
- ".github/workflows/ci.yml"
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
node: ["14", "12", engines]
exclude:
# On Windows, run tests with only the LTS environments.
- os: windows-latest
node: engines
- os: windows-latest
node: "14"
# On macOS, run tests with only the LTS environments.
- os: macOS-latest
node: engines
- os: macOS-latest
node: "14"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Get Node.JS version from package.json
if: matrix.node == 'engines'
id: get-version
run: echo ::set-output name=node::$(npx --q minimum-node-version)
- uses: actions/setup-node@v2-beta
if: matrix.node != 'engines'
with:
node-version: ${{ matrix.node }}
- uses: actions/setup-node@v2-beta
if: matrix.node == 'engines'
with:
node-version: ${{steps.get-version.outputs.node}}
- run: npm install
- run: npm install domexception
- run: npm run report -- --colors
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: ./coverage/coverage-final.json
+20
View File
@@ -0,0 +1,20 @@
name: CI
on:
pull_request:
paths:
- "**.js"
- "**eslint**"
- "package.json"
- ".github/workflows/lint.yml"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2-beta
with:
node-version: 14
- run: npm install
- run: npm run lint
+2 -1
View File
@@ -1 +1,2 @@
package-lock=false
package-lock=false
save-exact=false
-13
View File
@@ -1,13 +0,0 @@
language: node_js
node_js:
- "lts/*"
- "node"
script:
- npm run lint
- npm run coverage
cache:
directories:
- node_modules
+17 -5
View File
@@ -1,12 +1,24 @@
Changelog
=========
## v1.0.7
## v2.1.0
- Fix: .slice has an implementation bug (#54).
- Added blob backed up by filesystem (#55)
- Enhance: create TypeScript declarations from JSDoc (#45)
- Enhance: operate on blob parts (byte sequence) (#44)
- Enhance: use a `WeakMap` for private properties (#42)
- Other: update formatting
## v2.0.1
- Fix: remove upper bound for node engine semver (#49).
## v2.0.0
> Note: This release was previously published as `1.0.7`, but as it contains breaking changes, we renamed it to `2.0.0`.
- **Breaking:** minimum supported Node.js version is now 10.17.
- **Breaking:** `buffer` option has been removed.
- Enhance: create TypeScript declarations from JSDoc (#45).
- Enhance: operate on blob parts (byte sequence) (#44).
- Enhance: use a `WeakMap` for private properties (#42) .
- Other: update formatting.
## v1.0.6
+21 -3
View File
@@ -1,7 +1,7 @@
# fetch-blob
[![npm version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![build status][ci-image]][ci-url]
[![coverage status][codecov-image]][codecov-url]
[![install size][install-size-image]][install-size-url]
@@ -27,12 +27,30 @@ fetch('https://httpbin.org/post', {
.then(json => console.log(json));
```
### Blob part backed up by filesystem
To use, install [domexception](https://github.com/jsdom/domexception).
```sh
npm install fetch-blob domexception
```
```js
const blobFrom = require('fetch-blob/from.js');
const blob1 = blobFrom('./2-GiB-file.bin');
const blob2 = blobFrom('./2-GiB-file.bin');
// Not a 4 GiB memory snapshot, just holds 3 references
// points to where data is located on the disk
const blob = new Blob([blob1, blob2]);
console.log(blob.size) // 4 GiB
```
See the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [tests](https://github.com/node-fetch/fetch-blob/blob/master/test.js) for more details.
[npm-image]: https://flat.badgen.net/npm/v/fetch-blob
[npm-url]: https://www.npmjs.com/package/fetch-blob
[travis-image]: https://flat.badgen.net/travis/node-fetch/fetch-blob
[travis-url]: https://travis-ci.org/node-fetch/fetch-blob
[ci-image]: https://github.com/node-fetch/fetch-blob/workflows/CI/badge.svg
[ci-url]: https://github.com/node-fetch/fetch-blob/actions
[codecov-image]: https://flat.badgen.net/codecov/c/github/node-fetch/fetch-blob/master
[codecov-url]: https://codecov.io/gh/node-fetch/fetch-blob
[install-size-image]: https://flat.badgen.net/packagephobia/install/fetch-blob
+57
View File
@@ -0,0 +1,57 @@
const {statSync, createReadStream} = require('fs');
const Blob = require('.');
const DOMException = require('domexception');
/**
* @param {string} path filepath on the disk
* @returns {Blob}
*/
function blobFrom(path) {
const {size, mtime} = statSync(path);
const blob = new BlobDataItem({path, size, mtime});
return new Blob([blob]);
}
/**
* This is a blob backed up by a file on the disk
* with minium requirement
*
* @private
*/
class BlobDataItem {
constructor(options) {
this.size = options.size;
this.path = options.path;
this.start = options.start;
this.mtime = options.mtime;
}
// Slicing arguments is validated and formated
// by Blob.prototype.slice
slice(start, end) {
return new BlobDataItem({
path: this.path,
start,
mtime: this.mtime,
size: end - start
});
}
stream() {
if (statSync(this.path).mtime > this.mtime) {
throw new DOMException('The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.', 'NotReadableError');
}
return createReadStream(this.path, {
start: this.start,
end: this.start + this.size - 1
});
}
get [Symbol.toStringTag]() {
return 'Blob';
}
}
module.exports = blobFrom;
+17 -24
View File
@@ -15,20 +15,6 @@ async function * read(parts) {
}
}
/**
* @template T
* @param {T} object
* @returns {T is Blob}
*/
const isBlob = object => {
return (
typeof object === 'object' &&
typeof object.stream === 'function' &&
typeof object.constructor === 'function' &&
/^(Blob|File)$/.test(object[Symbol.toStringTag])
);
};
class Blob {
/**
* The Blob() constructor returns a new Blob object. The content
@@ -43,13 +29,13 @@ class Blob {
const parts = blobParts.map(element => {
let buffer;
if (Buffer.isBuffer(element)) {
if (element instanceof Buffer) {
buffer = element;
} else if (ArrayBuffer.isView(element)) {
buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength);
} else if (element instanceof ArrayBuffer) {
buffer = Buffer.from(element);
} else if (isBlob(element)) {
} else if (element instanceof Blob) {
buffer = element;
} else {
buffer = Buffer.from(typeof element === 'string' ? element : String(element));
@@ -152,7 +138,7 @@ class Blob {
} else {
const chunk = part.slice(relativeStart, Math.min(size, relativeEnd));
blobParts.push(chunk);
added += size;
added += ArrayBuffer.isView(chunk) ? chunk.byteLength : chunk.size;
relativeStart = 0; // All next sequental parts should start at 0
// don't add the overflow to new blobParts
@@ -167,6 +153,20 @@ class Blob {
return blob;
}
get [Symbol.toStringTag]() {
return 'Blob';
}
static [Symbol.hasInstance](object) {
return (
typeof object === 'object' &&
typeof object.stream === 'function' &&
object.stream.length === 0 &&
typeof object.constructor === 'function' &&
/^(Blob|File)$/.test(object[Symbol.toStringTag])
);
}
}
Object.defineProperties(Blob.prototype, {
@@ -175,11 +175,4 @@ Object.defineProperties(Blob.prototype, {
slice: {enumerable: true}
});
Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
value: 'Blob',
writable: false,
enumerable: false,
configurable: true
});
module.exports = Blob;
+9 -6
View File
@@ -1,17 +1,18 @@
{
"name": "fetch-blob",
"version": "1.0.7",
"version": "2.1.0",
"description": "A Blob implementation in Node.js, originally from node-fetch.",
"main": "index.js",
"files": [
"from.js",
"index.js",
"index.d.ts"
],
"scripts": {
"lint": "xo",
"test": "xo && ava",
"report": "nyc ava",
"coverage": "nyc --reporter json --reporter text ava && codecov -f coverage/coverage-final.json",
"report": "c8 --reporter json --reporter text ava",
"coverage": "c8 --reporter json --reporter text ava && codecov -f coverage/coverage-final.json",
"prepublishOnly": "tsc --declaration --emitDeclarationOnly --allowJs index.js"
},
"repository": "https://github.com/node-fetch/fetch-blob.git",
@@ -20,7 +21,7 @@
"node-fetch"
],
"engines": {
"node": "^10.17.0"
"node": "^10.17.0 || >=12.3.0"
},
"author": "David Frank",
"license": "MIT",
@@ -30,10 +31,10 @@
"homepage": "https://github.com/node-fetch/fetch-blob#readme",
"devDependencies": {
"ava": "^3.8.2",
"c8": "^7.2.0",
"codecov": "^3.7.0",
"get-stream": "^5.1.0",
"node-fetch": "^2.6.0",
"nyc": "^15.1.0",
"typescript": "^3.9.5",
"xo": "^0.32.0"
},
@@ -48,5 +49,7 @@
}
]
},
"dependencies": {}
"peerDependencies": {
"domexception": "^2.0.1"
}
}
+25
View File
@@ -1,7 +1,10 @@
const fs = require('fs');
const test = require('ava');
const Blob = require('.');
const blobFrom = require('./from');
const getStream = require('get-stream');
const {Response} = require('node-fetch');
const {TextDecoder} = require('util');
test('new Blob()', t => {
const blob = new Blob(); // eslint-disable-line no-unused-vars
@@ -113,6 +116,12 @@ test('Blob slice(0, -1)', async t => {
t.is(await blob.text(), 'abcdefg');
});
test('Blob(["hello ", "world"]).slice(5)', async t => {
const parts = ['hello ', 'world'];
const blob = new Blob(parts);
t.is(await blob.slice(5).text(), ' world');
});
test('throw away unwanted parts', async t => {
const blob = new Blob(['a', 'b', 'c']).slice(1, 2);
t.is(await blob.text(), 'b');
@@ -135,3 +144,19 @@ test('Blob works with node-fetch Response.text()', async t => {
const text = await response.text();
t.is(text, data);
});
test('blob part backed up by filesystem', async t => {
const blob = blobFrom('./LICENSE');
t.is(await blob.slice(0, 3).text(), 'MIT');
t.is(await blob.slice(4, 11).text(), 'License');
});
test('Reading after modified should fail', async t => {
const blob = blobFrom('./LICENSE');
await new Promise(resolve => setTimeout(resolve, 100));
const now = new Date();
// Change modified time
fs.utimesSync('./LICENSE', now, now);
const error = await blob.text().catch(error => error);
t.is(error.name, 'NotReadableError');
});