diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..7789a02 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + - package-ecosystem: npm + directory: / + schedule: + interval: daily + versioning-strategy: increase-if-necessary diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..1e2e5f3 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,21 @@ +name: Test +on: + - pull_request + - push +jobs: + test: + runs-on: ubuntu-24.04 + strategy: + matrix: + node: + - '18' + - '20' + - '22' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - run: npm install + - run: npm test + - uses: codecov/codecov-action@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4811400 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +coverage +node_modules +package-lock.json diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d319c08 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Thomas Bergwinkl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f239ee5 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# rdf-utils-stream + +[![build status](https://img.shields.io/github/actions/workflow/status/rdf-ext/rdf-utils-stream/test.yaml?branch=master)](https://github.com/rdf-ext/rdf-utils-stream/actions/workflows/test.yaml) +[![npm version](https://img.shields.io/npm/v/rdf-utils-stream.svg)](https://www.npmjs.com/package/rdf-utils-stream) + +Utils for RDF/JS Streams. diff --git a/eventToPromise.js b/eventToPromise.js new file mode 100644 index 0000000..4b11324 --- /dev/null +++ b/eventToPromise.js @@ -0,0 +1,8 @@ +async function eventToPromise (event) { + return new Promise((resolve, reject) => { + event.on('end', () => resolve()) + event.on('error', err => reject(err)) + }) +} + +export default eventToPromise diff --git a/package.json b/package.json new file mode 100644 index 0000000..5c4b60b --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "rdf-utils-stream", + "version": "0.0.0", + "type": "module", + "description": "RDF/JS Stream utils", + "main": "index.js", + "scripts": { + "test": "stricter-standard && c8 --reporter=lcov --reporter=text-summary mocha" + }, + "repository": { + "type": "git", + "url": "https://github.com/rdf-ext/rdf-utils-stream.git" + }, + "keywords": [ + "rdf", + "rdfjs", + "rdf-ext", + "stream", + "utils" + ], + "author": "Thomas Bergwinkl (https://www.bergnet.org/people/bergi/card#me)", + "license": "MIT", + "bugs": { + "url": "https://github.com/rdf-ext/rdf-utils-stream/issues" + }, + "homepage": "https://github.com/rdf-ext/rdf-utils-stream", + "dependencies": { + "events": "^3.3.0" + }, + "devDependencies": { + "c8": "^10.0.0", + "mocha": "^10.4.0", + "stricter-standard": "^0.3.1" + } +} diff --git a/promiseToEvent.js b/promiseToEvent.js new file mode 100644 index 0000000..e9268ac --- /dev/null +++ b/promiseToEvent.js @@ -0,0 +1,13 @@ +import { EventEmitter } from 'events' + +function promiseToEvent (promise) { + const event = new EventEmitter() + + promise + .then(() => event.emit('end')) + .catch(err => event.emit('error', err)) + + return event +} + +export default promiseToEvent diff --git a/test/eventToPromise.test.js b/test/eventToPromise.test.js new file mode 100644 index 0000000..d00bae3 --- /dev/null +++ b/test/eventToPromise.test.js @@ -0,0 +1,46 @@ +import { doesNotReject, rejects, strictEqual } from 'node:assert' +import { EventEmitter } from 'events' // eslint-disable-line import/order +import { describe, it } from 'mocha' +import eventToPromise from '../eventToPromise.js' + +describe('eventToPromise', () => { + it('should be a function', () => { + strictEqual(typeof eventToPromise, 'function') + }) + + it('should resolve on end event', async () => { + const event = new EventEmitter() + + await doesNotReject(async () => { + setTimeout(() => { + event.emit('end') + }, 10) + + await eventToPromise(event) + }) + }) + + it('should reject on error event', async () => { + const event = new EventEmitter() + + await rejects(async () => { + setTimeout(() => { + event.emit('error') + }, 10) + + await eventToPromise(event) + }) + }) + + it('should forward the error from the error event', async () => { + const event = new EventEmitter() + + await rejects(async () => { + setTimeout(() => { + event.emit('error', new Error('test')) + }, 10) + + await eventToPromise(event) + }, { message: 'test' }) + }) +}) diff --git a/test/promiseToEvent.test.js b/test/promiseToEvent.test.js new file mode 100644 index 0000000..3344438 --- /dev/null +++ b/test/promiseToEvent.test.js @@ -0,0 +1,68 @@ +import { strictEqual } from 'node:assert' +import { setTimeout } from 'node:timers/promises' +import { describe, it } from 'mocha' +import promiseToEvent from '../promiseToEvent.js' + +describe('promiseToEvent', () => { + it('should be a function', () => { + strictEqual(typeof promiseToEvent, 'function') + }) + + it('should return an EventEmitter object', () => { + const result = promiseToEvent(new Promise(() => {})) + + strictEqual(typeof result.emit, 'function') + strictEqual(typeof result.on, 'function') + }) + + it('should emit an end event on resolve', async () => { + let touched + const result = promiseToEvent(setTimeout(10)) + + result.on('end', () => { + touched = true + }) + + await setTimeout(20) + + strictEqual(touched, true) + }) + + it('should emit an error event on reject', async () => { + let touched + + const func = async () => { + await setTimeout(10) + throw new Error('test') + } + + const result = promiseToEvent(func()) + + result.on('error', () => { + touched = true + }) + + await setTimeout(20) + + strictEqual(touched, true) + }) + + it('should forward the emitted error', async () => { + let error + + const func = async () => { + await setTimeout(10) + throw new Error('test') + } + + const result = promiseToEvent(func()) + + result.on('error', err => { + error = err + }) + + await setTimeout(20) + + strictEqual(error.message, 'test') + }) +})