feat(core): moved bodyParser$ and logger$ into separate packages
MODIFY
package-lock.json
package-lock.json
"js-tokens": "^3.0.0"
}
},
+ "@marblejs/core": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@marblejs/core/-/core-0.2.2.tgz",
+ "integrity": "sha512-SplTT7sfF6uzkZSp7NAQ659mPCVF0wsRo3A8r4OGnD/jll3X0YkaOtRlOzzovtcVeqJx9ZzZpMrR6G0ospoOZQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "path-to-regexp": "^2.2.1"
+ }
+ },
"@types/events": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
"dev": true
},
+ "path-to-regexp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
+ "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==",
+ "dev": true
+ },
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",

MODIFY
package.json
package.json
"description": "Reactive Node APIs made easy",
"scripts": {
"postinstall": "npm run build",
- "build": "lerna bootstrap",
+ "build": "lerna bootstrap --hoist",
"benchmark": "npm run build && make -C benchmarks",
"lint": "tslint --config tslint.json --project tsconfig.spec.json",
"test": "jest --expand",
"rxjs": "~6.1.0"
},
"devDependencies": {
+ "@marblejs/core": "^0.2.2",
"@types/jest": "^22.2.3",
"@types/supertest": "^2.0.4",
"husky": "^0.14.0",

MODIFY
packages/core/package.json
packages/core/package.json
"rxjs": "~6.1.0"
},
"dependencies": {
- "chalk": "^2.4.1",
"path-to-regexp": "^2.2.1"
},
"devDependencies": {
- "@types/chalk": "^2.2.0",
"@types/path-to-regexp": "^1.7.0"
},
"publishConfig": {

MODIFY
packages/core/src/index.ts
packages/core/src/index.ts
export * from './effects/effects.interface';
export { combineRoutes } from './effects/effects.combiner';
-export * from './middlewares/bodyParser.middleware';
export * from './middlewares/error.middleware';
-export * from './middlewares/logger.middleware';
export * from './operators';
export * from './http.interface';
export * from './http.listener';

DELETE
packages/core/src/middlewares/bodyParser.middleware.ts
-import { Observable, of, throwError } from 'rxjs';
-import { catchError, map, switchMap, tap, toArray } from 'rxjs/operators';
-import { Effect } from '../effects/effects.interface';
-import { HttpRequest, HttpStatus } from '../http.interface';
-import { ContentType } from '../util/contentType.util';
-import { HttpError } from '../util/error.util';
-
-const fromReadableStream = (stream: HttpRequest): Observable<any> => {
- stream.pause();
- return new Observable(observer => {
- const next = chunk => observer.next(chunk);
- const complete = () => observer.complete();
- const error = err => observer.error(err);
-
- stream
- .on('data', next)
- .on('error', error)
- .on('end', complete)
- .resume();
-
- return () => {
- stream.removeListener('data', next);
- stream.removeListener('error', error);
- stream.removeListener('end', complete);
- };
- });
-};
-
-const getBody = (req: HttpRequest) =>
- fromReadableStream(req)
- .pipe(
- toArray(),
- map(chunks => Buffer.concat(chunks)),
- map(buffer => buffer.toString()),
- map(body => {
- switch (req.headers['content-type']) {
- case ContentType.APPLICATION_JSON:
- return JSON.parse(body);
- default:
- return body;
- }
- }),
- );
-
-export const bodyParser$: Effect<HttpRequest> = (request$, response) => request$
- .pipe(
- switchMap(req =>
- req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH'
- ? of(req).pipe(
- switchMap(getBody),
- tap(body => req.body = body),
- map(() => req),
- catchError(error => throwError(new HttpError('Request body parse error', HttpStatus.BAD_REQUEST)))
- )
- : of(req)
- )
- );

ADD
packages/middleware-body/package.json
+{
+ "name": "@marblejs/middleware-body",
+ "version": "0.2.2",
+ "description": "Body parser middleware for Marble.js",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "scripts": {
+ "start": "npm run watch",
+ "watch": "tsc -w",
+ "build": "tsc",
+ "prepack": "npm run build",
+ "prepare": "npm run build"
+ },
+ "files": [
+ "dist/"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marblejs/marble.git"
+ },
+ "engines": {
+ "node": ">= 8.0.0",
+ "npm": ">= 5.0.0"
+ },
+ "author": "marblejs",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marblejs/marble/issues"
+ },
+ "homepage": "https://github.com/marblejs/marble#readme",
+ "peerDependencies": {
+ "@marblejs/core": "^0.2.2",
+ "rxjs": "~6.1.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}

RENAME
packages/core/src/middlewares/bodyParser.middleware.spec.ts
packages/middleware-body/src/indes.spec.ts
+import { HttpRequest, HttpResponse } from '@marblejs/core';
import { of } from 'rxjs';
-import { HttpRequest, HttpResponse } from '../http.interface';
-import { Marbles } from '../util/marbles.spec-util';
-import { bodyParser$ } from './bodyParser.middleware';
+import { Marbles } from '../../core/src/util/marbles.spec-util';
+import { bodyParser$ } from './index';
const MockReq = require('mock-req');

ADD
packages/middleware-body/src/index.ts
+import { Effect, HttpError, HttpRequest, HttpStatus } from '@marblejs/core';
+import { Observable, of, throwError } from 'rxjs';
+import { catchError, map, switchMap, tap, toArray } from 'rxjs/operators';
+import { ContentType } from '../../core/src/util/contentType.util';
+
+const fromReadableStream = (stream: HttpRequest): Observable<any> => {
+ stream.pause();
+ return new Observable(observer => {
+ const next = chunk => observer.next(chunk);
+ const complete = () => observer.complete();
+ const error = err => observer.error(err);
+
+ stream
+ .on('data', next)
+ .on('error', error)
+ .on('end', complete)
+ .resume();
+
+ return () => {
+ stream.removeListener('data', next);
+ stream.removeListener('error', error);
+ stream.removeListener('end', complete);
+ };
+ });
+};
+
+const getBody = (req: HttpRequest) =>
+ fromReadableStream(req).pipe(
+ toArray(),
+ map(chunks => Buffer.concat(chunks)),
+ map(buffer => buffer.toString()),
+ map(body => {
+ switch (req.headers['content-type']) {
+ case ContentType.APPLICATION_JSON:
+ return JSON.parse(body);
+ default:
+ return body;
+ }
+ })
+ );
+
+export const bodyParser$: Effect<HttpRequest> = (request$, response) =>
+ request$.pipe(
+ switchMap(
+ req =>
+ req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH'
+ ? of(req).pipe(
+ switchMap(getBody),
+ tap(body => (req.body = body)),
+ map(() => req),
+ catchError(error =>
+ throwError(
+ new HttpError(
+ 'Request body parse error',
+ HttpStatus.BAD_REQUEST
+ )
+ )
+ )
+ )
+ : of(req)
+ )
+ );

ADD
packages/middleware-body/tsconfig.json
+{
+ "extends": "../../tsconfig.json",
+
+ "compilerOptions": {
+ "outDir": "./dist/",
+ "declaration": true,
+ "typeRoots": [
+ "./node_modules/@types",
+ "../../node_modules/@types"
+ ]
+ },
+
+ "inputs": [
+ "src"
+ ],
+
+ "include": [
+ "node_modules/@types",
+ "src/**/*.ts"
+ ],
+}

ADD
packages/middleware-logger/package-lock.json
+{
+ "requires": true,
+ "lockfileVersion": 1,
+ "dependencies": {
+ "@types/chalk": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz",
+ "integrity": "sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==",
+ "requires": {
+ "chalk": "*"
+ }
+ },
+ "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==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+ "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==",
+ "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="
+ },
+ "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="
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ },
+ "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"
+ }
+ }
+ }
+}

ADD
packages/middleware-logger/package.json
+{
+ "name": "@marblejs/middleware-logger",
+ "version": "0.2.2",
+ "description": "Logger middleware for Marble.js",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "scripts": {
+ "start": "npm run watch",
+ "watch": "tsc -w",
+ "build": "tsc",
+ "prepack": "npm run build",
+ "prepare": "npm run build"
+ },
+ "files": [
+ "dist/"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/marblejs/marble.git"
+ },
+ "engines": {
+ "node": ">= 8.0.0",
+ "npm": ">= 5.0.0"
+ },
+ "author": "marblejs",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/marblejs/marble/issues"
+ },
+ "homepage": "https://github.com/marblejs/marble#readme",
+ "peerDependencies": {
+ "@marblejs/core": "^0.2.2",
+ "rxjs": "~6.1.0"
+ },
+ "dependencies": {
+ "chalk": "^2.4.1"
+ },
+ "devDependencies": {
+ "@types/chalk": "^2.2.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}

RENAME
packages/core/src/middlewares/logger.middleware.spec.ts
packages/middleware-logger/src/index.spec.ts
+import { HttpRequest, HttpResponse } from '@marblejs/core';
import { EventEmitter } from 'events';
-import { HttpRequest, HttpResponse } from '../http.interface';
-import { Marbles } from '../util/marbles.spec-util';
-import { logger$ } from './logger.middleware';
+import { Marbles } from '../../core/src/util/marbles.spec-util';
+import { logger$ } from './index';
const createMockReq = (url: string, method: string) => ({ url, method } as HttpRequest);
const createMockRes = (status: number) => new class extends EventEmitter {

RENAME
packages/core/src/middlewares/logger.middleware.ts
packages/middleware-logger/src/index.ts
+import { Effect, HttpRequest } from '@marblejs/core';
import chalk from 'chalk';
import { tap } from 'rxjs/operators';
-import { Effect } from '../effects/effects.interface';
-import { HttpRequest } from '../http.interface';
const getStatusCode = (statusCode: number): string =>
statusCode >= 400
@@ -13,20 +12,27 @@ const getEndTime = (hrStartTime: [number, number]): number => {
return Math.round(hrEndTime * 100) / 100;
};
-const printLog = (method: string, url: string, statusCode: string, endTime: number) => {
+const printLog = (
+ method: string,
+ url: string,
+ statusCode: string,
+ endTime: number
+) => {
console.info(`%s %s %s %dms`, method, url, statusCode, endTime);
};
-export const logger$: Effect<HttpRequest> = (request$, response) => request$
- .pipe(
+export const logger$: Effect<HttpRequest> = (request$, response) =>
+ request$.pipe(
tap(request => {
const startTime = process.hrtime();
- response.on('finish', () => printLog(
+ response.on('finish', () =>
+ printLog(
request.method!,
request.url!,
getStatusCode(response.statusCode),
- getEndTime(startTime),
- ));
- }),
+ getEndTime(startTime)
+ )
+ );
+ })
);

ADD
packages/middleware-logger/tsconfig.json
+{
+ "extends": "../../tsconfig.json",
+
+ "compilerOptions": {
+ "outDir": "./dist/",
+ "declaration": true,
+ "typeRoots": [
+ "./node_modules/@types",
+ "../../node_modules/@types"
+ ]
+ },
+
+ "inputs": [
+ "src"
+ ],
+
+ "include": [
+ "node_modules/@types",
+ "src/**/*.ts"
+ ],
+}

RENAME
packages/core/src/specs/api.integration.spec.ts
packages/spec/api.integration.spec.ts
import { of } from 'rxjs';
import { filter, map, mapTo, switchMap } from 'rxjs/operators';
import * as request from 'supertest';
-import { combineRoutes } from '../effects/effects.combiner';
-import { Effect } from '../effects/effects.interface';
-import { HttpRequest } from '../http.interface';
-import { httpListener } from '../http.listener';
-import { bodyParser$ } from '../middlewares/bodyParser.middleware';
-import { matchPath, matchType, use } from '../operators';
+import { Effect, HttpRequest, combineRoutes, httpListener, matchPath, matchType, use } from '../core/src';
+import { bodyParser$ } from '../middleware-body/src';
const MOCKED_USER_LIST = [{ id: 1 }, { id: 2 }];

MODIFY
scripts/publish.sh
scripts/publish.sh
#!/usr/bin/env bash
cp README.md ./packages/core
+cp README.md ./packages/middleware-body
+cp README.md ./packages/middleware-logger
lerna publish
rm -rf ./packages/core/README.md
+rm -rf ./packages/middleware-body/README.md
+rm -rf ./packages/middleware-logger/README.md

feat(core): moved bodyParser$ and logger$ into separate packages