This commit is contained in:
crusader 2018-05-24 12:04:38 +09:00
commit bc3f80b726
82 changed files with 14268 additions and 0 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

39
.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# LoaferNg
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.3.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

241
angular.json Normal file
View File

@ -0,0 +1,241 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"example": {
"root": "example",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/example",
"index": "example/index.html",
"main": "example/main.ts",
"polyfills": "example/polyfills.ts",
"tsConfig": "example/tsconfig.app.json",
"assets": [
"example/favicon.ico",
"example/assets"
],
"styles": [
"example/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "example/environments/environment.ts",
"with": "example/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "example:build"
},
"configurations": {
"production": {
"browserTarget": "example:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "example:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "example/test.ts",
"polyfills": "example/polyfills.ts",
"tsConfig": "example/tsconfig.spec.json",
"karmaConfig": "example/karma.conf.js",
"styles": [
"example/styles.css"
],
"scripts": [],
"assets": [
"example/favicon.ico",
"example/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"example/tsconfig.app.json",
"example/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"example-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "example:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"@loafer/ng-logger": {
"root": "projects/loafer/ng-logger",
"sourceRoot": "projects/loafer/ng-logger/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/loafer/ng-logger/tsconfig.lib.json",
"project": "projects/loafer/ng-logger/ng-package.json"
},
"configurations": {
"production": {
"project": "projects/loafer/ng-logger/ng-package.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/loafer/ng-logger/src/test.ts",
"tsConfig": "projects/loafer/ng-logger/tsconfig.spec.json",
"karmaConfig": "projects/loafer/ng-logger/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/loafer/ng-logger/tsconfig.lib.json",
"projects/loafer/ng-logger/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"@loafer/ng-rest": {
"root": "projects/loafer/ng-rest",
"sourceRoot": "projects/loafer/ng-rest/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/loafer/ng-rest/tsconfig.lib.json",
"project": "projects/loafer/ng-rest/ng-package.json"
},
"configurations": {
"production": {
"project": "projects/loafer/ng-rest/ng-package.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/loafer/ng-rest/src/test.ts",
"tsConfig": "projects/loafer/ng-rest/tsconfig.spec.json",
"karmaConfig": "projects/loafer/ng-rest/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/loafer/ng-rest/tsconfig.lib.json",
"projects/loafer/ng-rest/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"@loafer/ng-rpc": {
"root": "projects/loafer/ng-rpc",
"sourceRoot": "projects/loafer/ng-rpc/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/loafer/ng-rpc/tsconfig.lib.json",
"project": "projects/loafer/ng-rpc/ng-package.json"
},
"configurations": {
"production": {
"project": "projects/loafer/ng-rpc/ng-package.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/loafer/ng-rpc/src/test.ts",
"tsConfig": "projects/loafer/ng-rpc/tsconfig.spec.json",
"karmaConfig": "projects/loafer/ng-rpc/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/loafer/ng-rpc/tsconfig.lib.json",
"projects/loafer/ng-rpc/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "example"
}

28
e2e/protractor.conf.js Normal file
View File

@ -0,0 +1,28 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

14
e2e/src/app.e2e-spec.ts Normal file
View File

@ -0,0 +1,14 @@
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
});

11
e2e/src/app.po.ts Normal file
View File

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}

13
e2e/tsconfig.e2e.json Normal file
View File

@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

View File

@ -0,0 +1,20 @@
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul>

View File

@ -0,0 +1,27 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to loafer-ng!');
}));
});

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
}

16
example/app/app.module.ts Normal file
View File

@ -0,0 +1,16 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

0
example/assets/.gitkeep Normal file
View File

9
example/browserslist Normal file
View File

@ -0,0 +1,9 @@
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
# IE 9-11

View File

@ -0,0 +1,3 @@
export const environment = {
production: true
};

View File

@ -0,0 +1,15 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

BIN
example/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

14
example/index.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>LoaferNg</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

31
example/karma.conf.js Normal file
View File

@ -0,0 +1,31 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

12
example/main.ts Normal file
View File

@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));

80
example/polyfills.ts Normal file
View File

@ -0,0 +1,80 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
*/
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
/*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*/
// (window as any).__Zone_enable_cross_context_check = true;
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

1
example/styles.css Normal file
View File

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

20
example/test.ts Normal file
View File

@ -0,0 +1,20 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

12
example/tsconfig.app.json Normal file
View File

@ -0,0 +1,12 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "es2015",
"types": []
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -0,0 +1,19 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"module": "commonjs",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

17
example/tslint.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}

11550
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

56
package.json Normal file
View File

@ -0,0 +1,56 @@
{
"name": "loafer-ng",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/common": "^6.0.2",
"@angular/core": "^6.0.2",
"@angular/compiler": "^6.0.2",
"@angular/animations": "^6.0.2",
"@angular/forms": "^6.0.2",
"@angular/http": "^6.0.2",
"@angular/platform-browser": "^6.0.2",
"@angular/platform-browser-dynamic": "^6.0.2",
"@angular/router": "^6.0.2",
"@loafer/core": "^0.0.1",
"@loafer/decorator": "^0.0.1",
"@ngrx/store": "^5.2.0",
"core-js": "^2.5.4",
"rxjs": "^6.0.0",
"rxjs-compat": "^6.0.0",
"zone.js": "^0.8.26"
},
"devDependencies": {
"@angular/compiler-cli": "^6.0.2",
"@angular-devkit/build-ng-packagr": "~0.6.3",
"@angular-devkit/build-angular": "~0.6.3",
"ng-packagr": "^3.0.0-rc.2",
"tsickle": ">=0.25.5",
"tslib": "^1.7.1",
"typescript": "~2.7.2",
"@angular/cli": "~6.0.3",
"@angular/language-service": "^6.0.2",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.2.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~1.7.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~1.4.2",
"karma-jasmine": "~1.1.1",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.3.0",
"ts-node": "~5.0.1",
"tslint": "~5.9.1"
}
}

View File

@ -0,0 +1,31 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@ -0,0 +1,8 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/loafer/ng-logger",
"deleteDestPath": true,
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/loafer/ng-logger",
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,15 @@
{
"name": "@loafer/ng-logger",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "https://git.loafle.net/loafer/ng.git"
},
"publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-loafle/"
},
"peerDependencies": {
"@angular/common": "^6.0.0-rc.0 || ^6.0.0",
"@angular/core": "^6.0.0-rc.0 || ^6.0.0"
}
}

View File

@ -0,0 +1,6 @@
import { LoggerLevel } from './ng-logger.type';
export class LoggerConfig {
level: LoggerLevel;
serverLogLevel?: LoggerLevel;
}

View File

@ -0,0 +1,65 @@
import {
NgModule,
ModuleWithProviders,
} from '@angular/core';
import {
LoggerConfig,
} from './ng-logger.config';
import {
LoggerService,
} from './ng-logger.service';
export interface LoggerFeatureModuleConfig {
url?: any;
}
export interface LoggerRootModuleConfig {
config: LoggerConfig;
}
const SERVICES = [
LoggerService,
];
@NgModule({})
export class LoggerRootModule {
constructor(
private loggerService: LoggerService,
) {
}
}
@NgModule({})
export class LoggerFeatureModule {
constructor(
private loggerService: LoggerService,
private root: LoggerRootModule,
) {
}
}
@NgModule({})
export class LoggerModule {
static forRoot(config: LoggerRootModuleConfig): ModuleWithProviders {
return {
ngModule: LoggerRootModule,
providers: [
{
provide: LoggerConfig,
useValue: config.config,
},
SERVICES,
],
};
}
static forFeature(config: LoggerFeatureModuleConfig): ModuleWithProviders {
return {
ngModule: LoggerFeatureModule,
providers: [
],
};
}
}

View File

@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { LoggerService } from './ng-logger.service';
describe('LoggerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [LoggerService]
});
});
it('should be created', inject([LoggerService], (service: LoggerService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,161 @@
import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import {
LoggerConfig,
} from './ng-logger.config';
import {
LoggerLevel,
LoggerLevelName,
} from './ng-logger.type';
export type ConsoleFunc = (message?: any, ...optionalParams: any[]) => void;
@Injectable()
export class LoggerService {
private _isIE: boolean;
public constructor(
private readonly config: LoggerConfig,
@Inject(PLATFORM_ID) private readonly platformId,
) {
this._isIE = isPlatformBrowser(platformId) &&
!!(navigator.userAgent.indexOf('MSIE') !== -1 || navigator.userAgent.match(/Trident\//) || navigator.userAgent.match(/Edge\//));
}
public get trace(): ConsoleFunc {
return this.getConsoleMethod(LoggerLevel.TRACE);
}
public get debug(): ConsoleFunc {
return this.getConsoleMethod(LoggerLevel.DEBUG);
}
public get info(): ConsoleFunc {
return this.getConsoleMethod(LoggerLevel.INFO);
}
public get log(): ConsoleFunc {
return this.getConsoleMethod(LoggerLevel.LOG);
}
public get warn(): ConsoleFunc {
return this.getConsoleMethod(LoggerLevel.WARN);
}
public get error(): ConsoleFunc {
return this.getConsoleMethod(LoggerLevel.ERROR);
}
private _console_log: ConsoleFunc;
private getConsoleMethod(level: LoggerLevel): ConsoleFunc {
if (level < this.config.level) {
return (message, ...additional: any[]) => {};
}
if (this._isIE) {
switch (level) {
case LoggerLevel.WARN:
return console.warn.bind(console, ...this.getLogHeader(level));
case LoggerLevel.ERROR:
return console.error.bind(console, ...this.getLogHeader(level));
case LoggerLevel.INFO:
return console.info.bind(console, ...this.getLogHeader(level));
default:
return console.log.bind(console, ...this.getLogHeader(level));
}
} else {
return console.log.bind(console, ...this.getLogHeader(level));
}
}
private getLogHeader(level: LoggerLevel): any[] {
const params: any[] = [];
params.push(`%c${this._timestamp()} [${LoggerLevelName[level]}]`);
if (!this._isIE) {
const color = this._getColor(level);
params.push(`color:${color}`);
}
return params;
}
private _timestamp(): string {
return new Date().toISOString();
}
private _log(level: LoggerLevel, message, additional: any[] = []): void {
if (!message) {
return;
}
// Allow logging on server even if client log level is off
// if (logOnServer) {
// this._logOnServer(level, message, additional);
// }
// if no message or the log level is less than the environ
if (level < this.config.level) {
return;
}
try {
message = typeof message === 'string' ? message : JSON.stringify(message, null, 2);
} catch (e) {
additional = [message, ...additional];
message = 'The provided "message" value could not be parsed with JSON.stringify().';
}
// Coloring doesn't work in IE
if (this._isIE) {
return this._logIE(level, message, additional);
}
const color = this._getColor(level);
const params: any[] = [];
params.push(`%c${this._timestamp()} [${LoggerLevelName[level]}]`);
params.push(`color:${color}`);
params.push(message);
params.push(...additional);
console.log.apply(console, params);
}
private _logIE(level: LoggerLevel, message: string, additional: any[] = []): void {
const params: any[] = [];
params.push(`${this._timestamp()} [${LoggerLevelName[level]}] `);
params.push(message);
params.push(...additional);
switch (level) {
case LoggerLevel.WARN:
console.warn.apply(console, params);
break;
case LoggerLevel.ERROR:
console.error.apply(console, params);
break;
case LoggerLevel.INFO:
console.info.apply(console, params);
break;
default:
console.log.apply(console, params);
}
}
private _getColor(level: LoggerLevel): 'blue' | 'teal' | 'gray' | 'red' | undefined {
switch (level) {
case LoggerLevel.TRACE:
return 'blue';
case LoggerLevel.DEBUG:
return 'teal';
case LoggerLevel.INFO:
case LoggerLevel.LOG:
return 'gray';
case LoggerLevel.WARN:
case LoggerLevel.ERROR:
return 'red';
case LoggerLevel.OFF:
default:
return undefined;
}
}
}

View File

@ -0,0 +1,26 @@
export enum LoggerLevel {
TRACE = 0,
DEBUG,
INFO,
LOG,
WARN,
ERROR,
OFF,
}
export const LoggerLevelName = [
'TRACE',
'DEBUG',
'INFO',
'LOG',
'WARN',
'ERROR',
'OFF'
];
export interface ServerLoggingParameter {
level: string;
message: string;
addtional?: string;
timestamp: Date;
}

View File

@ -0,0 +1,8 @@
/*
* Public API Surface of ng-logger
*/
export * from './lib/ng-logger.config';
export * from './lib/ng-logger.type';
export * from './lib/ng-logger.service';
export * from './lib/ng-logger.module';

View File

@ -0,0 +1,22 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -0,0 +1,33 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2015"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"flatModuleId": "AUTOGENERATED",
"flatModuleOutFile": "AUTOGENERATED"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

View File

@ -0,0 +1,31 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@ -0,0 +1,8 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/loafer/ng-rest",
"deleteDestPath": true,
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/loafer/ng-rest",
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,15 @@
{
"name": "@loafer/ng-rest",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "https://git.loafle.net/loafer/ng.git"
},
"publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-loafle/"
},
"peerDependencies": {
"@angular/common": "^6.0.0-rc.0 || ^6.0.0",
"@angular/core": "^6.0.0-rc.0 || ^6.0.0"
}
}

View File

@ -0,0 +1,65 @@
import { Injectable, Inject } from '@angular/core';
import { Location } from '@angular/common';
import { HttpClient, HttpHeaders, HttpParams, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/timeout';
import 'rxjs/add/observable/throw';
import {
RESTError,
RESTClientError,
} from './ng-rest.error';
export class RESTClient {
constructor(
private _baseURL: string,
private _httpClient: HttpClient,
) {
}
public get httpClient(): HttpClient {
return this._httpClient;
}
public request<T>(method: string, entry: string, options?: {
body?: any;
headers?: HttpHeaders | {
[header: string]: string | string[];
};
observe?: 'body';
params?: HttpParams | {
[param: string]: string | string[];
};
responseType?: 'json';
reportProgress?: boolean;
withCredentials?: boolean;
}): Observable<T> {
return this._httpClient
.request<T>(method, Location.joinWithSlash(this._baseURL, entry), options)
.map(response => response)
.catch((error: HttpErrorResponse) => {
const restClientError: RESTClientError = {
request: {
method: method,
entry: entry,
options: options,
},
response: error.error,
};
console.error(restClientError);
// const aryMsg = error.error.message.split('|');
// const resError: RESTError = {
// code: error.error.code,
// message: aryMsg[0],
// data: aryMsg[1] === 'null' ? '' : aryMsg[1],
// };
return Observable.throw(restClientError);
});
}
}

View File

@ -0,0 +1,14 @@
export interface RESTClientError {
request: {
method: string;
entry: string;
options?: any;
};
response: RESTError;
}
export interface RESTError {
code: number;
message: string;
data?: any;
}

View File

@ -0,0 +1,68 @@
import {
NgModule,
ModuleWithProviders,
Type,
Inject,
InjectionToken,
} from '@angular/core';
import {
_REST_BASE_URL,
} from './ng-rest.token';
import {
RESTService,
} from './ng-rest.service';
export interface RESTFeatureModuleConfig {
url?: any;
}
export interface RESTRootModuleConfig {
baseURL: string;
}
const SERVICES = [
RESTService,
];
@NgModule({})
export class RESTRootModule {
constructor(
private restService: RESTService,
) {
}
}
@NgModule({})
export class RESTFeatureModule {
constructor(
private restService: RESTService,
private root: RESTRootModule,
) {
}
}
@NgModule({})
export class RESTModule {
static forRoot(config: RESTRootModuleConfig): ModuleWithProviders {
return {
ngModule: RESTRootModule,
providers: [
{
provide: _REST_BASE_URL,
useValue: config.baseURL,
},
SERVICES,
],
};
}
static forFeature(config: RESTFeatureModuleConfig): ModuleWithProviders {
return {
ngModule: RESTFeatureModule,
providers: [
],
};
}
}

View File

@ -0,0 +1,16 @@
import {} from 'jasmine';
import { TestBed, inject } from '@angular/core/testing';
import { RESTService } from './ng-rest.service';
describe('RESTService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [RESTService]
});
});
it('should be created', inject([RESTService], (service: RESTService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,26 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Location } from '@angular/common';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/timeout';
import 'rxjs/add/observable/throw';
import { _REST_BASE_URL } from './ng-rest.token';
import { RESTClient } from './ng-rest.client';
@Injectable()
export class RESTService extends RESTClient {
constructor(
@Inject(_REST_BASE_URL) _baseURL: string,
@Inject(HttpClient) _httpClient: HttpClient,
) {
super(_baseURL, _httpClient);
}
}

View File

@ -0,0 +1,3 @@
import { InjectionToken } from '@angular/core';
export const _REST_BASE_URL = new InjectionToken('@loafer/ng-rest Internal Base URL');

View File

@ -0,0 +1,7 @@
/*
* Public API Surface of ng-rest
*/
export * from './lib/ng-rest.error';
export * from './lib/ng-rest.service';
export * from './lib/ng-rest.module';

View File

@ -0,0 +1,22 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -0,0 +1,33 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2015"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"flatModuleId": "AUTOGENERATED",
"flatModuleOutFile": "AUTOGENERATED"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

View File

@ -0,0 +1,31 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@ -0,0 +1,8 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/loafer/ng-rpc",
"deleteDestPath": true,
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/loafer/ng-rpc",
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,15 @@
{
"name": "@loafer/ng-rpc",
"version": "0.0.6",
"repository": {
"type": "git",
"url": "https://git.loafle.net/loafer/ng.git"
},
"publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-loafle/"
},
"peerDependencies": {
"@angular/common": "^6.0.0-rc.0 || ^6.0.0",
"@angular/core": "^6.0.0-rc.0 || ^6.0.0"
}
}

View File

@ -0,0 +1,146 @@
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { RPCClientError } from '../protocol/RPCError';
import { RPCClientRWC } from './RPCClientRWC';
import {
RPCClientCodec,
RPCClientResponseCodec,
RPCClientNotificationCodec,
} from '../protocol/RPCClientCodec';
export interface RPCRequestState {
subject: Subject<any>;
request: {
method: string;
params: any[];
};
}
export abstract class RPCClient<T> {
private _requestID: number;
private _pendingRequestsCount: number;
private _pendingRequests: Map<number, RPCRequestState>;
public constructor(
private _codec: RPCClientCodec,
private _rwc: RPCClientRWC<T>,
) {
this._requestID = 0;
this._pendingRequestsCount = 0;
this._pendingRequests = new Map();
}
private getRequestID(): number {
return ++this._requestID;
}
/**
* connect
*/
public connect(queryString?: string): void {
this._rwc.connect(queryString);
this._rwc.readResponse().subscribe(
(value: T) => {
this.onMessage(value);
},
(error: any) => {
console.error(error);
},
() => {
}
);
}
/**
* close
*/
public disconnect() {
this._rwc.disconnect();
}
/**
* notify
*/
public send(method: string, ...args: any[]): void {
this.sendInternal(false, method, args);
}
/**
* call
*/
public call<RT>(method: string, ...args: any[]): Observable<RT> {
return this.sendInternal<RT>(true, method, args);
}
/**
* callTimeout
*/
public callTimeout<RT>(ms: number, method: string, ...args: any[]): Observable<RT> {
return undefined;
}
private sendInternal<RT>(hasResponse: boolean, method: string, args?: any[]): Observable<RT> | undefined {
let id: number;
let resSubject: Subject<RT>;
if (hasResponse) {
id = this.getRequestID();
resSubject = new Subject<RT>();
const reqState: RPCRequestState = {
subject: resSubject,
request: {
method: method,
params: args,
}
};
this._pendingRequests.set(id, reqState);
this._pendingRequestsCount++;
}
const req = this._codec.request(method, args, id);
this._rwc.writeRequest(req);
if (undefined !== resSubject) {
return resSubject.asObservable();
}
return undefined;
}
private onMessage(message: Object): void {
const resCodec = this._codec.response(message);
if (resCodec.isNotification()) {
this.onNotification(resCodec.notification());
} else {
this.onResponse(resCodec);
}
}
protected onResponse(resCodec: RPCClientResponseCodec): void {
const id = resCodec.id();
const result = resCodec.result();
const error = resCodec.error();
const reqState: RPCRequestState = this._pendingRequests.get(id);
this._pendingRequests.delete(id);
this._pendingRequestsCount--;
if (undefined !== result) {
reqState.subject.next(result);
} else if (undefined !== error) {
const rpcClientError: RPCClientError = {
request: reqState.request,
response: error,
};
console.error(rpcClientError);
reqState.subject.error(rpcClientError);
}
}
protected abstract onNotification(notiCodec: RPCClientNotificationCodec): void;
}

View File

@ -0,0 +1,11 @@
import { InjectionToken } from '@angular/core';
import { Observable } from 'rxjs/Observable';
export interface RPCClientRWC<T> {
connect(queryString?: string): void;
readResponse(): Observable<T>;
writeRequest(data: any): void;
disconnect(): void;
connectionStatus(): Observable<boolean>;
}

View File

@ -0,0 +1,62 @@
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { map } from 'rxjs/operator/map';
import {
RxWebsocketSubject,
RxWebsocketSubjectConfig,
} from './RxWebsocketSubject';
import { RPCClientRWC } from '../../RPCClientRWC';
export class RPCClientWebsocketRWC<T> implements RPCClientRWC<T> {
private _wsSocketSubject: RxWebsocketSubject<T>;
private _responseSubject: Subject<T>;
public constructor(
private _config: RxWebsocketSubjectConfig,
) {
this._wsSocketSubject = new RxWebsocketSubject<T>(this._config);
}
public connect(queryString?: string): void {
if (undefined !== queryString) {
this._wsSocketSubject.queryString = queryString;
}
this._wsSocketSubject.connect();
this._wsSocketSubject.subscribe(
(value: T) => {
if (undefined !== this._responseSubject) {
this._responseSubject.next(value);
}
},
(error: any) => {
if (undefined !== this._responseSubject) {
this._responseSubject.error(error);
}
},
() => {
console.log('sss');
}
);
}
public disconnect(): void {
this._wsSocketSubject.disconnect();
}
public connectionStatus(): Observable<boolean> {
return this._wsSocketSubject.connectionStatus;
}
public readResponse(): Observable<T> {
if (undefined === this._responseSubject) {
this._responseSubject = new Subject<T>();
}
return this._responseSubject.asObservable();
}
public writeRequest(data: any): void {
this._wsSocketSubject.write(data);
}
}

View File

@ -0,0 +1,118 @@
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import { Subject } from 'rxjs/Subject';
import {
WebSocketSubject,
WebSocketSubjectConfig
} from 'rxjs/observable/dom/WebSocketSubject';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/takeWhile';
import 'rxjs/add/observable/interval';
export interface RxWebsocketSubjectConfig {
url: string;
protocol?: string | Array<string>;
reconnectInterval?: 5000;
reconnectRetry?: 10;
}
export class RxWebsocketSubject<T> extends Subject<T> {
private _reconnectionObservable: Observable<number>;
private _wsSubjectConfig: WebSocketSubjectConfig<T>;
private _socket: WebSocketSubject<any>;
private _connectionObserver: Observer<boolean>;
private _connectionStatus: Observable<boolean>;
private _queryString: string;
public constructor(private _config: RxWebsocketSubjectConfig) {
super();
this._connectionStatus = new Observable<boolean>((observer) => {
this._connectionObserver = observer;
}).share().distinctUntilChanged();
this._wsSubjectConfig = {
url: _config.url,
protocol: _config.protocol,
serializer: (value: any) => value,
closeObserver: {
next: (e: CloseEvent) => {
this._socket = null;
this._connectionObserver.next(false);
}
},
openObserver: {
next: (e: Event) => {
this._connectionObserver.next(true);
}
},
};
this._connectionStatus.subscribe((isConnected: boolean) => {
if (!this._reconnectionObservable && typeof(isConnected) === 'boolean' && !isConnected) {
this.reconnect();
}
});
}
public set queryString(query: string) {
this._queryString = query;
}
public get queryString(): string | undefined {
return this._queryString;
}
public get connectionStatus(): Observable<boolean> {
return this._connectionStatus;
}
public connect(): void {
const wsSubjectConfig = Object.assign({}, this._wsSubjectConfig);
if (undefined !== this._queryString) {
wsSubjectConfig.url = wsSubjectConfig.url + '?' + this._queryString;
}
this._socket = new WebSocketSubject(wsSubjectConfig);
this._socket.subscribe(
(m) => {
this.next(m);
},
(error: Event) => {
if (!this._socket) {
this.reconnect();
}
}
);
}
public disconnect(): void {
this._socket.complete();
}
private reconnect(): void {
this._reconnectionObservable = Observable.interval(this._config.reconnectInterval)
.takeWhile((v, index) => {
return index < this._config.reconnectRetry && !this._socket;
});
this._reconnectionObservable.subscribe(
() => {
this.connect();
},
null,
() => {
this._reconnectionObservable = null;
if (!this._socket) {
this.complete();
this._connectionObserver.complete();
}
}
);
}
public write(data: any): void {
this._socket.next(data);
}
}

View File

@ -0,0 +1,6 @@
export class SubscriberParameterError extends Error {
public constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}

View File

@ -0,0 +1,8 @@
import { InjectionToken } from '@angular/core';
export const RPC_CODEC = new InjectionToken('@loafer/ng-rpc RPC codec');
export const RPC_RWC = new InjectionToken('@loafer/ng-rpc RPC rwc');
export const _ROOT_SUBSCRIBERS = new InjectionToken('@loafer/ng-rpc RPC root subscribers');
export const _FEATURE_SUBSCRIBERS = new InjectionToken('@loafer/ng-rpc RPC feature subscribers');

View File

@ -0,0 +1,24 @@
import {
Type,
PropertyKeyType,
} from '@loafer/core';
import {
Annotation,
} from '@loafer/core/reflect';
import {
Decorator,
} from '@loafer/decorator';
export class RPCSubscriberAnnotation extends Annotation {
method: string;
}
export function RPCSubscriber(config: RPCSubscriberAnnotation): MethodDecorator {
return function<T>(target: Object, propertyKey: PropertyKeyType, descriptor: TypedPropertyDescriptor<T>)
: TypedPropertyDescriptor<T> | void {
Decorator.register(RPCSubscriberAnnotation, config, target, propertyKey, descriptor);
};
}

View File

@ -0,0 +1,106 @@
import {
NgModule,
ModuleWithProviders,
Type,
Inject,
InjectionToken,
} from '@angular/core';
import {
RPC_CODEC,
RPC_RWC,
_ROOT_SUBSCRIBERS,
_FEATURE_SUBSCRIBERS,
} from './core/token';
import {
RPCClientRWC,
} from './client/RPCClientRWC';
import {
RPCClientCodec,
} from './protocol/RPCClientCodec';
import {
RPCService,
} from './service/rpc.service';
import {
RPCSubscribeService,
} from './subscribe/subscribe.service';
const SERVICES = [
RPCService,
];
const SUBSCRIBERS = [
RPCSubscribeService,
];
@NgModule({})
export class RPCRootModule {
constructor(
private rpcService: RPCService,
private rpcSubscribeService: RPCSubscribeService,
@Inject(_ROOT_SUBSCRIBERS) rootSubscribers: any[],
) {
rootSubscribers.forEach((subscriber) => {
rpcSubscribeService.addSubscriber(subscriber);
});
}
}
@NgModule({})
export class RPCFeatureModule {
constructor(
private rpcService: RPCService,
private rpcSubscribeService: RPCSubscribeService,
@Inject(_FEATURE_SUBSCRIBERS) featureSubscribersGroups: any[][],
private root: RPCRootModule,
) {
featureSubscribersGroups.forEach((featureSubscribers) => {
featureSubscribers.forEach((subscriber) => {
rpcSubscribeService.addSubscriber(subscriber);
});
});
}
}
@NgModule({})
export class RPCModule {
static forRoot(subscribers: Type<any>[]): ModuleWithProviders {
return {
ngModule: RPCRootModule,
providers: [
subscribers,
{
provide: _ROOT_SUBSCRIBERS,
deps: subscribers,
useFactory: createSourceInstances,
},
SERVICES,
SUBSCRIBERS,
],
};
}
static forFeature(subscribers: Type<any>[]): ModuleWithProviders {
return {
ngModule: RPCFeatureModule,
providers: [
subscribers,
{
provide: _FEATURE_SUBSCRIBERS,
multi: true,
deps: subscribers,
useFactory: createSourceInstances,
},
],
};
}
}
export function createSourceInstances(...instances: any[]) {
return instances;
}

View File

@ -0,0 +1,20 @@
import { RPCError } from './RPCError';
export interface RPCClientCodec {
request(method: string, args: any[], id: number): any;
response(res: any): RPCClientResponseCodec;
}
export interface RPCClientResponseCodec {
id(): number | undefined;
error(): RPCError | undefined;
result(): any | undefined;
isNotification(): boolean;
notification(): RPCClientNotificationCodec | undefined;
}
export interface RPCClientNotificationCodec {
method(): string;
params(): any | undefined;
}

View File

@ -0,0 +1,41 @@
export interface RPCClientError {
request: {
method: string,
params: any[],
};
response: RPCError;
}
/**
* Error object representation when a method invocation fails.
*/
export interface RPCError {
/** Indicates the error type that occurred. */
code: RPCErrorCode;
/** A short description of the error. */
message: string;
/** Additional information about the error */
data?: any;
}/*
/** Error codes are same as xml-rpc codes. See http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php */
export const enum RPCErrorCode {
/** Parse error Invalid JSON was received by the Server. */
ParseError = -32700,
/** Invalid Request The JSON sent is not a valid Request object. */
InvalidRequest = -32600,
/** The method does not exist / is not available. */
MethodNotFound = -32601,
/** Invalid method parameter(s). */
InvalidParams = - -32602,
/** Internal JSON-RPC error. */
InternalError = -32603
/** -32000 to -32099: Reserved for implementation-defined Server errors. */
}

View File

@ -0,0 +1,125 @@
import {
RPCClientCodec,
RPCClientResponseCodec,
RPCClientNotificationCodec,
} from '../RPCClientCodec';
import {
RPCError,
} from '../RPCError';
export interface ClientNotification {
method: string;
params?: string[];
}
export interface ClientRequest {
jsonrpc: string;
method: string;
params?: string[];
id?: number;
}
export interface ClientResponse {
jsonrpc: string;
result?: any;
error?: RPCError;
id?: number;
}
export class JSONRPCClientCodec implements RPCClientCodec {
public request(method: string, args: any[], id?: number): any {
const params = convertParamsToStringArray(args);
const req: ClientRequest = {
jsonrpc: '2.0',
method: method,
params: 0 === params.length ? null : params,
id: id,
};
return JSON.stringify(req);
}
public response(res: any): RPCClientResponseCodec {
const _res: ClientResponse = {
id: res.id,
jsonrpc: res.jsonrpc,
result: res.result,
error: res.error,
};
return new JSONRPCClientResponseCodec(_res);
}
}
function convertParamsToStringArray(args: any[]): string[] | undefined {
const values: string[] = [];
if (undefined === args || null === args || 0 === args.length) {
return values;
}
for (let indexI = 0; indexI < args.length; indexI++) {
const arg = args[indexI];
switch (typeof arg) {
case 'boolean':
case 'number': // enum
values.push(String(arg));
break;
case 'string':
values.push(arg);
break;
case 'object': // array, map
values.push(JSON.stringify(arg));
break;
default:
throw new Error(`Not supported type[${typeof arg}]`);
}
}
return values;
}
export class JSONRPCClientResponseCodec implements RPCClientResponseCodec {
public constructor(private _res: ClientResponse) {
}
public id(): number | undefined {
return this._res.id;
}
public error(): RPCError | undefined {
return this._res.error;
}
public result(): any | undefined {
return this._res.result;
}
public isNotification(): boolean {
if (undefined !== this.id() || undefined === this.result()) {
return false;
}
return true;
}
public notification(): RPCClientNotificationCodec | undefined {
if (undefined !== this.id() || undefined === this.result()) {
return undefined;
}
const _noti: ClientNotification = {
method: this._res.result.method,
params: this._res.result.params,
};
return new JSONRPCClientNotificationCodec(_noti);
}
}
export class JSONRPCClientNotificationCodec implements RPCClientNotificationCodec {
public constructor(private _noti: ClientNotification) {
}
public method(): string {
return this._noti.method;
}
public params(): any[] | undefined {
return this._noti.params;
}
}

View File

@ -0,0 +1,33 @@
import { Injectable, Inject } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import {
RPC_CODEC,
RPC_RWC,
} from '../core/token';
import { RPCClient } from '../client/RPCClient';
import { RPCClientRWC } from '../client/RPCClientRWC';
import { RPCClientCodec, RPCClientNotificationCodec } from '../protocol/RPCClientCodec';
import {
RPCSubscribeService,
} from '../subscribe/subscribe.service';
@Injectable()
export class RPCService extends RPCClient<any> {
constructor(
@Inject(RPC_CODEC) rpcClientCodec: RPCClientCodec,
@Inject(RPC_RWC) rpcClientRWC: RPCClientRWC<any>,
private rpcSubscribeService: RPCSubscribeService,
) {
super(rpcClientCodec, rpcClientRWC);
}
protected onNotification(notiCodec: RPCClientNotificationCodec): void {
this.rpcSubscribeService.notify(notiCodec);
}
}

View File

@ -0,0 +1,162 @@
import { Injectable, Inject } from '@angular/core';
import { Store, select } from '@ngrx/store';
import {
Type,
PropertyKeyType,
} from '@loafer/core';
import { TypeUtil } from '@loafer/core/util/TypeUtil';
import {
Class,
Method,
Metadata,
} from '@loafer/core/reflect';
import { RPCSubscriberAnnotation } from '../decorator/rpc-subscriber.decorator';
import {
RPCClientNotificationCodec,
} from '../protocol/RPCClientCodec';
import { SubscriberParameterError } from '../core/error';
export interface SubscriberMethod {
className: PropertyKeyType;
methodName: PropertyKeyType;
parameterTypes: string[];
method: Method;
instance: any;
}
@Injectable()
export class RPCSubscribeService {
private subscriberTypes: Set<Type<any>>;
private subscribers: Set<any>;
private subscriberMethodMap: Map<string, SubscriberMethod[]>;
constructor(
) {
this.subscriberTypes = new Set();
this.subscribers = new Set();
this.subscriberMethodMap = new Map();
}
public addSubscriber(subscriber: Type<any>): void {
const type = TypeUtil.getType(subscriber);
if (this.subscriberTypes.has(type)) {
// console.log(`Subscriber[${type.name}] has been added`);
return;
}
this.subscriberTypes.add(type);
const clazz = Class.forType(type);
if (undefined === clazz) {
console.log(`Type[${subscriber.name}] is not decorated type`);
return;
}
const methods = clazz.getMethods();
methods.forEach((method, propertyKey) => {
const annon = method.getAnnotation(RPCSubscriberAnnotation);
if (undefined === annon) {
return;
}
const subscriberMethodName = annon.method;
let subscriberMethods: SubscriberMethod[] = this.subscriberMethodMap.get(subscriberMethodName);
if (undefined === subscriberMethods) {
subscriberMethods = [];
this.subscriberMethodMap.set(subscriberMethodName, subscriberMethods);
}
const paramTypes = this.getParamTypes(method);
const subscriberMethod: SubscriberMethod = {
className: clazz.getName(),
methodName: method.getName(),
parameterTypes: paramTypes,
method: method,
instance: subscriber,
};
subscriberMethods.push(subscriberMethod);
});
}
public notify(codec: RPCClientNotificationCodec): void {
const method = codec.method();
const params = codec.params();
const subscriberMethods: SubscriberMethod[] = this.subscriberMethodMap.get(method);
if (undefined === subscriberMethods) {
console.warn(`Subscriber for method[${method}] is not exist`);
return;
}
subscriberMethods.forEach((subscriberMethod) => {
try {
const args = this.converParams(params, subscriberMethod.parameterTypes);
subscriberMethod.method.invoke(subscriberMethod.instance, ...args);
} catch (error) {
console.error(error);
}
});
}
private getParamTypes(method: Method): string[] {
if (undefined === method || null === method || 0 === method.getParameterCount()) {
return [];
}
const parameters = method.getParameters();
const results: string[] = [];
for (let indexI = 0; indexI < parameters.length; indexI++) {
const paramType = parameters[indexI].getType();
results.push(paramType.name);
}
return results;
}
private converParams(params: string[], paramTypes: string[]): any[] {
const results: any[] = [];
if (undefined === params || null === params || 0 === params.length) {
return results;
}
if (undefined === paramTypes || null === paramTypes || 0 === paramTypes.length) {
return results;
}
if (params.length !== paramTypes.length) {
throw new SubscriberParameterError(`Count is not same from server[${params.length}] and method[${paramTypes.length}]`);
}
for (let indexI = 0; indexI < params.length; indexI++) {
const param = params[indexI];
const type = paramTypes[indexI];
switch (type) {
case 'Object':
case 'Array':
case 'Map':
results.push(JSON.parse(param));
break;
case 'String':
results.push(param);
break;
case 'Number':
results.push(Number(param));
break;
case 'Boolean':
results.push(Boolean(param));
break;
case 'Function':
throw new SubscriberParameterError(`Function type [${indexI}] is not allowed`);
default:
throw new SubscriberParameterError(`${type} type parameter[${indexI}] is not allowed`);
}
}
return results;
}
}

View File

@ -0,0 +1,17 @@
/*
* Public API Surface of ng-rpc
*/
export * from './lib/client/RPCClient';
export * from './lib/client/RPCClientRWC';
export * from './lib/client/rwc/websocket/RPCClientWebsocketRWC';
export * from './lib/client/rwc/websocket/RxWebsocketSubject';
export * from './lib/core/error';
export * from './lib/core/token';
export * from './lib/decorator/rpc-subscriber.decorator';
export * from './lib/protocol/RPCClientCodec';
export * from './lib/protocol/RPCError';
export * from './lib/protocol/json/JSONRPCClientCodec';
export * from './lib/service/rpc.service';
export * from './lib/subscribe/subscribe.service';
export * from './lib/ng-rpc.module';

View File

@ -0,0 +1,22 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -0,0 +1,33 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2015"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"flatModuleId": "AUTOGENERATED",
"flatModuleOutFile": "AUTOGENERATED"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

31
tsconfig.json Normal file
View File

@ -0,0 +1,31 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
],
"paths": {
"@loafer/ng-logger": [
"dist/loafer/ng-logger"
],
"@loafer/ng-rest": [
"dist/loafer/ng-rest"
],
"@loafer/ng-rpc": [
"dist/loafer/ng-rpc"
]
}
}
}

130
tslint.json Normal file
View File

@ -0,0 +1,130 @@
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"spaces"
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
"no-input-rename": true,
"no-output-rename": true,
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}