Compare commits

...

14 Commits

Author SHA1 Message Date
9ed3de718f 2.0.6 2023-11-01 14:17:39 +01:00
14530f393c fix(core): update 2023-11-01 14:17:39 +01:00
15a226d30d 2.0.5 2023-11-01 14:16:59 +01:00
16c5c89662 fix(core): update 2023-11-01 14:16:58 +01:00
851a96c014 2.0.4 2023-07-12 11:27:47 +02:00
4ea42cb9fb fix(core): update 2023-07-12 11:27:46 +02:00
41eed6423d 2.0.3 2022-06-07 18:32:08 +02:00
0e067004a4 fix(core): update 2022-06-07 18:32:08 +02:00
9fe222b500 2.0.2 2022-06-07 16:16:15 +02:00
05e9067a34 fix(core): update 2022-06-07 16:16:14 +02:00
2aff46eb0e 2.0.1 2022-03-31 01:20:20 +02:00
6aa4b86598 fix(core): update 2022-03-31 01:20:20 +02:00
af30268551 2.0.0 2022-03-31 01:20:01 +02:00
e562e8f099 BREAKING CHANGE(core): switch to esm 2022-03-31 01:20:01 +02:00
24 changed files with 12651 additions and 1937 deletions

20
.gitignore vendored
View File

@ -1,4 +1,20 @@
node_modules/
.nogit/
# artifacts
coverage/
public/
pages/
coverage/
# installs
node_modules/
# caches
.yarn/
.cache/
.rpt2_cache
# builds
dist/
dist_*/
# custom

View File

@ -1,16 +1,19 @@
# gitzone standard
image: hosttoday/ht-docker-node:npmci
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: "$CI_BUILD_STAGE"
key: '$CI_BUILD_STAGE'
stages:
- security
- test
- release
- metadata
- security
- test
- release
- metadata
before_script:
- npm install -g @shipzone/npmci
# ====================
# security stage
@ -19,63 +22,42 @@ mirror:
stage: security
script:
- npmci git mirror
only:
- tags
tags:
- lossless
- docker
- notpriv
snyk:
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install -g snyk
- npmci command npm install --ignore-scripts
- npmci command snyk test
- npmci command npm install --production --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=prod --production
tags:
- docker
- notpriv
sast:
stage: security
image: registry.gitlab.com/hosttoday/ht-docker-dbase:npmci
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci npm install
- npmci command npm run build
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}"
--volume "$PWD:/code"
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
artifacts:
reports:
sast: gl-sast-report.json
- npmci command npm install --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=dev
tags:
- docker
- priv
allow_failure: true
# ====================
# test stage
# ====================
testLTS:
stage: test
script:
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
testSTABLE:
testStable:
stage: test
script:
- npmci npm prepare
@ -85,7 +67,17 @@ testSTABLE:
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release:
stage: release
@ -95,6 +87,7 @@ release:
only:
- tags
tags:
- lossless
- docker
- notpriv
@ -103,20 +96,15 @@ release:
# ====================
codequality:
stage: metadata
image: docker:stable
allow_failure: true
services:
- docker:stable-dind
only:
- tags
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths: [codeclimate.json]
- npmci command npm install -g typescript
- npmci npm prepare
- npmci npm install
tags:
- lossless
- docker
- priv
@ -127,18 +115,20 @@ trigger:
only:
- tags
tags:
- lossless
- docker
- notpriv
pages:
image: hosttoday/ht-docker-node:npmci
stage: metadata
script:
- npmci command npm install -g typedoc typescript
- npmci node install lts
- npmci command npm install -g @git.zone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/
- npmci command tsdoc
tags:
- lossless
- docker
- notpriv
only:

11
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "npm test",
"name": "Run npm test",
"request": "launch",
"type": "node-terminal"
}
]
}

26
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"json.schemas": [
{
"fileMatch": ["/npmextra.json"],
"schema": {
"type": "object",
"properties": {
"npmci": {
"type": "object",
"description": "settings for npmci"
},
"gitzone": {
"type": "object",
"description": "settings for gitzone",
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}
}
}
}
]
}

View File

@ -1,43 +0,0 @@
# @pushrocks/smartstream
simplifies access to node streams
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartstream)
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartstream)
* [github.com (source mirror)](https://github.com/pushrocks/smartstream)
* [docs (typedoc)](https://pushrocks.gitlab.io/smartstream/)
## Status for master
[![build status](https://gitlab.com/pushrocks/smartstream/badges/master/build.svg)](https://gitlab.com/pushrocks/smartstream/commits/master)
[![coverage report](https://gitlab.com/pushrocks/smartstream/badges/master/coverage.svg)](https://gitlab.com/pushrocks/smartstream/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/@pushrocks/smartstream.svg)](https://www.npmjs.com/package/@pushrocks/smartstream)
[![Known Vulnerabilities](https://snyk.io/test/npm/@pushrocks/smartstream/badge.svg)](https://snyk.io/test/npm/@pushrocks/smartstream)
[![TypeScript](https://img.shields.io/badge/TypeScript->=%203.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
## Usage
Use TypeScript for best in class instellisense.
```typescript
import { Smartstream } from 'smartstream'
import * as gUglify from 'gulp-uglify'
let mySmartstream = new Smartstream([
gulp.src(['./file1.js','./file2.js']),
gUglify(),
gulp.dest('./some/output/path')
])
mySmartstream.onError((err) => { /* handle error */ }) // handles all errors in stream
myStream.onCustomEvent('myeventname', (args...) => { /* Do something */ }) // emit an custom event anywhere in your stream
mySmartstream.run().then(() => {/* do something when stream is finished */})
```
For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html)
[![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://maintainedby.lossless.com)

View File

@ -4,12 +4,13 @@
"npmAccessLevel": "public"
},
"gitzone": {
"projectType": "npm",
"module": {
"githost": "gitlab.com",
"gitscope": "pushrocks",
"gitscope": "push.rocks",
"gitrepo": "smartstream",
"shortDescription": "simplifies access to node streams",
"npmPackagename": "@pushrocks/smartstream",
"description": "simplifies access to node streams",
"npmPackagename": "@push.rocks/smartstream",
"license": "MIT"
}
}

1663
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
{
"name": "@pushrocks/smartstream",
"version": "1.0.14",
"name": "@push.rocks/smartstream",
"version": "2.0.6",
"private": false,
"description": "simplifies access to node streams",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"type": "module",
"scripts": {
"test": "(tstest test/)",
"build": "(tsbuild)"
@ -20,15 +21,33 @@
},
"homepage": "https://gitlab.com/pushrocks/smartstream#README",
"devDependencies": {
"@gitzone/tsbuild": "^2.1.8",
"@gitzone/tstest": "^1.0.18",
"@pushrocks/tapbundle": "^3.0.7",
"tslint": "^5.12.1",
"tslint-config-prettier": "^1.18.0"
"@git.zone/tsbuild": "^2.1.66",
"@git.zone/tsrun": "^1.2.44",
"@git.zone/tstest": "^1.0.77",
"@push.rocks/smartfile": "^10.0.33",
"@push.rocks/tapbundle": "^5.0.15"
},
"dependencies": {
"@pushrocks/smartpromise": "^2.0.5",
"@types/through2": "^2.0.34",
"through2": "^3.0.0"
}
"@push.rocks/smartpromise": "^4.0.3",
"@push.rocks/smartrx": "^3.0.7",
"@types/from2": "^2.3.4",
"@types/through2": "^2.0.40",
"from2": "^2.3.0",
"through2": "^4.0.2"
},
"browserslist": [
"last 1 chrome versions"
],
"files": [
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"
]
}

5705
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

54
readme.md Normal file
View File

@ -0,0 +1,54 @@
# @pushrocks/smartstream
simplifies access to node streams
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartstream)
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartstream)
* [github.com (source mirror)](https://github.com/pushrocks/smartstream)
* [docs (typedoc)](https://pushrocks.gitlab.io/smartstream/)
## Status for master
Status Category | Status Badge
-- | --
GitLab Pipelines | [![pipeline status](https://gitlab.com/pushrocks/smartstream/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/pushrocks/smartstream/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@pushrocks/smartstream)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/pushrocks/smartstream)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@pushrocks/smartstream)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@pushrocks/smartstream)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@pushrocks/smartstream)](https://lossless.cloud)
Platform support | [![Supports Windows 10](https://badgen.net/badge/supports%20Windows%2010/yes/green?icon=windows)](https://lossless.cloud) [![Supports Mac OS X](https://badgen.net/badge/supports%20Mac%20OS%20X/yes/green?icon=apple)](https://lossless.cloud)
## Usage
Use TypeScript for best in class instellisense.
```typescript
import { Smartstream } from 'smartstream'
import * as gUglify from 'gulp-uglify'
let mySmartstream = new Smartstream([
gulp.src(['./file1.js','./file2.js']),
gUglify(),
gulp.dest('./some/output/path')
])
mySmartstream.onError((err) => { /* handle error */ }) // handles all errors in stream
myStream.onCustomEvent('myeventname', (args...) => { /* Do something */ }) // emit an custom event anywhere in your stream
mySmartstream.run().then(() => {/* do something when stream is finished */})
```
## Contribution
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
[![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)

6210
test/assets/readabletext.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
hi+wow
noice

44
test/test.smartstream.ts Normal file
View File

@ -0,0 +1,44 @@
import { expect, tap } from '@push.rocks/tapbundle';
import { SmartStream } from '../ts/smartstream.classes.smartstream.js'; // Adjust the import to your file structure
import * as smartrx from '@push.rocks/smartrx';
import * as fs from 'fs';
tap.test('should create a SmartStream from a Buffer', async () => {
const bufferData = Buffer.from('This is a test buffer');
const smartStream = SmartStream.fromBuffer(bufferData);
let receivedData = Buffer.alloc(0);
return new Promise<void>((resolve) => {
smartStream.on('data', (chunk: Buffer) => {
receivedData = Buffer.concat([receivedData, chunk]);
});
smartStream.on('end', () => {
expect(receivedData.toString()).toEqual(bufferData.toString());
resolve();
});
});
});
tap.test('should create a SmartStream from an Observable', async () => {
const observableData = 'Observable test data';
const testObservable = smartrx.rxjs.of(Buffer.from(observableData));
const smartStream = SmartStream.fromObservable(testObservable);
let receivedData = Buffer.alloc(0);
return new Promise<void>((resolve) => {
smartStream.on('data', (chunk: Buffer) => {
receivedData = Buffer.concat([receivedData, chunk]);
});
smartStream.on('end', () => {
expect(receivedData.toString()).toEqual(observableData);
resolve();
});
});
});
tap.start();

View File

@ -0,0 +1,68 @@
import { expect, tap } from '@push.rocks/tapbundle';
import * as smartfile from '@push.rocks/smartfile';
import * as smartstream from '../ts/index.js';
let testIntake: smartstream.StreamIntake<string>;
tap.test('should handle a read stream', async (tools) => {
const counter = 0;
const testSmartstream = new smartstream.StreamWrapper([
smartfile.fsStream.createReadStream('./test/assets/readabletext.txt'),
smartstream.createDuplexStream<Buffer, Buffer>(
async (chunkStringArg: Buffer, streamTools) => {
// do something with the stream here
const result = chunkStringArg.toString().substr(0, 100);
streamTools.pipeMore('wow =========== \n');
return Buffer.from(result);
},
async (tools) => {
// tools.pipeMore('hey, this is the end')
return Buffer.from('this is the end');
},
{ objectMode: false }
),
smartstream.createDuplexStream<Buffer, string>(async (chunkStringArg) => {
console.log(chunkStringArg.toString());
return null;
}),
smartstream.cleanPipe(),
]);
await testSmartstream.run();
});
tap.test('should create a valid Intake', async (tools) => {
testIntake = new smartstream.StreamIntake<string>();
testIntake
.getReadable()
.pipe(
smartstream.createDuplexStream<string, string>(
async (chunkString) => {
await tools.delayFor(100);
console.log(chunkString);
return chunkString;
},
async () => {
return 'noice';
}
)
)
.pipe(smartfile.fsStream.createWriteStream('./test/assets/writabletext.txt'));
const testFinished = tools.defer();
let counter = 0;
testIntake.pushNextObservable.subscribe(() => {
if (counter < 50) {
counter++;
testIntake.pushData('hi');
testIntake.pushData('+wow');
testIntake.pushData('\n');
} else {
testIntake.signalEnd();
testFinished.resolve();
}
});
await testFinished.promise;
testIntake.signalEnd();
});
tap.start();

View File

@ -1,13 +1,13 @@
import fs = require('fs');
import { expect, tap } from '@pushrocks/tapbundle';
import * as smartfile from '@push.rocks/smartfile';
import { expect, tap } from '@push.rocks/tapbundle';
import * as smartstream from '../ts/index';
import * as smartstream from '../ts/smartstream.classes.streamwrapper.js';
let testSmartstream: smartstream.Smartstream;
let testSmartstream: smartstream.StreamWrapper;
tap.test('should combine a stream', async () => {
testSmartstream = new smartstream.Smartstream([
fs.createReadStream('./test/assets/test.md'),
fs.createWriteStream('./test/assets/testCopy.md')
testSmartstream = new smartstream.StreamWrapper([
smartfile.fsStream.createReadStream('./test/assets/test.md'),
smartfile.fsStream.createWriteStream('./test/assets/testCopy.md'),
]);
await testSmartstream.run();
});

8
ts/00_commitinfo_data.ts Normal file
View File

@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@push.rocks/smartstream',
version: '2.0.6',
description: 'simplifies access to node streams'
}

View File

@ -1,107 +1,4 @@
import * as plugins from './smartstream.plugins';
// interfaces
import { Transform } from 'stream';
export interface IErrorFunction {
(err): any;
}
export interface ICustomEventFunction {
(): any;
}
export interface ICustomEventObject {
eventName: string;
eventFunction: ICustomEventFunction;
}
/**
* class Smartstream handles
*/
export class Smartstream {
private streamArray = [];
private customEventObjectArray: ICustomEventObject[] = [];
private streamStartedDeferred = plugins.smartpromise.defer();
/**
* constructor
*/
constructor(streamArrayArg: any[]) {
this.streamArray = streamArrayArg;
}
/**
* make something with the stream itself
*/
streamStarted(): Promise<any> {
return this.streamStartedDeferred.promise;
}
/**
* attach listener to custom event
*/
onCustomEvent(eventNameArg: string, eventFunctionArg: ICustomEventFunction) {
this.customEventObjectArray.push({
eventName: eventNameArg,
eventFunction: eventFunctionArg
});
}
/**
* run the stream
* @returns Promise
*/
run(): Promise<void> {
const done = plugins.smartpromise.defer<void>();
// clone Array
const streamExecutionArray = [];
for (const streamItem of this.streamArray) {
streamExecutionArray.push(streamItem);
}
// combine the stream
let finalStream = null;
let firstIteration: boolean = true;
for (const stream of streamExecutionArray) {
if (firstIteration === true) {
finalStream = stream;
}
stream.on('error', err => {
done.reject(err);
});
for (const customEventObject of this.customEventObjectArray) {
stream.on(customEventObject.eventName, customEventObject.eventFunction);
}
if (!firstIteration) {
finalStream = finalStream.pipe(stream);
}
firstIteration = false;
}
this.streamStartedDeferred.resolve();
finalStream.on('end', () => {
done.resolve();
});
finalStream.on('close', () => {
done.resolve();
});
finalStream.on('finish', () => {
done.resolve();
});
return done.promise;
}
}
export let cleanPipe = () => {
return plugins.through2.obj(
(file, enc, cb) => {
cb();
},
cb => {
cb();
}
);
};
export * from './smartstream.classes.smartstream.js';
export * from './smartstream.classes.streamwrapper.js';
export * from './smartstream.classes.streamintake.js';
export * from './smartstream.duplex.js';

View File

@ -0,0 +1,55 @@
import * as plugins from './smartstream.plugins.js';
import { Duplex, type DuplexOptions } from 'stream';
export class SmartStream extends Duplex {
private observableSubscription?: plugins.smartrx.rxjs.Subscription;
constructor(options?: DuplexOptions) {
super(options);
}
_read(size: number) {
// Implement if you need custom behavior, otherwise leave it empty
}
_write(chunk: any, encoding: string, callback: (error?: Error | null) => void) {
// Implement if you need custom behavior
callback();
}
static fromBuffer(buffer: Buffer, options?: DuplexOptions): SmartStream {
const smartStream = new SmartStream(options);
process.nextTick(() => {
smartStream.push(buffer);
smartStream.push(null); // Signal the end of the data
});
return smartStream;
}
static fromObservable(observable: plugins.smartrx.rxjs.Observable<any>, options?: DuplexOptions): SmartStream {
const smartStream = new SmartStream(options);
smartStream.observableSubscription = observable.subscribe({
next: (data) => {
if (!smartStream.push(data)) {
// Pause the observable if the stream buffer is full
smartStream.observableSubscription?.unsubscribe();
smartStream.once('drain', () => {
// Resume the observable when the stream buffer is drained
smartStream.observableSubscription?.unsubscribe();
smartStream.observableSubscription = observable.subscribe(data => {
smartStream.push(data);
});
});
}
},
error: (err) => {
smartStream.emit('error', err);
},
complete: () => {
smartStream.push(null); // Signal the end of the data
}
});
return smartStream;
}
}

View File

@ -0,0 +1,66 @@
import * as plugins from './smartstream.plugins.js';
export class StreamIntake<T> {
private signalEndBoolean = false;
private chunkStore: T[] = [];
public pushNextObservable = new plugins.smartrx.ObservableIntake<any>();
private pushedNextDeferred = plugins.smartpromise.defer();
private readableStream = plugins.from2.obj(async (size, next) => {
// console.log('get next');
// execute without backpressure
while (this.chunkStore.length > 0) {
next(null, this.chunkStore.shift());
}
if (this.signalEndBoolean) {
next(null, null);
}
// lets trigger backpressure handling
this.pushNextObservable.push('please push next');
await this.pushedNextDeferred.promise;
this.pushedNextDeferred = plugins.smartpromise.defer();
// execute with backpressure
while (this.chunkStore.length > 0) {
next(null, this.chunkStore.shift());
}
if (this.signalEndBoolean) {
next(null, null);
}
});
constructor() {
this.pushNextObservable.push('please push next');
}
/**
* returns a new style readble stream
*/
public getReadable() {
const readable = new plugins.stream.Readable({
objectMode: true,
});
return readable.wrap(this.readableStream);
}
/**
* returns an oldstyle readble stream
*/
public getReadableStream() {
return this.readableStream;
}
public pushData(chunkData: T) {
this.chunkStore.push(chunkData);
this.pushedNextDeferred.resolve();
}
public signalEnd() {
this.signalEndBoolean = true;
this.pushedNextDeferred.resolve();
this.pushNextObservable.signalComplete();
}
}

View File

@ -0,0 +1,107 @@
import * as plugins from './smartstream.plugins.js';
// interfaces
import { Transform } from 'stream';
export interface IErrorFunction {
(err: Error): any;
}
export interface ICustomEventFunction {
(): any;
}
export interface ICustomEventObject {
eventName: string;
eventFunction: ICustomEventFunction;
}
/**
* class Smartstream handles
*/
export class StreamWrapper {
private streamArray: Array<plugins.stream.Duplex> = [];
private customEventObjectArray: ICustomEventObject[] = [];
private streamStartedDeferred = plugins.smartpromise.defer();
/**
* constructor
*/
constructor(streamArrayArg: any[]) {
this.streamArray = streamArrayArg;
}
/**
* make something with the stream itself
*/
streamStarted(): Promise<any> {
return this.streamStartedDeferred.promise;
}
/**
* attach listener to custom event
*/
onCustomEvent(eventNameArg: string, eventFunctionArg: ICustomEventFunction) {
this.customEventObjectArray.push({
eventName: eventNameArg,
eventFunction: eventFunctionArg,
});
}
/**
* run the stream
* @returns Promise
*/
run(): Promise<void> {
const done = plugins.smartpromise.defer<void>();
// clone Array
const streamExecutionArray: Array<plugins.stream.Duplex> = [];
for (const streamItem of this.streamArray) {
streamExecutionArray.push(streamItem);
}
// combine the stream
let finalStream = null;
let firstIteration: boolean = true;
for (const stream of streamExecutionArray) {
if (firstIteration === true) {
finalStream = stream;
}
stream.on('error', (err) => {
done.reject(err);
});
for (const customEventObject of this.customEventObjectArray) {
stream.on(customEventObject.eventName, customEventObject.eventFunction);
}
if (!firstIteration) {
finalStream = finalStream.pipe(stream);
}
firstIteration = false;
}
this.streamStartedDeferred.resolve();
finalStream.on('end', () => {
done.resolve();
});
finalStream.on('close', () => {
done.resolve();
});
finalStream.on('finish', () => {
done.resolve();
});
return done.promise;
}
}
export let cleanPipe = () => {
return plugins.through2.obj(
(file, enc, cb) => {
cb();
},
(cb) => {
cb();
}
);
};

83
ts/smartstream.duplex.ts Normal file
View File

@ -0,0 +1,83 @@
import * as plugins from './smartstream.plugins.js';
export interface ITruncateFunc {
(): void;
}
export interface IPipeMoreFunc {
(pipeObject: any): void;
}
export interface IStreamTools {
truncate: ITruncateFunc;
pipeMore: IPipeMoreFunc;
}
export interface IStreamFunction<T, rT> {
(chunkArg: T, toolsArg: IStreamTools): Promise<rT>;
}
export interface IStreamEndFunction<rT> {
(toolsArg: IStreamTools): Promise<rT>;
}
export interface IStreamOptions {
objectMode?: boolean;
readableObjectMode?: boolean;
writableObjectMode?: boolean;
}
export let createDuplexStream = <T, rT>(
funcArg: IStreamFunction<T, rT>,
endFuncArg?: IStreamEndFunction<rT>,
optionsArg: IStreamOptions = {
objectMode: false,
readableObjectMode: true,
writableObjectMode: true,
}
) => {
return plugins.through2(
optionsArg,
function (chunk, enc, cb) {
let truncated = false;
const tools: IStreamTools = {
truncate: () => {
truncated = true;
cb(null, null);
},
pipeMore: (pipeObject) => {
this.push(pipeObject);
},
};
const asyncWrapper = async () => {
const resultChunk: rT = await funcArg(chunk, tools);
if (!truncated) {
cb(null, resultChunk);
}
};
asyncWrapper().catch((err) => {
console.log(err);
});
},
function (cb) {
const tools: IStreamTools = {
truncate: () => {
cb();
},
pipeMore: (pushArg) => {
this.push(pushArg);
},
};
const asyncWrapper = async () => {
if (endFuncArg) {
const result = await endFuncArg(tools);
this.push(result);
}
cb();
};
asyncWrapper().catch((err) => {
console.log(err);
});
}
);
};

View File

@ -1,4 +1,16 @@
import * as smartpromise from '@pushrocks/smartpromise';
import * as through2 from 'through2';
// node native
import * as stream from 'stream';
export { smartpromise, through2 };
export { stream };
// pushrocks scope
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrx from '@push.rocks/smartrx';
export { smartpromise, smartrx };
// thirdparty
import from2 from 'from2';
import through2 from 'through2';
export { from2, through2 };

14
tsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true
},
"exclude": [
"dist_*/**/*.d.ts"
]
}

View File

@ -1,17 +0,0 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}