├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── continuous-deployment-workflow.yml
│ └── continuous-integration-workflow.yml
├── .gitignore
├── LICENSE
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── projects
├── cache
│ ├── .eslintrc.json
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ │ ├── lib
│ │ │ ├── cache.decorator.spec.ts
│ │ │ ├── cache.decorator.ts
│ │ │ ├── cache.instance.ts
│ │ │ ├── cache.manager.ts
│ │ │ ├── cache.model.ts
│ │ │ ├── cache.module.spec.ts
│ │ │ ├── cache.module.ts
│ │ │ ├── impl
│ │ │ │ ├── memory-cache.ts
│ │ │ │ ├── no-op-cache.ts
│ │ │ │ └── storage-cache.ts
│ │ │ └── simple-cache.manager.ts
│ │ ├── polyfills.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ └── tsconfig.spec.json
├── cookie
│ ├── .eslintrc.json
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ │ ├── lib
│ │ │ ├── browser
│ │ │ │ ├── browser-cookie.factory.ts
│ │ │ │ └── index.ts
│ │ │ ├── cookie.decorator.spec.ts
│ │ │ ├── cookie.decorator.ts
│ │ │ ├── cookie.model.ts
│ │ │ ├── cookie.module.spec.ts
│ │ │ ├── cookie.module.ts
│ │ │ ├── cookie.service.spec.ts
│ │ │ ├── cookie.service.ts
│ │ │ ├── cookie.token.ts
│ │ │ └── server
│ │ │ │ ├── index.ts
│ │ │ │ └── server-cookie.factory.ts
│ │ ├── polyfills.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ └── tsconfig.spec.json
├── device
│ ├── .eslintrc.json
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ │ ├── lib
│ │ │ ├── device.model.ts
│ │ │ ├── device.module.spec.ts
│ │ │ ├── device.module.ts
│ │ │ ├── device.service.spec.ts
│ │ │ ├── device.service.ts
│ │ │ └── device.token.ts
│ │ ├── polyfills.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ └── tsconfig.spec.json
├── logger
│ ├── .eslintrc.json
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ │ ├── lib
│ │ │ ├── console-logger.service.spec.ts
│ │ │ ├── console-logger.service.ts
│ │ │ ├── level.model.ts
│ │ │ ├── level.token.ts
│ │ │ ├── logger.decorator.spec.ts
│ │ │ ├── logger.decorator.ts
│ │ │ ├── logger.module.spec.ts
│ │ │ ├── logger.module.ts
│ │ │ ├── logger.rxjs.ts
│ │ │ └── logger.service.ts
│ │ ├── polyfills.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ └── tsconfig.spec.json
├── spring
│ ├── .eslintrc.json
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ │ ├── lib
│ │ │ ├── spring-data.model.ts
│ │ │ └── spring-data.spec.ts
│ │ ├── polyfills.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ └── tsconfig.spec.json
└── utils
│ ├── .eslintrc.json
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ ├── lib
│ │ ├── functions.ts
│ │ ├── once.decorator.spec.ts
│ │ ├── once.decorator.ts
│ │ ├── queue.decorator.spec.ts
│ │ ├── queue.decorator.ts
│ │ ├── wait.decorator.spec.ts
│ │ └── wait.decorator.ts
│ ├── polyfills.ts
│ ├── public-api.ts
│ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ └── tsconfig.spec.json
├── publish.sh
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # Ignore miscellaneous folders
2 | .angular/
3 | .github/
4 | .idea/
5 | coverage/
6 | dist/
7 | node_modules/
8 | tmp/
9 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": [
4 | "projects/**/*"
5 | ],
6 | "overrides": [
7 | {
8 | "files": [
9 | "*.ts"
10 | ],
11 | "parserOptions": {
12 | "project": [
13 | "tsconfig.json",
14 | "e2e/tsconfig.json"
15 | ],
16 | "createDefaultProgram": true
17 | },
18 | "extends": [
19 | "plugin:@angular-eslint/recommended",
20 | "plugin:@angular-eslint/template/process-inline-templates",
21 | "eslint:recommended",
22 | "plugin:@typescript-eslint/recommended",
23 | "plugin:@typescript-eslint/recommended-requiring-type-checking"
24 | ],
25 | "rules": {
26 | "@angular-eslint/component-selector": [
27 | "error",
28 | {
29 | "type": "element",
30 | "prefix": "app",
31 | "style": "kebab-case"
32 | }
33 | ],
34 | "@angular-eslint/directive-selector": [
35 | "error",
36 | {
37 | "type": "attribute",
38 | "prefix": "app",
39 | "style": "camelCase"
40 | }
41 | ],
42 | "@typescript-eslint/no-unsafe-member-access": "off",
43 | "@typescript-eslint/no-unsafe-assignment": "off",
44 | "@typescript-eslint/no-unsafe-call": "off",
45 | "@typescript-eslint/no-unsafe-return": "off",
46 | "@typescript-eslint/no-empty-function": "off",
47 | "@typescript-eslint/no-unused-vars": "off",
48 | "@typescript-eslint/no-explicit-any": "off",
49 | "@typescript-eslint/restrict-template-expressions": "off",
50 | "@typescript-eslint/ban-types": "off"
51 | }
52 | },
53 | {
54 | "files": [
55 | "*.html"
56 | ],
57 | "extends": [
58 | "plugin:@angular-eslint/template/recommended"
59 | ],
60 | "rules": {}
61 | }
62 | ]
63 | }
64 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## I'm submitting a...
2 |
3 |
4 | [ ] Regression (a behavior that used to work and stopped working in a new release)
5 | [ ] Bug report
6 | [ ] Feature request
7 | [ ] Documentation issue or request
8 |
9 |
10 | ## Packages
11 |
12 |
13 | [ ] @ngx-toolkit/cookie
14 | [ ] @ngx-toolkit/device
15 | [ ] @ngx-toolkit/logger
16 | [ ] @ngx-toolkit/spring
17 |
18 |
19 | ## Current behavior
20 |
21 |
22 |
23 | ## Expected behavior
24 |
25 |
26 |
27 | ## Minimal reproduction of the problem with instructions
28 |
32 |
33 | ## What is the motivation / use case for changing the behavior?
34 |
35 |
36 |
37 | ## Environment
38 |
39 |
40 | Angular version: X.Y.Z
41 | Ngx-Toolkit version: X.Y.Z
42 |
43 | Browser:
44 | - [ ] Chrome (desktop) version XX
45 | - [ ] Chrome (Android) version XX
46 | - [ ] Chrome (iOS) version XX
47 | - [ ] Firefox version XX
48 | - [ ] Safari (desktop) version XX
49 | - [ ] Safari (iOS) version XX
50 | - [ ] IE version XX
51 | - [ ] Edge version XX
52 |
53 | For Tooling issues:
54 | - Node version: XX
55 | - Platform:
56 |
57 | Others:
58 |
59 |
60 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## PR Checklist
2 | Please check if your PR fulfills the following requirements:
3 |
4 | - [ ] Tests for the changes have been added (for bug fixes / features)
5 | - [ ] Docs have been added / updated (for bug fixes / features)
6 |
7 |
8 | ## PR Type
9 | What kind of change does this PR introduce?
10 |
11 |
12 | ```
13 | [ ] Bugfix
14 | [ ] Feature
15 | [ ] Code style update (formatting, local variables)
16 | [ ] Refactoring (no functional changes, no api changes)
17 | [ ] Build related changes
18 | [ ] Documentation content changes
19 | [ ] Other... Please describe:
20 | ```
21 |
22 | ## What is the current behavior?
23 |
24 |
25 | Issue Number: N/A
26 |
27 |
28 | ## What is the new behavior?
29 |
30 |
31 | ## Does this PR introduce a breaking change?
32 | ```
33 | [ ] Yes
34 | [ ] No
35 | ```
36 |
37 |
38 |
39 |
40 | ## Other information
41 |
--------------------------------------------------------------------------------
/.github/workflows/continuous-deployment-workflow.yml:
--------------------------------------------------------------------------------
1 | name: CD
2 | on:
3 | release:
4 | types: [created]
5 | jobs:
6 | publish:
7 | name: Publish to NPM
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v1
11 | - uses: actions/setup-node@v1
12 | with:
13 | node-version: '16'
14 | registry-url: https://registry.npmjs.org
15 | - run: npm ci --ignore-scripts
16 | - run: npm run lint
17 | - run: npm run test
18 | - run: npm run build:libs
19 | - run: bash publish.sh -xe
20 | env:
21 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
22 |
--------------------------------------------------------------------------------
/.github/workflows/continuous-integration-workflow.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [ push, pull_request ]
3 | jobs:
4 | checks:
5 | name: Linters
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v1
9 | - uses: actions/setup-node@v1
10 | with:
11 | node-version: '16'
12 | - run: npm ci --ignore-scripts
13 | - run: npm run lint
14 | tests:
15 | name: Tests
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v1
19 | - name: Use Node.js 16.x
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: 16.x
23 | - run: |
24 | npm ci --ignore-scripts
25 | npm run test
26 | cat ./coverage/*/lcov.info > ./coverage/lcov.info
27 | - name: Coveralls
28 | uses: coverallsapp/github-action@master
29 | with:
30 | github-token: ${{ secrets.GITHUB_TOKEN }}
31 | build:
32 | name: Build
33 | runs-on: ubuntu-latest
34 | steps:
35 | - uses: actions/checkout@v1
36 | - uses: actions/setup-node@v1
37 | with:
38 | node-version: '16'
39 | - run: npm ci --ignore-scripts
40 | - run: npm run build:libs
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | # Only exists if Bazel was run
8 | /bazel-out
9 |
10 | # dependencies
11 | /node_modules
12 |
13 | # profiling files
14 | chrome-profiler-events*.json
15 | speed-measure-plugin*.json
16 |
17 | # IDEs and editors
18 | /.idea
19 | .project
20 | .classpath
21 | .c9/
22 | *.launch
23 | .settings/
24 | *.sublime-workspace
25 |
26 | # IDE - VSCode
27 | .vscode/*
28 | !.vscode/settings.json
29 | !.vscode/tasks.json
30 | !.vscode/launch.json
31 | !.vscode/extensions.json
32 | .history/*
33 |
34 | # misc
35 | /.angular/cache
36 | /.sass-cache
37 | /connect.lock
38 | /coverage
39 | /libpeerconnection.log
40 | npm-debug.log
41 | yarn-error.log
42 | testem.log
43 | /typings
44 |
45 | # System Files
46 | .DS_Store
47 | Thumbs.db
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Dewizz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/org/ngx-toolkit)
2 | [](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
3 | [](https://travis-ci.org/dewizz/ngx-toolkit)
4 | [](https://coveralls.io/github/dewizz/ngx-toolkit?branch=master)
5 | [](https://gitter.im/ngx-toolkit/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | # @ngx-toolkit
8 | > Set of components for Angular.
9 |
10 | # Table of Contents
11 | * [Packages](#packages)
12 | * [License](#license)
13 |
14 | ---
15 |
16 | # Packages
17 | The packages from this repository are published as scoped packages under [@ngx-toolkit](https://www.npmjs.com/org/ngx-toolkit)
18 |
19 | - [@ngx-toolkit/cache ](https://github.com/dewizz/ngx-toolkit/blob/master/projects/cache/README.md) - Cache Service
20 | - [@ngx-toolkit/cookie](https://github.com/dewizz/ngx-toolkit/blob/master/projects/cookie/README.md) - Cookie Service
21 | - [@ngx-toolkit/device](https://github.com/dewizz/ngx-toolkit/blob/master/projects/device/README.md) - Device detection
22 | - [@ngx-toolkit/logger](https://github.com/dewizz/ngx-toolkit/blob/master/projects/logger/README.md) - Logger Service
23 | - [@ngx-toolkit/spring](https://github.com/dewizz/ngx-toolkit/blob/master/projects/spring/README.md) - Spring Utilities
24 | - [@ngx-toolkit/utils](https://github.com/dewizz/ngx-toolkit/blob/master/projects/utils/README.md) - Common Utilities
25 |
26 | ----
27 |
28 | # License
29 | © 2018 Dewizz
30 |
31 | [MIT](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
32 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "cli": {
4 | "defaultCollection": "@angular-eslint/schematics"
5 | },
6 | "version": 1,
7 | "newProjectRoot": "projects",
8 | "projects": {
9 | "cache": {
10 | "projectType": "library",
11 | "root": "projects/cache",
12 | "sourceRoot": "projects/cache/src",
13 | "prefix": "lib",
14 | "architect": {
15 | "build": {
16 | "builder": "@angular-devkit/build-angular:ng-packagr",
17 | "options": {
18 | "tsConfig": "projects/cache/tsconfig.lib.json",
19 | "project": "projects/cache/ng-package.json"
20 | },
21 | "configurations": {
22 | "production": {
23 | "tsConfig": "projects/cache/tsconfig.lib.prod.json"
24 | }
25 | }
26 | },
27 | "test": {
28 | "builder": "@angular-devkit/build-angular:karma",
29 | "options": {
30 | "main": "projects/cache/src/test.ts",
31 | "polyfills": "projects/cache/src/polyfills.ts",
32 | "tsConfig": "projects/cache/tsconfig.spec.json",
33 | "karmaConfig": "projects/cache/karma.conf.js"
34 | }
35 | },
36 | "lint": {
37 | "builder": "@angular-eslint/builder:lint",
38 | "options": {
39 | "lintFilePatterns": [
40 | "projects/cache/**/*.ts",
41 | "projects/cache/**/*.html"
42 | ]
43 | }
44 | }
45 | }
46 | },
47 | "cookie": {
48 | "projectType": "library",
49 | "root": "projects/cookie",
50 | "sourceRoot": "projects/cookie/src",
51 | "prefix": "lib",
52 | "architect": {
53 | "build": {
54 | "builder": "@angular-devkit/build-angular:ng-packagr",
55 | "options": {
56 | "tsConfig": "projects/cookie/tsconfig.lib.json",
57 | "project": "projects/cookie/ng-package.json"
58 | },
59 | "configurations": {
60 | "production": {
61 | "tsConfig": "projects/cookie/tsconfig.lib.prod.json"
62 | }
63 | }
64 | },
65 | "test": {
66 | "builder": "@angular-devkit/build-angular:karma",
67 | "options": {
68 | "main": "projects/cookie/src/test.ts",
69 | "polyfills": "projects/cookie/src/polyfills.ts",
70 | "tsConfig": "projects/cookie/tsconfig.spec.json",
71 | "karmaConfig": "projects/cookie/karma.conf.js"
72 | }
73 | },
74 | "lint": {
75 | "builder": "@angular-eslint/builder:lint",
76 | "options": {
77 | "lintFilePatterns": [
78 | "projects/cookie/**/*.ts",
79 | "projects/cookie/**/*.html"
80 | ]
81 | }
82 | }
83 | }
84 | },
85 | "device": {
86 | "projectType": "library",
87 | "root": "projects/device",
88 | "sourceRoot": "projects/device/src",
89 | "prefix": "lib",
90 | "architect": {
91 | "build": {
92 | "builder": "@angular-devkit/build-angular:ng-packagr",
93 | "options": {
94 | "tsConfig": "projects/device/tsconfig.lib.json",
95 | "project": "projects/device/ng-package.json"
96 | },
97 | "configurations": {
98 | "production": {
99 | "tsConfig": "projects/device/tsconfig.lib.prod.json"
100 | }
101 | }
102 | },
103 | "test": {
104 | "builder": "@angular-devkit/build-angular:karma",
105 | "options": {
106 | "main": "projects/device/src/test.ts",
107 | "polyfills": "projects/device/src/polyfills.ts",
108 | "tsConfig": "projects/device/tsconfig.spec.json",
109 | "karmaConfig": "projects/device/karma.conf.js"
110 | }
111 | },
112 | "lint": {
113 | "builder": "@angular-eslint/builder:lint",
114 | "options": {
115 | "lintFilePatterns": [
116 | "projects/device/**/*.ts",
117 | "projects/device/**/*.html"
118 | ]
119 | }
120 | }
121 | }
122 | },
123 | "logger": {
124 | "projectType": "library",
125 | "root": "projects/logger",
126 | "sourceRoot": "projects/logger/src",
127 | "prefix": "lib",
128 | "architect": {
129 | "build": {
130 | "builder": "@angular-devkit/build-angular:ng-packagr",
131 | "options": {
132 | "tsConfig": "projects/logger/tsconfig.lib.json",
133 | "project": "projects/logger/ng-package.json"
134 | },
135 | "configurations": {
136 | "production": {
137 | "tsConfig": "projects/logger/tsconfig.lib.prod.json"
138 | }
139 | }
140 | },
141 | "test": {
142 | "builder": "@angular-devkit/build-angular:karma",
143 | "options": {
144 | "main": "projects/logger/src/test.ts",
145 | "polyfills": "projects/logger/src/polyfills.ts",
146 | "tsConfig": "projects/logger/tsconfig.spec.json",
147 | "karmaConfig": "projects/logger/karma.conf.js"
148 | }
149 | },
150 | "lint": {
151 | "builder": "@angular-eslint/builder:lint",
152 | "options": {
153 | "lintFilePatterns": [
154 | "projects/logger/**/*.ts",
155 | "projects/logger/**/*.html"
156 | ]
157 | }
158 | }
159 | }
160 | },
161 | "spring": {
162 | "projectType": "library",
163 | "root": "projects/spring",
164 | "sourceRoot": "projects/spring/src",
165 | "prefix": "lib",
166 | "architect": {
167 | "build": {
168 | "builder": "@angular-devkit/build-angular:ng-packagr",
169 | "options": {
170 | "tsConfig": "projects/spring/tsconfig.lib.json",
171 | "project": "projects/spring/ng-package.json"
172 | },
173 | "configurations": {
174 | "production": {
175 | "tsConfig": "projects/spring/tsconfig.lib.prod.json"
176 | }
177 | }
178 | },
179 | "test": {
180 | "builder": "@angular-devkit/build-angular:karma",
181 | "options": {
182 | "main": "projects/spring/src/test.ts",
183 | "polyfills": "projects/spring/src/polyfills.ts",
184 | "tsConfig": "projects/spring/tsconfig.spec.json",
185 | "karmaConfig": "projects/spring/karma.conf.js"
186 | }
187 | },
188 | "lint": {
189 | "builder": "@angular-eslint/builder:lint",
190 | "options": {
191 | "lintFilePatterns": [
192 | "projects/spring/**/*.ts",
193 | "projects/spring/**/*.html"
194 | ]
195 | }
196 | }
197 | }
198 | },
199 | "utils": {
200 | "projectType": "library",
201 | "root": "projects/utils",
202 | "sourceRoot": "projects/utils/src",
203 | "prefix": "lib",
204 | "architect": {
205 | "build": {
206 | "builder": "@angular-devkit/build-angular:ng-packagr",
207 | "options": {
208 | "tsConfig": "projects/utils/tsconfig.lib.json",
209 | "project": "projects/utils/ng-package.json"
210 | },
211 | "configurations": {
212 | "production": {
213 | "tsConfig": "projects/utils/tsconfig.lib.prod.json"
214 | }
215 | }
216 | },
217 | "test": {
218 | "builder": "@angular-devkit/build-angular:karma",
219 | "options": {
220 | "main": "projects/utils/src/test.ts",
221 | "polyfills": "projects/utils/src/polyfills.ts",
222 | "tsConfig": "projects/utils/tsconfig.spec.json",
223 | "karmaConfig": "projects/utils/karma.conf.js"
224 | }
225 | },
226 | "lint": {
227 | "builder": "@angular-eslint/builder:lint",
228 | "options": {
229 | "lintFilePatterns": [
230 | "projects/utils/**/*.ts",
231 | "projects/utils/**/*.html"
232 | ]
233 | }
234 | }
235 | }
236 | }
237 | },
238 | "defaultProject": "cache"
239 | }
240 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-toolkit",
3 | "version": "13.2.1",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "build": "ng build",
9 | "test": "npm run test:lib:device && npm run test:lib:cache && npm run test:lib:cookie && npm run test:lib:logger && npm run test:lib:spring && npm run test:lib:utils",
10 | "test:lib:device": "ng test device --watch false --code-coverage --browsers=ChromeHeadlessCI",
11 | "test:lib:cache": "ng test cache --watch false --code-coverage --browsers=ChromeHeadlessCI",
12 | "test:lib:cookie": "ng test cookie --watch false --code-coverage --browsers=ChromeHeadlessCI",
13 | "test:lib:logger": "ng test logger --watch false --code-coverage --browsers=ChromeHeadlessCI",
14 | "test:lib:spring": "ng test spring --watch false --code-coverage --browsers=ChromeHeadlessCI",
15 | "test:lib:utils": "ng test utils --watch false --code-coverage --browsers=ChromeHeadlessCI",
16 | "lint": "ng lint",
17 | "e2e": "ng e2e",
18 | "build:libs": "npm run build:lib:device && npm run build:lib:cache && npm run build:lib:cookie && npm run build:lib:logger && npm run build:lib:spring && npm run build:lib:utils",
19 | "build:lib:device": "ng build device --configuration production",
20 | "build:lib:cache": "ng build cache --configuration production",
21 | "build:lib:cookie": "ng build cookie --configuration production",
22 | "build:lib:logger": "ng build logger --configuration production",
23 | "build:lib:spring": "ng build spring --configuration production",
24 | "build:lib:utils": "ng build utils --configuration production"
25 | },
26 | "private": true,
27 | "dependencies": {
28 | "@angular/animations": "~13.2.0",
29 | "@angular/common": "~13.2.0",
30 | "@angular/compiler": "~13.2.0",
31 | "@angular/core": "~13.2.0",
32 | "@angular/forms": "~13.2.0",
33 | "@angular/platform-browser": "~13.2.0",
34 | "@angular/platform-browser-dynamic": "~13.2.0",
35 | "@angular/router": "~13.2.0",
36 | "rxjs": "~6.5.4",
37 | "tslib": "^2.3.1",
38 | "zone.js": "~0.11.4"
39 | },
40 | "devDependencies": {
41 | "@angular-devkit/build-angular": "~13.2.0",
42 | "@angular-eslint/builder": "~13.0.1",
43 | "@angular-eslint/eslint-plugin": "~13.0.1",
44 | "@angular-eslint/eslint-plugin-template": "~13.0.1",
45 | "@angular-eslint/schematics": "~13.0.1",
46 | "@angular-eslint/template-parser": "~13.0.1",
47 | "@angular/cli": "~13.2.0",
48 | "@angular/compiler-cli": "~13.2.0",
49 | "@angular/language-service": "~13.2.0",
50 | "@types/jasmine": "~3.10.0",
51 | "@types/node": "^12.11.1",
52 | "@typescript-eslint/eslint-plugin": "~5.10.1",
53 | "@typescript-eslint/parser": "~5.10.1",
54 | "codelyzer": "^6.0.2",
55 | "eslint": "^8.2.0",
56 | "jasmine-core": "~4.0.0",
57 | "karma": "~6.3.0",
58 | "karma-chrome-launcher": "~3.1.0",
59 | "karma-coverage": "~2.1.0",
60 | "karma-jasmine": "~4.0.0",
61 | "karma-jasmine-html-reporter": "~1.7.0",
62 | "ng-packagr": "^13.2.0",
63 | "protractor": "~7.0.0",
64 | "ts-node": "~10.4.0",
65 | "tslint": "~6.1.3",
66 | "typescript": "~4.5.2"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/projects/cache/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json",
3 | "ignorePatterns": [
4 | "!**/*",
5 | "**/*.spec.ts"
6 | ],
7 | "overrides": [
8 | {
9 | "files": [
10 | "*.ts"
11 | ],
12 | "parserOptions": {
13 | "project": [
14 | "projects/cache/tsconfig.lib.json",
15 | "projects/cache/tsconfig.spec.json"
16 | ],
17 | "createDefaultProgram": true
18 | },
19 | "rules": {
20 | "@angular-eslint/directive-selector": [
21 | "error",
22 | {
23 | "type": "attribute",
24 | "prefix": "lib",
25 | "style": "camelCase"
26 | }
27 | ],
28 | "@angular-eslint/component-selector": [
29 | "error",
30 | {
31 | "type": "element",
32 | "prefix": "lib",
33 | "style": "kebab-case"
34 | }
35 | ],
36 | "@typescript-eslint/no-unsafe-member-access": "off",
37 | "@typescript-eslint/no-unsafe-assignment": "off",
38 | "@typescript-eslint/no-unsafe-call": "off",
39 | "@typescript-eslint/no-unsafe-return": "off",
40 | "@typescript-eslint/no-unsafe-argument": "off",
41 | "@typescript-eslint/no-empty-function": "off",
42 | "@typescript-eslint/no-unused-vars": "off",
43 | "@typescript-eslint/no-explicit-any": "off",
44 | "@typescript-eslint/restrict-template-expressions": "off",
45 | "@typescript-eslint/ban-types": "off"
46 | }
47 | },
48 | {
49 | "files": [
50 | "*.html"
51 | ],
52 | "rules": {}
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/projects/cache/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/@ngx-toolkit/cache)
2 | [](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
3 | [](https://travis-ci.org/dewizz/ngx-toolkit)
4 | [](https://coveralls.io/github/dewizz/ngx-toolkit?branch=master)
5 | [](https://gitter.im/ngx-toolkit/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | # @ngx-toolkit/cache
8 |
9 | > Angular Cache implementation for Browser & Server platforms.
10 |
11 | # Table of contents:
12 | * [Installation](#installation)
13 | * [Annotations](#annotations)
14 | * [Cache](#cache)
15 | * [MemoryCache](#memorycache)
16 | * [StorageCache](#storagecache)
17 | * [NoOpCache](#noopcache)
18 | * [Custom Implementation](#custom-implementation)
19 | * [License](#license)
20 |
21 | ---
22 |
23 | # Installation
24 |
25 | Install the npm package.
26 |
27 | ```bash
28 | # To get the latest stable version and update package.json file:
29 | npm install @ngx-toolkit/cache --save
30 | # or
31 | yarn add @ngx-toolkit/cache
32 | ```
33 |
34 | Registered `CacheModule` in the root Module of your application with `forRoot(caches: Cache[])` static method.
35 |
36 | ```typescript
37 | import { NgModule } from '@angular/core';
38 | import { BrowserModule } from '@angular/platform-browser';
39 | import { CacheModule, MemoryCache } from '@ngx-toolkit/cache';
40 |
41 | import { AppComponent } from './app.component';
42 |
43 | @NgModule({
44 | imports: [ BrowserModule, CacheModule.forRoot([
45 | new MemoryCache('myMemoryCache')
46 | ]) ],
47 | declarations: [ AppComponent ],
48 | bootstrap: [ AppComponent ]
49 | })
50 | export class AppModule { }
51 | ```
52 | ----
53 |
54 | # Annotations
55 |
56 | ## CacheDefaults
57 | ````typescript
58 | /**
59 | * Allows the configuration of defaults for `CacheResult`, `CachePut`, `CacheRemove`, and `CacheRemoveAll` at the class level.
60 | * Without the method level annotations this annotation has no effect.
61 | * @param cacheName
62 | */
63 | @CacheDefaults(cacheName: string)
64 | ````
65 |
66 | ## CacheResult
67 | ````typescript
68 | /**
69 | * When a method annotated with `CacheResult` is invoked a cache key will be generated
70 | * and *Cache.get(key)* is called before the annotated method actually executes.
71 | * If a value is found in the cache it is returned and the annotated method is never actually executed.
72 | * If no value is found the annotated method is invoked and the returned value is stored in the cache with the generated key.
73 | *
74 | * @param params (Optional) {cacheName?: string}
75 | */
76 | @CacheResult(params?: { cacheName?: string })
77 | ````
78 |
79 | ## CachePut
80 | ````typescript
81 | /**
82 | * When a method annotated with `CachePut` is invoked a cache key will be generated
83 | * and *Cache.put(key, value)* will be invoked on the specified cache storing the value marked with `CacheValue`.
84 | *
85 | * @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
86 | */
87 | @CachePut(params?: {cacheName?: string, afterInvocation: boolean = true})
88 | ````
89 |
90 | ## CacheKey
91 | ````typescript
92 | /**
93 | * Marks a method argument as part of the cache key.
94 | * If no arguments are marked all arguments are used.
95 | * The exception is for a method annotated with `CachePut` where the `CacheValue` parameter is never included in the key.
96 | */
97 | @CacheKey()
98 | ````
99 |
100 | ## CacheValue
101 | ````typescript
102 | /**
103 | * Marks the parameter to be cached for a method annotated with `CachePut`.
104 | */
105 | @CacheValue()
106 | ````
107 |
108 | ## CacheRemove
109 | ````typescript
110 | /**
111 | * When a method annotated with `CacheRemove` is invoked a cache key will be generated
112 | * and *Cache.remove(key)* will be invoked on the specified cache.
113 | * The default behavior is to call *Cache.evict(key)* after the annotated method is invoked,
114 | * this behavior can be changed by setting *`afterInvocation`* to false in which case *Cache.evict(key)*
115 | * will be called before the annotated method is invoked.
116 | *
117 | * @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
118 | */
119 | @CacheRemove(params?: {cacheName?: string, afterInvocation: boolean = true})
120 | ````
121 |
122 | ## CacheRemoveAll
123 | ````typescript
124 | /**
125 | * When a method annotated with `CacheRemoveAll` is invoked all elements in the specified cache will be removed via the *Cache.clear()* method.
126 | * The default behavior is to call *Cache.clear()* after the annotated method is invoked,
127 | * this behavior can be changed by setting *`afterInvocation`* to false in which case *Cache.clear()* will be called before the annotated method is invoked.
128 | *
129 | * @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
130 | */
131 | @CacheRemoveAll(params?: {cacheName?: string, afterInvocation: boolean = true})
132 | ````
133 |
134 | Example:
135 | ````typescript
136 | @CacheDefaults('myCacheBean')
137 | class CacheBean {
138 |
139 | @CachePut()
140 | myMethod(@CacheKey() id: number, @CacheValue() value: any): void {
141 | ...
142 | }
143 |
144 | @CacheResult()
145 | get(id: number): any {
146 | ...
147 | }
148 |
149 | @CacheRemove()
150 | refresh(id: number): void {
151 | ...
152 | }
153 |
154 | @CacheRemoveAll()
155 | refreshAll(): void {
156 | ...
157 | }
158 | }
159 | ````
160 |
161 | # Cache
162 |
163 | ## MemoryCache
164 |
165 | Cache in memory
166 |
167 | ```typescript
168 | import { MemoryCache } from '@ngx-toolkit/cache';
169 | new MemoryCache('myCacheName');
170 | ```
171 |
172 | ## StorageCache
173 |
174 | Cache in a storage
175 |
176 | ```typescript
177 | import { StorageCache } from '@ngx-toolkit/cache';
178 | new StorageCache('myCacheName', window.sessionStorage || window.localStorage);
179 | ```
180 |
181 | ## NoOpCache
182 |
183 | *No cache, do nothing...*
184 |
185 | ```typescript
186 | import { NoOpCache } from '@ngx-toolkit/cache';
187 | new NoOpCache('myCacheName');
188 | ```
189 |
190 |
191 | # Custom implementation
192 |
193 | You can create your own implementation. You simply need to implement the `Cache` interface:
194 |
195 | ```typescript
196 | export interface Cache {
197 |
198 | /**
199 | * Return the cache name.
200 | */
201 | readonly name: string;
202 |
203 | /**
204 | * Return the value to which this cache maps the specified key
205 | */
206 | get(key: string): T;
207 |
208 | /**
209 | * Associate the specified value with the specified key in this cache.
210 | * If the cache previously contained a mapping for this key, the old
211 | * value is replaced by the specified value.
212 | */
213 | put(key: string, value: T): void;
214 |
215 | /**
216 | * Evict the mapping for this key from this cache if it is present.
217 | */
218 | evict(key: string): void;
219 |
220 | /**
221 | * Remove all mappings from the cache.
222 | */
223 | clear(): void;
224 | }
225 |
226 | ```
227 |
228 | # License
229 | © 2018 Dewizz
230 |
231 | [MIT](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
232 |
--------------------------------------------------------------------------------
/projects/cache/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, '../../coverage/cache'),
29 | subdir: '.',
30 | reporters: [
31 | {type: 'html'},
32 | {type: 'lcovonly'},
33 | {type: 'text-summary'}
34 | ]
35 | },
36 | reporters: ['progress', 'kjhtml'],
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessCI'],
42 | customLaunchers: {
43 | ChromeHeadlessCI: {
44 | base: 'ChromeHeadless',
45 | flags: ['--no-sandbox']
46 | }
47 | },
48 | singleRun: false,
49 | restartOnFileChange: true
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/projects/cache/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/cache",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/cache/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-toolkit/cache",
3 | "version": "0.0.0-PLACEHOLDER",
4 | "description": "Angular cache with Universal support",
5 | "homepage": "https://github.com/dewizz/ngx-toolkit",
6 | "license": "MIT",
7 | "author": "Dewizz",
8 | "keywords": [
9 | "angular",
10 | "ngx",
11 | "ngx-toolkit",
12 | "universal",
13 | "annotation",
14 | "decorator",
15 | "cache",
16 | "cache-service"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/dewizz/ngx-toolkit"
21 | },
22 | "bugs": {
23 | "url": "https://github.com/dewizz/ngx-toolkit/issues"
24 | },
25 | "dependencies": {
26 | "tslib": "^2.0.0"
27 | },
28 | "peerDependencies": {
29 | "@angular/common": "^13.2.0",
30 | "@angular/core": "^13.2.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/cache.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import {CacheDefaults, CacheKey, CachePut, CacheRemove, CacheRemoveAll, CacheResult, CacheValue} from './cache.decorator';
2 | import {initCacheManager} from './cache.instance';
3 | import {MemoryCache} from './impl/memory-cache';
4 | import {StorageCache} from './impl/storage-cache';
5 | import {SimpleCacheManager} from './simple-cache.manager';
6 |
7 | @CacheDefaults('cacheBean')
8 | class CacheBean {
9 |
10 | @CachePut()
11 | put(@CacheKey() id: number, @CacheValue() value: any): void {
12 | }
13 |
14 | @CacheResult()
15 | get(id: number): any {
16 | return null;
17 | }
18 |
19 | @CacheRemove()
20 | clear(id: number): void {
21 | }
22 |
23 | @CacheRemoveAll()
24 | clearAll(): void {
25 | }
26 | }
27 |
28 | describe('CacheDecorator', () => {
29 |
30 | beforeEach(() => {
31 |
32 | });
33 |
34 | it('should throw an error if no cacheManager declared', () => {
35 | const bean: CacheBean = new CacheBean();
36 |
37 | initCacheManager(null);
38 | expect(function() {
39 | bean.put(1, 2);
40 | }).toThrowError();
41 | });
42 |
43 | it('should throw an error if no cache declared', () => {
44 | const bean: CacheBean = new CacheBean();
45 |
46 | initCacheManager(new SimpleCacheManager());
47 | expect(function() {
48 | bean.put(1, 2);
49 | }).toThrowError();
50 | });
51 |
52 | it('should not has an injector', () => {
53 | const bean: CacheBean = new CacheBean();
54 |
55 | [new MemoryCache('cacheBean'), new StorageCache('cacheBean', localStorage)].forEach(cache => {
56 | const cacheManager: SimpleCacheManager = new SimpleCacheManager();
57 | cacheManager.addCache(cache);
58 | initCacheManager(cacheManager);
59 |
60 |
61 | bean.put(1, 'put');
62 | bean.put(2, 'put2');
63 |
64 | expect(bean.get(2)).toBe('put2');
65 |
66 | expect(bean.get(3)).toBeNull();
67 |
68 | bean.clear(2);
69 | expect(bean.get(2)).toBeNull();
70 |
71 | expect(bean.get(1)).toBe('put');
72 |
73 | bean.clearAll();
74 | expect(bean.get(1)).toBeNull();
75 | });
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/cache.decorator.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata/Reflect';
2 | import {getCacheManager} from './cache.instance';
3 | import {Cache} from './cache.model';
4 |
5 | export const METADATA_KEY_CACHE_DEFAULTS = '_cache_defaults';
6 | export const METADATA_KEY_CACHE_KEYS = '_cache_keys';
7 | export const METADATA_KEY_CACHE_VALUE = '_cache_value';
8 |
9 | export interface CacheParams {
10 | cacheName?: string;
11 | }
12 |
13 | export interface CacheParamsInvoc extends CacheParams {
14 | afterInvocation?: boolean;
15 | }
16 |
17 | /**
18 | * Allows the configuration of defaults for `CacheResult`, `CachePut`, `CacheRemove`, and `CacheRemoveAll` at the class level.
19 | * Without the method level annotations this annotation has no effect.
20 | *
21 | * @param cacheName
22 | */
23 | export function CacheDefaults(cacheName: string): ClassDecorator {
24 | return (target: Function): void => {
25 | Reflect.defineMetadata(METADATA_KEY_CACHE_DEFAULTS, cacheName, target);
26 | };
27 | }
28 |
29 | /**
30 | * When a method annotated with `CacheResult` is invoked a cache key will be generated
31 | * and *Cache.get(key)* is called before the annotated method actually executes.
32 | * If a value is found in the cache it is returned and the annotated method is never actually executed.
33 | * If no value is found the annotated method is invoked and the returned value is stored in the cache with the generated key.
34 | *
35 | * @param params (Optional) {cacheName?: string}
36 | */
37 | export function CacheResult(params?: CacheParams): MethodDecorator {
38 | params = getDefaultParams(params);
39 |
40 | return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor => {
41 | const originalMethod = descriptor.value;
42 | descriptor.value = function(...args: any[]) {
43 | const cache: Cache = getCache(target, params);
44 | const cacheKey: string = getCacheKey(target, propertyKey, args);
45 |
46 | // Find cache value
47 | let result: any = cache.get(cacheKey);
48 |
49 | // Call function & save function if no cache value
50 | if (result === undefined) {
51 | // Call function & save result
52 | result = originalMethod.apply(this, args);
53 | cache.put(cacheKey, result);
54 | }
55 |
56 | return result;
57 | };
58 | return descriptor;
59 | };
60 | }
61 |
62 | /**
63 | * Marks a method argument as part of the cache key.
64 | * If no arguments are marked all arguments are used.
65 | * The exception is for a method annotated with `CachePut` where the `CacheValue` parameter is never included in the key.
66 | */
67 | export function CacheKey(): ParameterDecorator {
68 | return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
69 | const indices = Reflect.getMetadata(`${METADATA_KEY_CACHE_KEYS}_${propertyKey.toString()}`, target, propertyKey) || [];
70 | indices.push(parameterIndex);
71 | Reflect.defineMetadata(`${METADATA_KEY_CACHE_KEYS}_${propertyKey.toString()}`, indices, target, propertyKey);
72 | };
73 | }
74 |
75 | /**
76 | * When a method annotated with `CachePut` is invoked a cache key will be generated
77 | * and *Cache.put(key, value)* will be invoked on the specified cache storing the value marked with `CacheValue`.
78 | *
79 | * @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
80 | */
81 | export function CachePut(params?: CacheParamsInvoc): MethodDecorator {
82 | params = getDefaultParams(params);
83 |
84 | return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor => {
85 | const originalMethod = descriptor.value;
86 | descriptor.value = function(...args: any[]) {
87 | const cache: Cache = getCache(target, params);
88 | const indexValue: number = Reflect.getMetadata(`${METADATA_KEY_CACHE_VALUE}_${propertyKey.toString()}`, target, propertyKey);
89 | const cacheKey: string = getCacheKey(target, propertyKey, args, indexValue);
90 |
91 | if (!params.afterInvocation && indexValue && indexValue >= 0 && indexValue < args.length) {
92 | cache.put(cacheKey, args[indexValue]);
93 | }
94 |
95 | const result = originalMethod.apply(this, args);
96 |
97 | if (params.afterInvocation && indexValue && indexValue >= 0 && indexValue < args.length) {
98 | cache.put(cacheKey, args[indexValue]);
99 | }
100 |
101 | return result;
102 | };
103 | return descriptor;
104 | };
105 | }
106 |
107 | /**
108 | * Marks the parameter to be cached for a method annotated with `CachePut`.
109 | */
110 | export function CacheValue(): ParameterDecorator {
111 | return (target: Object, propertyKey: string | symbol, parameterIndex: number) => {
112 | Reflect.defineMetadata(`${METADATA_KEY_CACHE_VALUE}_${propertyKey.toString()}`, parameterIndex, target, propertyKey);
113 | };
114 | }
115 |
116 | /**
117 | * When a method annotated with `CacheRemove` is invoked a cache key will be generated
118 | * and *Cache.remove(key)* will be invoked on the specified cache.
119 | * The default behavior is to call *Cache.evict(key)* after the annotated method is invoked,
120 | * this behavior can be changed by setting *`afterInvocation`* to false in which case *Cache.evict(key)*
121 | * will be called before the annotated method is invoked.
122 | *
123 | * @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
124 | */
125 | export function CacheRemove(params?: CacheParamsInvoc): MethodDecorator {
126 | params = getDefaultParams(params);
127 |
128 | return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor => {
129 | const originalMethod = descriptor.value;
130 | descriptor.value = function(...args: any[]) {
131 | const cache: Cache = getCache(target, params);
132 | const cacheKey: string = getCacheKey(target, propertyKey, args);
133 |
134 | if (!params.afterInvocation) {
135 | cache.evict(cacheKey);
136 | }
137 |
138 | const result: any = originalMethod.apply(this, args);
139 |
140 |
141 | if (params.afterInvocation) {
142 | cache.evict(cacheKey);
143 | }
144 |
145 | return result;
146 | };
147 | return descriptor;
148 | };
149 | }
150 |
151 | /**
152 | * When a method annotated with `CacheRemoveAll` is invoked all elements in the specified cache will be removed via the *Cache.clear()* method.
153 | * The default behavior is to call *Cache.clear()* after the annotated method is invoked,
154 | * this behavior can be changed by setting *`afterInvocation`* to false in which case *Cache.clear()* will be called before the annotated method is invoked.
155 | *
156 | * @param params (Optional) {cacheName?: string, afterInvocation: boolean = true}
157 | */
158 | export function CacheRemoveAll(params?: CacheParamsInvoc): MethodDecorator {
159 | params = getDefaultParams(params);
160 |
161 | return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor => {
162 | const originalMethod = descriptor.value;
163 | descriptor.value = function(...args: any[]) {
164 | const cache: Cache = getCache(target, params);
165 | if (!params.afterInvocation) {
166 | cache.clear();
167 | }
168 |
169 | const result: any = originalMethod.apply(this, args);
170 |
171 | if (params.afterInvocation) {
172 | cache.clear();
173 | }
174 |
175 | return result;
176 | };
177 | return descriptor;
178 | };
179 | }
180 |
181 | function getDefaultParams(cacheParams: CacheParams): { afterInvocation: boolean } & CacheParams {
182 | return Object.assign({
183 | afterInvocation: true
184 | }, cacheParams || {});
185 | }
186 |
187 | function getCache(target: Object, params: CacheParams): Cache {
188 | if (!params.cacheName) {
189 | params.cacheName = Reflect.getMetadata(METADATA_KEY_CACHE_DEFAULTS, target.constructor) || '';
190 | }
191 |
192 | const cache: Cache = getCacheManager().getCache(params.cacheName);
193 | if (!cache) {
194 | throw new Error(`Cache '${params.cacheName}' not found for ${target.constructor.name}`);
195 | }
196 | return cache;
197 | }
198 |
199 | function getCacheKey(target: Object, propertyKey: string | symbol, args: any[], cacheValueIndex = -1): string {
200 | if (!args) {
201 | args = [];
202 | }
203 |
204 | const indices: number[] = Reflect.getMetadata(`${METADATA_KEY_CACHE_KEYS}_${propertyKey.toString()}`, target, propertyKey);
205 | if (indices) {
206 | args = args.filter((value: any, index: number) => indices.indexOf(index) !== -1 && cacheValueIndex !== index);
207 | } else if (cacheValueIndex !== -1) {
208 | args = args.filter((value: any, index: number) => cacheValueIndex !== index);
209 | }
210 |
211 | if (args.length === 0) {
212 | throw new Error(`Couldn't generate key without params for '${propertyKey.toString()}' method of ${target.constructor.name}`);
213 | }
214 |
215 | return args.map(a => (JSON.stringify(a) || a.toString())).join('|');
216 | }
217 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/cache.instance.ts:
--------------------------------------------------------------------------------
1 | import {CacheManager} from './cache.manager';
2 |
3 | const CACHE_INSTANCE = {
4 | manager: undefined
5 | };
6 |
7 |
8 | export function initCacheManager(cacheManager: CacheManager) {
9 | CACHE_INSTANCE.manager = cacheManager;
10 | }
11 |
12 | export function getCacheManager(): CacheManager {
13 | if (!CACHE_INSTANCE.manager) {
14 | throw new Error('No cache found, `initCacheManager` before');
15 | }
16 | return CACHE_INSTANCE.manager;
17 | }
18 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/cache.manager.ts:
--------------------------------------------------------------------------------
1 | import {Cache} from './cache.model';
2 |
3 | export interface CacheManager {
4 | /**
5 | * Return the cache associated with the given name.
6 | */
7 | getCache(name: string): Cache;
8 |
9 | /**
10 | * Return a collection of the cache names known by this manager.
11 | */
12 | getCacheNames(): string[];
13 | }
14 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/cache.model.ts:
--------------------------------------------------------------------------------
1 | export interface Cache {
2 |
3 | /**
4 | * Return the cache name.
5 | */
6 | readonly name: string;
7 |
8 | /**
9 | * Return the value to which this cache maps the specified key
10 | */
11 | get(key: string): T;
12 |
13 | /**
14 | * Associate the specified value with the specified key in this cache.
15 | * If the cache previously contained a mapping for this key, the old
16 | * value is replaced by the specified value.
17 | */
18 | put(key: string, value: T): void;
19 |
20 | /**
21 | * Evict the mapping for this key from this cache if it is present.
22 | */
23 | evict(key: string): void;
24 |
25 | /**
26 | * Remove all mappings from the cache.
27 | */
28 | clear(): void;
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/cache.module.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed} from '@angular/core/testing';
2 | import {getCacheManager, initCacheManager} from './cache.instance';
3 | import {CacheModule} from './cache.module';
4 | import {MemoryCache} from './impl/memory-cache';
5 |
6 | describe('CacheModule', () => {
7 | it('should work', () => {
8 | expect(new CacheModule()).toBeDefined();
9 | });
10 |
11 | it('should configure cacheManager', () => {
12 | initCacheManager(null);
13 |
14 | TestBed.configureTestingModule({
15 | imports: [CacheModule.forRoot([
16 | new MemoryCache('simpleCache')
17 | ])]
18 | });
19 |
20 | expect(getCacheManager()).toBeDefined();
21 | expect(getCacheManager().getCacheNames().length).toBe(1);
22 | expect(getCacheManager().getCacheNames()[0]).toBe('simpleCache');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/cache.module.ts:
--------------------------------------------------------------------------------
1 | import {CommonModule} from '@angular/common';
2 | import {ModuleWithProviders, NgModule} from '@angular/core';
3 | import {initCacheManager} from './cache.instance';
4 | import {Cache} from './cache.model';
5 | import {SimpleCacheManager} from './simple-cache.manager';
6 |
7 | @NgModule({
8 | imports: [CommonModule]
9 | })
10 | export class CacheModule {
11 | /**
12 | * In root module to provide caches
13 | */
14 | static forRoot(caches: Cache[]): ModuleWithProviders {
15 | initCacheManager(new SimpleCacheManager(caches));
16 |
17 | return {
18 | ngModule: CacheModule
19 | };
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/impl/memory-cache.ts:
--------------------------------------------------------------------------------
1 | import {Cache} from '../cache.model';
2 |
3 | export class MemoryCache implements Cache {
4 |
5 | readonly name: string;
6 | private cache: Map = new Map();
7 |
8 | constructor(name: string) {
9 | this.name = name;
10 | }
11 |
12 | clear(): void {
13 | this.cache.clear();
14 | }
15 |
16 | evict(key: string): void {
17 | this.cache.delete(key);
18 | }
19 |
20 | get(key: string): T {
21 | return this.cache.get(key);
22 | }
23 |
24 | put(key: string, value: T): void {
25 | this.cache.set(key, value);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/impl/no-op-cache.ts:
--------------------------------------------------------------------------------
1 | import {Cache} from '../cache.model';
2 |
3 | export class NoOpCache implements Cache {
4 |
5 | readonly name: string;
6 |
7 | constructor(name: string) {
8 | this.name = name;
9 | }
10 |
11 | clear(): void {
12 | }
13 |
14 | evict(key: string): void {
15 | }
16 |
17 | get(key: string): T {
18 | return undefined;
19 | }
20 |
21 | put(key: string, value: T): void {
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/impl/storage-cache.ts:
--------------------------------------------------------------------------------
1 | import {Cache} from '../cache.model';
2 |
3 | export class StorageCache implements Cache {
4 |
5 | readonly name: string;
6 | private storage: Storage;
7 |
8 | constructor(name: string, storage: Storage) {
9 | this.name = name;
10 | this.storage = storage;
11 | }
12 |
13 | clear(): void {
14 | this.storage.clear();
15 | }
16 |
17 | evict(key: string): void {
18 | this.storage.removeItem(key);
19 | }
20 |
21 | get(key: string): T {
22 | const value: string = this.storage.getItem(key);
23 | if (!value) {
24 | return undefined;
25 | }
26 |
27 | return JSON.parse(value) || null;
28 | }
29 |
30 | put(key: string, value: T): void {
31 | this.storage.setItem(key, JSON.stringify(value));
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/projects/cache/src/lib/simple-cache.manager.ts:
--------------------------------------------------------------------------------
1 | import {CacheManager} from './cache.manager';
2 | import {Cache} from './cache.model';
3 |
4 | export class SimpleCacheManager implements CacheManager {
5 | private cacheMap: Map;
6 |
7 | constructor(caches?: Cache[]) {
8 | this.setCaches(caches);
9 | }
10 |
11 | getCacheNames(): string[] {
12 | return Array.from(this.cacheMap.keys());
13 | }
14 |
15 | getCache(name: string): Cache {
16 | return this.cacheMap.get(name) || null;
17 | }
18 |
19 | addCache(cache: Cache): void {
20 | if (!cache) {
21 | throw new Error('Cache is undefined');
22 | }
23 | this.cacheMap.set(cache.name, cache);
24 | }
25 |
26 | setCaches(caches: Cache[]): void {
27 | this.cacheMap = new Map();
28 | if (caches) {
29 | caches.forEach(cache => this.addCache(cache));
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/projects/cache/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/cache/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of cache
3 | */
4 |
5 | export {CacheModule} from './lib/cache.module';
6 | export {
7 | CacheDefaults, CacheResult, CacheKey, CacheValue, CachePut, CacheRemove, CacheRemoveAll
8 | }from './lib/cache.decorator';
9 | export {CacheManager} from './lib/cache.manager';
10 | export {SimpleCacheManager} from './lib/simple-cache.manager';
11 | export {getCacheManager, initCacheManager} from './lib/cache.instance';
12 | export {Cache} from './lib/cache.model';
13 | export {NoOpCache} from './lib/impl/no-op-cache';
14 | export {StorageCache} from './lib/impl/storage-cache';
15 | export {MemoryCache} from './lib/impl/memory-cache';
16 |
--------------------------------------------------------------------------------
/projects/cache/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().map(context);
27 |
--------------------------------------------------------------------------------
/projects/cache/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "skipTemplateCodegen": true,
16 | "strictMetadataEmit": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": [
20 | "src/test.ts",
21 | "**/*.spec.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/projects/cache/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/cache/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/cookie/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json",
3 | "ignorePatterns": [
4 | "!**/*",
5 | "**/*.spec.ts"
6 | ],
7 | "overrides": [
8 | {
9 | "files": [
10 | "*.ts"
11 | ],
12 | "parserOptions": {
13 | "project": [
14 | "projects/cookie/tsconfig.lib.json",
15 | "projects/cookie/tsconfig.spec.json"
16 | ],
17 | "createDefaultProgram": true
18 | },
19 | "rules": {
20 | "@angular-eslint/directive-selector": [
21 | "error",
22 | {
23 | "type": "attribute",
24 | "prefix": "lib",
25 | "style": "camelCase"
26 | }
27 | ],
28 | "@angular-eslint/component-selector": [
29 | "error",
30 | {
31 | "type": "element",
32 | "prefix": "lib",
33 | "style": "kebab-case"
34 | }
35 | ],
36 | "@typescript-eslint/no-unsafe-member-access": "off",
37 | "@typescript-eslint/no-unsafe-assignment": "off",
38 | "@typescript-eslint/no-unsafe-call": "off",
39 | "@typescript-eslint/no-unsafe-return": "off",
40 | "@typescript-eslint/no-unsafe-argument": "off",
41 | "@typescript-eslint/no-empty-function": "off",
42 | "@typescript-eslint/no-unused-vars": "off",
43 | "@typescript-eslint/no-explicit-any": "off",
44 | "@typescript-eslint/restrict-template-expressions": "off",
45 | "@typescript-eslint/ban-types": "off"
46 | }
47 | },
48 | {
49 | "files": [
50 | "*.html"
51 | ],
52 | "rules": {}
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/projects/cookie/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/@ngx-toolkit/cookie)
2 | [](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
3 | [](https://travis-ci.org/dewizz/ngx-toolkit)
4 | [](https://coveralls.io/github/dewizz/ngx-toolkit?branch=master)
5 | [](https://gitter.im/ngx-toolkit/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | # @ngx-toolkit/cookie
8 |
9 | > Angular CookieService implementation for Browser & Server platforms.
10 |
11 | # Table of contents:
12 | * [Installation](#installation)
13 | * [Usage with...](#usage)
14 | * [annotation](#usage-annotation)
15 | * [service](#usage-service)
16 | * [API](#api)
17 | * [Cookie](#cookie)
18 | * [CookieService](#cookieservice)
19 | * [CookieOptions](#cookieoptions)
20 | * [Universal Usage](#universal-usage)
21 | * [License](#license)
22 |
23 | ---
24 |
25 | # Installation
26 |
27 | Install the npm package.
28 |
29 | ```bash
30 | # To get the latest stable version and update package.json file:
31 | npm install @ngx-toolkit/cookie --save
32 | # or
33 | yarn add @ngx-toolkit/cookie
34 | ```
35 |
36 | Registered `CookieModule` in the root Module of your application with `forRoot(cookieOptions?: CookieOptions)` static method.
37 | *CookieOptions is optional, by default the path is set to '/' and cookies never expired.*
38 |
39 | ```typescript
40 | import { NgModule } from '@angular/core';
41 | import { BrowserModule } from '@angular/platform-browser';
42 | import { CookieModule } from '@ngx-toolkit/cookie';
43 |
44 | import { AppComponent } from './app.component';
45 |
46 | @NgModule({
47 | imports: [ BrowserModule, CookieModule.forRoot() ],
48 | declarations: [ AppComponent ],
49 | bootstrap: [ AppComponent ]
50 | })
51 | export class AppModule { }
52 | ```
53 |
54 | # Usage
55 |
56 | ## Annotation
57 | ```typescript
58 | import { Component, OnInit } from '@angular/core';
59 | import { Cookie } from '@ngx-toolkit/cookie';
60 |
61 | @Component({
62 | selector: 'app-root',
63 | templateUrl: './app.component.html',
64 | styleUrls: ['./app.component.css']
65 | })
66 | export class AppComponent implements OnInit {
67 |
68 | @Cookie('accept-cookie')
69 | acceptedCookie: boolean;
70 |
71 | acceptCookie() {
72 | this.acceptedCookie = true;
73 | }
74 | }
75 | ```
76 |
77 | ## Service
78 | ```typescript
79 | import { Component, OnInit } from '@angular/core';
80 | import { CookieService } from '@ngx-toolkit/cookie';
81 |
82 | @Component({
83 | selector: 'app-root',
84 | templateUrl: './app.component.html',
85 | styleUrls: ['./app.component.css']
86 | })
87 | export class AppComponent implements OnInit {
88 |
89 | acceptedCookie: boolean;
90 |
91 | constructor(private cookieService: CookieService) {}
92 |
93 | ngOnInit() {
94 | this.acceptedCookie = this.cookieService.getItem('accept-cookie') === 'true';
95 | }
96 |
97 | acceptCookie() {
98 | this.cookieService.setItem('accept-cookie', 'true');
99 | this.acceptedCookie = true;
100 | }
101 | }
102 | ```
103 |
104 | # API
105 |
106 | ## Cookie
107 |
108 | Cookie annotation:
109 | ```typescript
110 | /**
111 | * Get / Set cookie
112 | * @param {string} name (default is the property name)
113 | * @param {CookieOptions} options (to override default options)
114 | */
115 | @Cookie(name?: string, options?: CookieOptions)
116 | property: any;
117 | ```
118 |
119 | ## CookieService
120 |
121 | The `CookieSerivce` API:
122 |
123 | ```typescript
124 | interface CookieService {
125 |
126 | /**
127 | * Get all cookies in object format.
128 | *
129 | * @returns {{[p: string]: string}}
130 | */
131 | getAll(): { [key: string]: string };
132 |
133 | /**
134 | * Read a cookie. If the cookie doesn't exist a null value will be returned.
135 | *
136 | * @param key The name of the cookie to read (string).
137 | * @param {string} key
138 | * @returns {string | null}
139 | */
140 | getItem(key: string): string | null;
141 |
142 | /**
143 | * Check whether a cookie exists in the current position.
144 | *
145 | * @param key The name of the cookie to test (string).
146 | * @returns {boolean}
147 | */
148 | hasItem(key: string): boolean;
149 |
150 | /**
151 | * Add that cookie to the storage, or update that cookie's value if it already exists.
152 | *
153 | * @param {string} The name of the cookie you want to create/update.
154 | * @param {string} the value you want to give the cookie you are creating/updating.
155 | * @param {CookieOptions} Override default options
156 | */
157 | setItem(key: string, data?: string, options?: CookieOptions): void;
158 |
159 | /**
160 | * Delete a cookie.
161 | *
162 | * @param {string} The name of the cookie to remove
163 | * @param {CookieOptions} Override default options
164 | */
165 | removeItem(key: string, options?: CookieOptions): void;
166 |
167 | /**
168 | * Remove all cookie.
169 | *
170 | * @param {CookieOptions} Override default options
171 | */
172 | clear(options?: CookieOptions): void;
173 |
174 | /**
175 | * Return all cookie names.
176 | *
177 | * @returns {any} Cookie names
178 | */
179 | keys(): string[];
180 |
181 | /**
182 | * Returns an integer representing the number of cookie items.
183 | *
184 | * @returns {number}
185 | */
186 | get length(): number;
187 |
188 | /**
189 | * Return the cookie name at a index.
190 | *
191 | * @param {number} The index position.
192 | * @returns {any} The cookie name or null
193 | */
194 | key(index: number): string | null;
195 | }
196 | ```
197 |
198 | ## CookieOptions
199 |
200 | ```typescript
201 | class CookieOptions {
202 | /**
203 | * The path from where the cookie will be readable.
204 | */
205 | path?: string;
206 | /**
207 | * The domain from where the cookie will be readable.
208 | */
209 | domain?: string;
210 | /**
211 | * The expiration :
212 | * - in seconds for the max-age
213 | * - Infinity for a never expiration
214 | * - Date in GMTString format
215 | * - Date object
216 | *
217 | * If not specified the cookie will expire at the end of the session.
218 | */
219 | expires?: Date | string | number;
220 | /**
221 | * If true, the cookie will be transmitted only over secure protocol as https.
222 | */
223 | secure?: boolean;
224 | }
225 | ```
226 |
227 | # Universal Usage
228 |
229 | You just have to provide another `CookieFactory` implemantation.
230 |
231 | `ServerCookieFactory` implementation is available (works with express only).
232 | Sample with [@nguniversal/express-engine](https://github.com/angular/universal/tree/master/modules/express-engine):
233 |
234 | ```typescript
235 | import { NgModule } from '@angular/core';
236 | import { ServerModule } from '@angular/platform-server';
237 | import { REQUEST, RESPONSE } from '@nguniversal/express-engine';
238 | import { CookieFactory, ServerCookieFactory } from '@ngx-toolkit/cookie';
239 |
240 | import { AppModule } from './app.module';
241 | import { AppComponent } from './app.component';
242 |
243 | export function newCookieFactory(req: any, res: any) {
244 | return new ServerCookieFactory(req, res);
245 | }
246 |
247 | @NgModule({
248 | imports: [ AppModule, ServerModule ],
249 | bootstrap: [ AppComponent ],
250 | providers: [
251 | {
252 | provide: CookieFactory,
253 | useFactory: newCookieFactory,
254 | deps: [REQUEST, RESPONSE]
255 | }
256 | ],
257 | })
258 | export class AppServerModule { }
259 | ```
260 |
261 | ----
262 |
263 | # License
264 | © 2018 Dewizz
265 |
266 | [MIT](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
267 |
--------------------------------------------------------------------------------
/projects/cookie/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, '../../coverage/cookie'),
29 | subdir: '.',
30 | reporters: [
31 | {type: 'html'},
32 | {type: 'lcovonly'},
33 | {type: 'text-summary'}
34 | ]
35 | },
36 | reporters: ['progress', 'kjhtml'],
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessCI'],
42 | customLaunchers: {
43 | ChromeHeadlessCI: {
44 | base: 'ChromeHeadless',
45 | flags: ['--no-sandbox']
46 | }
47 | },
48 | singleRun: false,
49 | restartOnFileChange: true
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/projects/cookie/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/cookie",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/cookie/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-toolkit/cookie",
3 | "version": "0.0.0-PLACEHOLDER",
4 | "description": "Angular cookie with Universal support",
5 | "homepage": "https://github.com/dewizz/ngx-toolkit",
6 | "license": "MIT",
7 | "author": "Dewizz",
8 | "keywords": [
9 | "angular",
10 | "ngx",
11 | "ngx-toolkit",
12 | "universal",
13 | "annotation",
14 | "decorator",
15 | "cookie",
16 | "cookie-service"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/dewizz/ngx-toolkit"
21 | },
22 | "bugs": {
23 | "url": "https://github.com/dewizz/ngx-toolkit/issues"
24 | },
25 | "dependencies": {
26 | "tslib": "^2.0.0"
27 | },
28 | "peerDependencies": {
29 | "@angular/common": "^13.2.0",
30 | "@angular/core": "^13.2.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/browser/browser-cookie.factory.ts:
--------------------------------------------------------------------------------
1 | import {DOCUMENT} from '@angular/common';
2 | import {Inject, Injectable} from '@angular/core';
3 | import {CookieOptions, cookiesStrToObj} from '../cookie.model';
4 | import {CookieFactory} from '../cookie.service';
5 |
6 | @Injectable()
7 | export class BrowserCookieFactory implements CookieFactory {
8 | private lastCookies = '';
9 | private cookies: { [key in string]: string } = {};
10 |
11 | constructor(@Inject(DOCUMENT) private document: any) {
12 | }
13 |
14 | getAll(): { [p: string]: string } {
15 | const cookiesStr: string = this.document.cookie;
16 | if (this.lastCookies !== cookiesStr) {
17 | this.lastCookies = cookiesStr;
18 | this.cookies = cookiesStrToObj(cookiesStr);
19 | }
20 | return this.cookies;
21 | }
22 |
23 | save(key: string, data: string, options: CookieOptions): void {
24 | this.document.cookie = `${encodeURIComponent(key)}=${data ? encodeURIComponent(data) : ''}${
25 | options.expires ? `; expires=${( options.expires).toUTCString()}` : ''
26 | }${options.domain ? `; domain=${options.domain}` : ''}${options.path ? `; path=${options.path}` : ''}${
27 | options.secure ? '; secure' : ''
28 | }`;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/browser/index.ts:
--------------------------------------------------------------------------------
1 | export {BrowserCookieFactory} from './browser-cookie.factory';
2 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import {BrowserCookieFactory} from './browser';
2 | import {Cookie, COOKIE_DECORATOR_DATA} from './cookie.decorator';
3 | import {CookieService} from './cookie.service';
4 |
5 | describe('CookieDecorator', () => {
6 | beforeEach(() => {
7 | const service: CookieService = new CookieService(null, new BrowserCookieFactory(document));
8 | service.clear();
9 |
10 | COOKIE_DECORATOR_DATA.cookieService = service;
11 | });
12 |
13 | it('should not has an injector', () => {
14 | class Test {
15 | @Cookie() test: string;
16 | }
17 |
18 | const test: Test = new Test();
19 | expect(test.test).toBeNull();
20 | test.test = 'data';
21 | expect(test.test).toEqual('data');
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.decorator.ts:
--------------------------------------------------------------------------------
1 | import {CookieOptions} from './cookie.model';
2 | import {CookieService} from './cookie.service';
3 |
4 | export interface CookieDecoratorData {
5 | cookieService?: CookieService;
6 | }
7 |
8 | export const COOKIE_DECORATOR_DATA: CookieDecoratorData = {};
9 |
10 | /**
11 | * Property Decorator to Get / Set cookie
12 | */
13 | export function Cookie(name?: string, options?: CookieOptions): PropertyDecorator {
14 | return function(target: any, key: string) {
15 | let _value: any;
16 |
17 | if (delete target[key]) {
18 | const cookieName: string = name || key;
19 |
20 | Object.defineProperty(target, key, {
21 | get: function() {
22 | if (COOKIE_DECORATOR_DATA.cookieService) {
23 | const cookieValue: string = COOKIE_DECORATOR_DATA.cookieService.getItem(cookieName);
24 | try {
25 | return JSON.parse(cookieValue);
26 | } catch (e) {
27 | return cookieValue;
28 | }
29 | } else {
30 | return _value;
31 | }
32 | },
33 | set: function(value) {
34 | if (COOKIE_DECORATOR_DATA.cookieService) {
35 | COOKIE_DECORATOR_DATA.cookieService.setItem(
36 | cookieName,
37 | typeof value === 'string' ? value : JSON.stringify(value),
38 | options
39 | );
40 | } else {
41 | _value = value;
42 | }
43 | },
44 | enumerable: true,
45 | configurable: true
46 | });
47 | }
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.model.ts:
--------------------------------------------------------------------------------
1 | export interface CookieOptions {
2 | /**
3 | * The path from where the cookie will be readable.
4 | */
5 | path?: string;
6 | /**
7 | * The domain from where the cookie will be readable.
8 | */
9 | domain?: string;
10 | /**
11 | * The expiration :
12 | * - in seconds for the max-age
13 | * - Infinity for a never expiration
14 | * - Date in GMTString format
15 | * - Date object
16 | *
17 | * If not specified the cookie will expire at the end of the session.
18 | */
19 | expires?: Date | string | number;
20 | /**
21 | * If true, the cookie will be transmitted only over secure protocol as https.
22 | */
23 | secure?: boolean;
24 | }
25 |
26 | /**
27 | * Convert cookies string to object
28 | * @param cookiesStr
29 | */
30 | export function cookiesStrToObj(cookiesStr: string): { [key in string]: string } {
31 | const cookies: { [key in string]: string } = {};
32 | if (!cookiesStr) {
33 | return cookies;
34 | }
35 |
36 | cookiesStr.split('; ').forEach(cookie => {
37 | const cookieSplited: string[] = cookie.split('=');
38 | cookies[decodeURIComponent(cookieSplited[0])] = decodeURIComponent(cookieSplited[1]);
39 | });
40 | return cookies;
41 | }
42 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.module.spec.ts:
--------------------------------------------------------------------------------
1 | import {ApplicationInitStatus} from '@angular/core';
2 | import {TestBed} from '@angular/core/testing';
3 | import {COOKIE_DECORATOR_DATA} from './cookie.decorator';
4 | import {CookieModule} from './cookie.module';
5 |
6 | describe('CookieModule', () => {
7 | it('should work', () => {
8 | expect(new CookieModule()).toBeDefined();
9 | });
10 |
11 | it('should configure COOKIE_DECORATOR_DATA', async (done: DoneFn) => {
12 | await TestBed.configureTestingModule({
13 | imports: [CookieModule.forRoot()]
14 | })
15 | // https://github.com/angular/angular/issues/24218
16 | .get(ApplicationInitStatus).donePromise;
17 |
18 | expect(COOKIE_DECORATOR_DATA.cookieService).toBeDefined();
19 | done();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.module.ts:
--------------------------------------------------------------------------------
1 | import {DOCUMENT} from '@angular/common';
2 | import {APP_INITIALIZER, ModuleWithProviders, NgModule} from '@angular/core';
3 | import {BrowserCookieFactory} from './browser/index';
4 | import {COOKIE_DECORATOR_DATA} from './cookie.decorator';
5 | import {CookieOptions} from './cookie.model';
6 | import {CookieFactory, CookieService} from './cookie.service';
7 | import {COOKIE_OPTIONS} from './cookie.token';
8 |
9 | export function setupCookieDecorator(cookieService: CookieService) {
10 | COOKIE_DECORATOR_DATA.cookieService = cookieService;
11 | return () => null;
12 | }
13 |
14 | export function newCookieFactory(document: any) {
15 | return new BrowserCookieFactory(document);
16 | }
17 |
18 | @NgModule()
19 | export class CookieModule {
20 | /**
21 | * In root module to provide the CookieService & CookieOptions
22 | */
23 | static forRoot(cookieOptions?: CookieOptions): ModuleWithProviders {
24 | return {
25 | ngModule: CookieModule,
26 | providers: [
27 | {
28 | provide: COOKIE_OPTIONS,
29 | useValue: cookieOptions
30 | },
31 | {
32 | provide: CookieFactory,
33 | useFactory: newCookieFactory,
34 | deps: [DOCUMENT]
35 | },
36 | CookieService,
37 | {
38 | provide: APP_INITIALIZER,
39 | useFactory: setupCookieDecorator,
40 | deps: [CookieService],
41 | multi: true
42 | }
43 | ]
44 | };
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {BrowserCookieFactory} from './browser/browser-cookie.factory';
2 | import {CookieFactory, CookieService, DATE_MAX_EXPIRES} from './cookie.service';
3 |
4 | describe('CookieService', () => {
5 | let service: CookieService;
6 | let factory: CookieFactory;
7 |
8 | beforeEach(() => {
9 | factory = new BrowserCookieFactory(document);
10 | service = new CookieService(null, factory);
11 | service.clear();
12 | });
13 |
14 | it('#service is defined', () => {
15 | expect(service).toBeDefined();
16 | expect(service instanceof CookieService).toBeTruthy();
17 | });
18 |
19 | it('check unexist cookie', () => {
20 | const cookieKey = 'cookie_name';
21 |
22 | expect(service.hasItem(cookieKey)).toBeFalsy();
23 | expect(service.getItem(cookieKey)).toBeNull();
24 | expect(service.keys()).toEqual([]);
25 | expect(service.length).toBe(0);
26 | });
27 |
28 | it('#setItem should world :)', () => {
29 | const cookieKey = 'cookie_name';
30 | const cookieValue = 'cookie_value';
31 |
32 | service.setItem(null);
33 |
34 | service.setItem(cookieKey, cookieValue);
35 | expect(service.hasItem(cookieKey)).toBeTruthy();
36 | expect(service.getItem(cookieKey)).toBe(cookieValue);
37 |
38 | expect(service.keys()).toEqual([cookieKey]);
39 | expect(service.length).toBe(1);
40 | });
41 |
42 | it('check unexist cookie', () => {
43 | const cookieKey1 = 'cookie1';
44 | const cookieKey2 = 'cookie2';
45 |
46 | service.setItem(cookieKey1, cookieKey1);
47 | service.setItem(cookieKey2, cookieKey2);
48 | expect(service.keys()).toEqual([cookieKey1, cookieKey2]);
49 | expect(service.length).toBe(2);
50 | expect(service.key(1)).toEqual(cookieKey2);
51 | expect(service.key(2)).toEqual(null);
52 | });
53 |
54 | it('check default options', () => {
55 | spyOn(factory, 'save');
56 |
57 | service.setItem('test', 'test');
58 | expect(factory.save).toHaveBeenCalledWith('test', 'test', {path: '/', expires: DATE_MAX_EXPIRES});
59 |
60 | service.setItem('test', 'test', {path: '/toto'});
61 | expect(factory.save).toHaveBeenCalledWith('test', 'test', {path: '/toto', expires: DATE_MAX_EXPIRES});
62 |
63 | service.setItem('test', 'test', {path: '/toto', domain: 'dewizz.com'});
64 | expect(factory.save).toHaveBeenCalledWith('test', 'test', {
65 | path: '/toto',
66 | expires: DATE_MAX_EXPIRES,
67 | domain: 'dewizz.com'
68 | });
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.service.ts:
--------------------------------------------------------------------------------
1 | import {Inject, Injectable, Optional} from '@angular/core';
2 | import {CookieOptions} from './cookie.model';
3 | import {COOKIE_OPTIONS} from './cookie.token';
4 |
5 | export const DATE_MAX_EXPIRES: Date = new Date('Fri, 31 Dec 9999 23:59:59 GMT');
6 | export const DEFAULT_COOKIE_OPTIONS: CookieOptions = {
7 | path: '/',
8 | expires: Infinity
9 | };
10 |
11 | export abstract class CookieFactory {
12 | /**
13 | * Get all cookies in object format.
14 | */
15 | abstract getAll(): { [key: string]: string };
16 |
17 | /**
18 | * Implementation (create / update / delete)
19 | */
20 | abstract save(key: string, data: string, options: CookieOptions): void;
21 | }
22 |
23 | @Injectable()
24 | export class CookieService {
25 | private cookieOptions: CookieOptions;
26 |
27 | constructor(
28 | @Optional()
29 | @Inject(COOKIE_OPTIONS)
30 | cookieOptions: CookieOptions,
31 | private cookieFactory: CookieFactory
32 | ) {
33 | this.cookieOptions = cookieOptions || DEFAULT_COOKIE_OPTIONS;
34 | }
35 |
36 | /**
37 | * Returns an integer representing the number of cookie items.
38 | */
39 | get length(): number {
40 | return this.keys().length;
41 | }
42 |
43 | /**
44 | * Transform to expires Date
45 | */
46 | private static getExpiresDate(expires?: Date | string | number): Date {
47 | if (!expires) {
48 | return null;
49 | }
50 |
51 | switch (expires.constructor) {
52 | case Number:
53 | return expires === Infinity ? DATE_MAX_EXPIRES : new Date( expires * 1000 + Date.now());
54 | case String:
55 | return new Date( expires);
56 | default:
57 | return expires;
58 | }
59 | }
60 |
61 | /**
62 | * Get all cookies in object format.
63 | */
64 | getAll(): { [key: string]: string } {
65 | return this.cookieFactory.getAll();
66 | }
67 |
68 | /**
69 | * Read a cookie. If the cookie doesn't exist a null value will be returned.
70 | */
71 | getItem(key: string): string | null {
72 | return this.getAll()[key] || null;
73 | }
74 |
75 | /**
76 | * Check whether a cookie exists in the current position.
77 | */
78 | hasItem(key: string): boolean {
79 | return Object.prototype.hasOwnProperty.call(this.getAll(), key);
80 | }
81 |
82 | /**
83 | * Return all cookie names.
84 | */
85 | keys(): string[] {
86 | return Object.keys(this.getAll());
87 | }
88 |
89 | /**
90 | * Return the cookie name at a index.
91 | */
92 | key(index: number): string | null {
93 | return this.keys()[index] || null;
94 | }
95 |
96 | /**
97 | * Add that cookie to the storage, or update that cookie's value if it already exists.
98 | */
99 | setItem(key: string, data?: string, options?: CookieOptions): void {
100 | if (!key) {
101 | return;
102 | }
103 |
104 | // Merge options
105 | options = Object.assign({}, this.cookieOptions, options || {});
106 |
107 | // Remove data
108 | if (!data) {
109 | options.expires = 'Thu, 01 Jan 1970 00:00:00 GMT';
110 | }
111 |
112 | // Convert expires to Date
113 | if (options.expires) {
114 | options.expires = CookieService.getExpiresDate(options.expires);
115 | }
116 |
117 | this.cookieFactory.save(key, data, options);
118 | }
119 |
120 | /**
121 | * Delete a cookie.
122 | */
123 | removeItem(key: string, options?: CookieOptions): void {
124 | return this.setItem(key, null, options);
125 | }
126 |
127 | /**
128 | * Remove all cookie.
129 | */
130 | clear(options?: CookieOptions): void {
131 | this.keys().forEach(key => {
132 | this.removeItem(key, options);
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/cookie.token.ts:
--------------------------------------------------------------------------------
1 | import {InjectionToken} from '@angular/core';
2 | import {CookieOptions} from './cookie.model';
3 |
4 | export const COOKIE_OPTIONS = new InjectionToken('COOKIE_OPTIONS');
5 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/server/index.ts:
--------------------------------------------------------------------------------
1 | export {ServerCookieFactory} from './server-cookie.factory';
2 |
--------------------------------------------------------------------------------
/projects/cookie/src/lib/server/server-cookie.factory.ts:
--------------------------------------------------------------------------------
1 | import {CookieOptions, cookiesStrToObj} from '../cookie.model';
2 | import {CookieFactory} from '../cookie.service';
3 |
4 | export class ServerCookieFactory implements CookieFactory {
5 | private cookies: { [key in string]: string } = {};
6 |
7 | constructor(request: any, private response: any) {
8 | if (request && request.headers) {
9 | this.cookies = cookiesStrToObj(request.headers.cookie);
10 | }
11 | }
12 |
13 | getAll(): { [p: string]: string } {
14 | return this.cookies;
15 | }
16 |
17 | save(key: string, data: string, options: CookieOptions): void {
18 | if (!data) {
19 | delete this.cookies[key];
20 | this.response.clearCookie(key, options);
21 | } else {
22 | this.cookies[key] = data;
23 | this.response.cookie(key, data, options);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/projects/cookie/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/cookie/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of cookie
3 | */
4 |
5 | export {CookieOptions} from './lib/cookie.model';
6 | export {COOKIE_OPTIONS} from './lib/cookie.token';
7 | export {CookieFactory, CookieService} from './lib/cookie.service';
8 | export {CookieModule} from './lib/cookie.module';
9 | export {BrowserCookieFactory} from './lib/browser/index';
10 | export {ServerCookieFactory} from './lib/server/index';
11 | export {Cookie} from './lib/cookie.decorator';
12 |
--------------------------------------------------------------------------------
/projects/cookie/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().map(context);
27 |
--------------------------------------------------------------------------------
/projects/cookie/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "skipTemplateCodegen": true,
16 | "strictMetadataEmit": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": [
20 | "src/test.ts",
21 | "**/*.spec.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/projects/cookie/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/cookie/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/device/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json",
3 | "ignorePatterns": [
4 | "!**/*",
5 | "**/*.spec.ts"
6 | ],
7 | "overrides": [
8 | {
9 | "files": [
10 | "*.ts"
11 | ],
12 | "parserOptions": {
13 | "project": [
14 | "projects/device/tsconfig.lib.json",
15 | "projects/device/tsconfig.spec.json"
16 | ],
17 | "createDefaultProgram": true
18 | },
19 | "rules": {
20 | "@angular-eslint/directive-selector": [
21 | "error",
22 | {
23 | "type": "attribute",
24 | "prefix": "lib",
25 | "style": "camelCase"
26 | }
27 | ],
28 | "@angular-eslint/component-selector": [
29 | "error",
30 | {
31 | "type": "element",
32 | "prefix": "lib",
33 | "style": "kebab-case"
34 | }
35 | ],
36 | "@typescript-eslint/no-unsafe-member-access": "off",
37 | "@typescript-eslint/no-unsafe-assignment": "off",
38 | "@typescript-eslint/no-unsafe-call": "off",
39 | "@typescript-eslint/no-unsafe-return": "off",
40 | "@typescript-eslint/no-unsafe-argument": "off",
41 | "@typescript-eslint/no-empty-function": "off",
42 | "@typescript-eslint/no-unused-vars": "off",
43 | "@typescript-eslint/no-explicit-any": "off",
44 | "@typescript-eslint/restrict-template-expressions": "off",
45 | "@typescript-eslint/ban-types": "off"
46 | }
47 | },
48 | {
49 | "files": [
50 | "*.html"
51 | ],
52 | "rules": {}
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/projects/device/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/@ngx-toolkit/device)
2 | [](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
3 | [](https://travis-ci.org/dewizz/ngx-toolkit)
4 | [](https://coveralls.io/github/dewizz/ngx-toolkit?branch=master)
5 | [](https://gitter.im/ngx-toolkit/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | # @ngx-toolkit/device
8 | > Angular Device detection for Browser & Server platforms
9 |
10 | # Table of contents:
11 | * [Installation](#installation)
12 | * [Usage](#usage)
13 | * [API](#api)
14 | * [Device](#device)
15 | * [Universal Usage](#universal-usage)
16 | * [License](#license)
17 |
18 | ---
19 |
20 | # Installation
21 |
22 | Install the npm package.
23 |
24 | ```bash
25 | # To get the latest stable version and update package.json file:
26 | npm install @ngx-toolkit/device --save
27 | # or
28 | yarn add @ngx-toolkit/device
29 | ```
30 |
31 | Registered `DeviceModule` in the root Module of your application with `forRoot()` static method.
32 |
33 | ```typescript
34 | import { NgModule } from '@angular/core';
35 | import { BrowserModule } from '@angular/platform-browser';
36 | import { DeviceModule } from '@ngx-toolkit/device';
37 |
38 | import { AppComponent } from './app.component';
39 |
40 | @NgModule({
41 | imports: [ BrowserModule, DeviceModule.forRoot() ],
42 | declarations: [ AppComponent ],
43 | bootstrap: [ AppComponent ]
44 | })
45 | export class AppModule { }
46 | ```
47 |
48 | # Usage
49 |
50 | ```typescript
51 | import { Component, OnInit, Inject } from '@angular/core';
52 | import { DEVICE, Device } from '@ngx-toolkit/device';
53 |
54 | @Component({
55 | selector: 'app-root',
56 | templateUrl: './app.component.html',
57 | styleUrls: ['./app.component.css']
58 | })
59 | export class AppComponent implements OnInit {
60 |
61 | constructor(@Inject(DEVICE) device: Device) {
62 | console.log(device.isMobile());
63 | }
64 |
65 | }
66 | ```
67 |
68 | # API
69 |
70 | ## Device
71 |
72 | The `Device` API:
73 |
74 | ```typescript
75 | enum DeviceType {
76 | TABLET,
77 | MOBILE,
78 | NORMAL
79 | }
80 |
81 | enum DevicePlatform {
82 | ANDROID,
83 | IOS,
84 | UNKNOWN
85 | }
86 |
87 | interface Device {
88 | type: DeviceType;
89 | platform: DevicePlatform;
90 |
91 | isNormal(): boolean;
92 | isMobile(): boolean;
93 | isTablet(): boolean;
94 | }
95 | ```
96 |
97 | # Universal Usage
98 |
99 | You just have to provide an UserAgent.
100 |
101 | Sample with [@nguniversal/express-engine](https://github.com/angular/universal/tree/master/modules/express-engine):
102 |
103 | ```typescript
104 | import { NgModule } from '@angular/core';
105 | import { ServerModule } from '@angular/platform-server';
106 | import { REQUEST } from '@nguniversal/express-engine';
107 | import { USER_AGENT } from '@ngx-toolkit/device';
108 |
109 | import { AppModule } from './app.module';
110 | import { AppComponent } from './app.component';
111 |
112 | export function userAgentFactory(request: any) {
113 | return request.get('User-Agent');
114 | }
115 |
116 | @NgModule({
117 | imports: [ AppModule, ServerModule ],
118 | bootstrap: [ AppComponent ],
119 | providers: [
120 | {
121 | provide: USER_AGENT,
122 | useFactory: userAgentFactory,
123 | deps: [REQUEST]
124 | }
125 | ],
126 | })
127 | export class AppServerModule { }
128 | ```
129 |
130 | ----
131 |
132 | # License
133 | © 2018 Dewizz
134 |
135 | [MIT](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
136 |
--------------------------------------------------------------------------------
/projects/device/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, '../../coverage/device'),
29 | subdir: '.',
30 | reporters: [
31 | {type: 'html'},
32 | {type: 'lcovonly'},
33 | {type: 'text-summary'}
34 | ]
35 | },
36 | reporters: ['progress', 'kjhtml'],
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessCI'],
42 | customLaunchers: {
43 | ChromeHeadlessCI: {
44 | base: 'ChromeHeadless',
45 | flags: ['--no-sandbox']
46 | }
47 | },
48 | singleRun: false,
49 | restartOnFileChange: true
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/projects/device/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/device",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/device/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-toolkit/device",
3 | "version": "0.0.0-PLACEHOLDER",
4 | "description": "Angular device user-agent detection with Universal support",
5 | "homepage": "https://github.com/dewizz/ngx-toolkit",
6 | "license": "MIT",
7 | "author": "Dewizz",
8 | "keywords": [
9 | "angular",
10 | "ngx",
11 | "ngx-toolkit",
12 | "universal",
13 | "user-agent",
14 | "decorator",
15 | "device",
16 | "device-detection"
17 | ],
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/dewizz/ngx-toolkit"
21 | },
22 | "bugs": {
23 | "url": "https://github.com/dewizz/ngx-toolkit/issues"
24 | },
25 | "dependencies": {
26 | "tslib": "^2.0.0"
27 | },
28 | "peerDependencies": {
29 | "@angular/core": "^13.2.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/projects/device/src/lib/device.model.ts:
--------------------------------------------------------------------------------
1 | export enum DeviceType {
2 | TABLET = 0,
3 | MOBILE,
4 | NORMAL
5 | }
6 |
7 | export enum DevicePlatform {
8 | ANDROID = 0,
9 | IOS,
10 | UNKNOWN
11 | }
12 |
13 | export class Device {
14 | type: DeviceType;
15 | platform: DevicePlatform;
16 |
17 | constructor(type: DeviceType = DeviceType.NORMAL, platform: DevicePlatform = DevicePlatform.UNKNOWN) {
18 | this.type = type;
19 | this.platform = platform;
20 | }
21 |
22 | isNormal(): boolean {
23 | return this.type === DeviceType.NORMAL;
24 | }
25 |
26 | isMobile(): boolean {
27 | return this.type === DeviceType.MOBILE;
28 | }
29 |
30 | isTablet(): boolean {
31 | return this.type === DeviceType.TABLET;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/projects/device/src/lib/device.module.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed} from '@angular/core/testing';
2 | import {Device, DevicePlatform} from './device.model';
3 | import {DeviceModule} from './device.module';
4 | import {DEVICE, USER_AGENT} from './device.token';
5 |
6 | describe('DeviceModule', () => {
7 | it('should work', () => {
8 | expect(new DeviceModule()).toBeDefined();
9 | });
10 |
11 | it('should has a device', () => {
12 | TestBed.configureTestingModule({imports: [DeviceModule.forRoot()]});
13 |
14 | const device: Device = TestBed.get(DEVICE);
15 | expect(device).toBeDefined();
16 | });
17 |
18 | it('should has a device with specific UserAgent', () => {
19 | TestBed.configureTestingModule({
20 | imports: [DeviceModule.forRoot()],
21 | providers: [
22 | {
23 | provide: USER_AGENT,
24 | useValue:
25 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11A465 Twitter for iPhone'
26 | }
27 | ]
28 | });
29 |
30 | const device: Device = TestBed.get(DEVICE);
31 | expect(device).toBeDefined();
32 | expect(device.isMobile()).toBeTruthy();
33 | expect(device.platform).toBe(DevicePlatform.IOS);
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/projects/device/src/lib/device.module.ts:
--------------------------------------------------------------------------------
1 | import {DOCUMENT} from '@angular/common';
2 | import {ModuleWithProviders, NgModule, Optional} from '@angular/core';
3 | import {Device} from './device.model';
4 | import {DeviceService} from './device.service';
5 | import {DEVICE, USER_AGENT} from './device.token';
6 |
7 | export function deviceResolverFactory(userAgent?: string, document?: any): Device {
8 | if (!userAgent && document) {
9 | userAgent = document.defaultView?.navigator?.userAgent;
10 | }
11 |
12 | return DeviceService.resolveDevice(userAgent);
13 | }
14 |
15 | @NgModule()
16 | export class DeviceModule {
17 | /**
18 | * In root module to provide the DEVICE
19 | */
20 | static forRoot(): ModuleWithProviders {
21 | return {
22 | ngModule: DeviceModule,
23 | providers: [
24 | {
25 | provide: DEVICE,
26 | useFactory: deviceResolverFactory,
27 | deps: [[new Optional(), USER_AGENT], [new Optional(), DOCUMENT]]
28 | }
29 | ]
30 | };
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/projects/device/src/lib/device.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {Device, DevicePlatform, DeviceType} from './device.model';
2 | import {DeviceService} from './device.service';
3 |
4 | describe('DeviceService', () => {
5 | it('devices is mobiles', () => {
6 | let device: Device;
7 |
8 | // Android
9 | [
10 | 'Mozilla/5.0 (Linux; Android 6.0.1; SM-G920V Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36',
11 | 'Mozilla/5.0 (Linux; Android 5.1.1; SM-G928X Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36',
12 | 'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586',
13 | 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 6P Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36',
14 | 'Mozilla/5.0 (Linux; Android 6.0.1; E6653 Build/32.2.A.0.253) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36',
15 | 'Mozilla/5.0 (Linux; Android 6.0; HTC One M9 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36',
16 | 'Opera/12.02 (Android 4.1; Linux; Opera Mobi/ADR-1111101157; U; en-US) Presto/2.9.201 Version/12.02'
17 | ].forEach(UA => {
18 | device = DeviceService.resolveDevice(UA);
19 | expect(device.type).toBe(DeviceType.MOBILE);
20 | expect(device.isMobile()).toBeTruthy();
21 | expect(device.platform).toBe(DevicePlatform.ANDROID);
22 | });
23 |
24 | // Iphone / Ipod
25 | [
26 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11A465 Twitter for iPhone',
27 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_2 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10B146',
28 | 'Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_3 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B511 Safari/9537.53',
29 | 'Mozilla/5.0 (iPod; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A405 Safari/7534.48.3'
30 | ].forEach(UA => {
31 | device = DeviceService.resolveDevice(UA);
32 | expect(device.type).toBe(DeviceType.MOBILE);
33 | expect(device.isMobile()).toBeTruthy();
34 | expect(device.platform).toBe(DevicePlatform.IOS);
35 | });
36 |
37 | // Others
38 | [
39 | 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+',
40 | 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.1+ (KHTML, like Gecko)',
41 | 'Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)',
42 | 'NokiaE66/GoBrowser/2.0.297',
43 | 'Nokia5320XpressMusic/GoBrowser/1.6.91',
44 | 'Opera/9.80 (J2ME/MIDP; Opera Mini/9.80 (J2ME/22.478; U; en) Presto/2.5.25 Version/10.54',
45 | 'Opera/9.80 (Windows NT 6.1; Opera Mobi/49; U; en) Presto/2.4.18 Version/10.00'
46 | ].forEach(UA => {
47 | device = DeviceService.resolveDevice(UA);
48 | expect(device.type).toBe(DeviceType.MOBILE);
49 | expect(device.isMobile()).toBeTruthy();
50 | expect(device.platform).toBe(DevicePlatform.UNKNOWN);
51 | });
52 | });
53 |
54 | it('devices is tablets', () => {
55 | let device: Device;
56 |
57 | // Android
58 | [
59 | 'Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36',
60 | 'MMozilla/5.0 (Linux; Android 6.0.1; SGP771 Build/32.2.A.0.253; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36',
61 | 'Mozilla/5.0 (Linux; Android 5.1.1; SHIELD Tablet Build/LMY48C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Safari/537.36',
62 | 'Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-T550 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.3 Chrome/38.0.2125.102 Safari/537.36',
63 | 'Mozilla/5.0 (Linux; Android 4.4.3; KFTHWI Build/KTU84M) AppleWebKit/537.36 (KHTML, like Gecko) Silk/47.1.79 like Chrome/47.0.2526.80 Safari/537.36'
64 | ].forEach(UA => {
65 | device = DeviceService.resolveDevice(UA);
66 | expect(device.type).toBe(DeviceType.TABLET);
67 | expect(device.isTablet()).toBeTruthy();
68 | expect(device.platform).toBe(DevicePlatform.ANDROID);
69 | });
70 |
71 | // IPad
72 | [
73 | 'Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10',
74 | 'Mozilla/5.0 (iPad; U; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8L1 Safari/6533.18.5'
75 | ].forEach(UA => {
76 | device = DeviceService.resolveDevice(UA);
77 | expect(device.type).toBe(DeviceType.TABLET);
78 | expect(device.isTablet()).toBeTruthy();
79 | expect(device.platform).toBe(DevicePlatform.IOS);
80 | });
81 |
82 | // Other
83 | [
84 | 'Mozilla/5.0 (Linux; U; en-US) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) Version/4.0 Kindle/3.0 (screen 600x800; rotate)'
85 | ].forEach(UA => {
86 | device = DeviceService.resolveDevice(UA);
87 | expect(device.type).toBe(DeviceType.TABLET);
88 | expect(device.isTablet()).toBeTruthy();
89 | expect(device.platform).toBe(DevicePlatform.UNKNOWN);
90 | });
91 | });
92 |
93 | it('devices is normal (Desktop)', () => {
94 | let device: Device;
95 |
96 | [
97 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246',
98 | 'Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36',
99 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9',
100 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36',
101 | 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1',
102 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36'
103 | ].forEach(UA => {
104 | device = DeviceService.resolveDevice(UA);
105 | expect(device.type).toBe(DeviceType.NORMAL);
106 | expect(device.isNormal()).toBeTruthy();
107 | expect(device.platform).toBe(DevicePlatform.UNKNOWN);
108 | });
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/projects/device/src/lib/device.service.ts:
--------------------------------------------------------------------------------
1 | import {Device, DevicePlatform, DeviceType} from './device.model';
2 |
3 | /**
4 | * see https://github.com/spring-projects/spring-mobile
5 | */
6 | export class DeviceService {
7 | private static KNOWN_MOBILE_USER_AGENT_PREFIXES: string[] = [
8 | 'w3c ',
9 | 'w3c-',
10 | 'acs-',
11 | 'alav',
12 | 'alca',
13 | 'amoi',
14 | 'avan',
15 | 'benq',
16 | 'bird',
17 | 'blac',
18 | 'blaz',
19 | 'brew',
20 | 'cell',
21 | 'cldc',
22 | 'cmd-',
23 | 'dang',
24 | 'doco',
25 | 'eric',
26 | 'hipt',
27 | 'htc_',
28 | 'inno',
29 | 'ipaq',
30 | 'ipod',
31 | 'jigs',
32 | 'kddi',
33 | 'keji',
34 | 'leno',
35 | 'lg-c',
36 | 'lg-d',
37 | 'lg-g',
38 | 'lge-',
39 | 'lg/u',
40 | 'maui',
41 | 'maxo',
42 | 'midp',
43 | 'mits',
44 | 'mmef',
45 | 'mobi',
46 | 'mot-',
47 | 'moto',
48 | 'mwbp',
49 | 'nec-',
50 | 'newt',
51 | 'noki',
52 | 'palm',
53 | 'pana',
54 | 'pant',
55 | 'phil',
56 | 'play',
57 | 'port',
58 | 'prox',
59 | 'qwap',
60 | 'sage',
61 | 'sams',
62 | 'sany',
63 | 'sch-',
64 | 'sec-',
65 | 'send',
66 | 'seri',
67 | 'sgh-',
68 | 'shar',
69 | 'sie-',
70 | 'siem',
71 | 'smal',
72 | 'smar',
73 | 'sony',
74 | 'sph-',
75 | 'symb',
76 | 't-mo',
77 | 'teli',
78 | 'tim-',
79 | 'tosh',
80 | 'tsm-',
81 | 'upg1',
82 | 'upsi',
83 | 'vk-v',
84 | 'voda',
85 | 'wap-',
86 | 'wapa',
87 | 'wapi',
88 | 'wapp',
89 | 'wapr',
90 | 'webc',
91 | 'winw',
92 | 'winw',
93 | 'xda ',
94 | 'xda-'
95 | ];
96 |
97 | private static KNOWN_MOBILE_USER_AGENT_KEYWORDS: string[] = [
98 | 'blackberry',
99 | 'webos',
100 | 'ipod',
101 | 'lge vx',
102 | 'midp',
103 | 'maemo',
104 | 'mmp',
105 | 'mobile',
106 | 'netfront',
107 | 'hiptop',
108 | 'nintendo DS',
109 | 'novarra',
110 | 'openweb',
111 | 'opera mobi',
112 | 'opera mini',
113 | 'palm',
114 | 'psp',
115 | 'phone',
116 | 'smartphone',
117 | 'symbian',
118 | 'up.browser',
119 | 'up.link',
120 | 'wap',
121 | 'windows ce'
122 | ];
123 |
124 | private static KNOWN_TABLET_USER_AGENT_KEYWORDS: string[] = ['ipad', 'playbook', 'hp-tablet', 'kindle'];
125 |
126 | static resolveDevice(userAgent: string): Device {
127 | if (!userAgent) {
128 | return new Device(DeviceType.NORMAL, DevicePlatform.UNKNOWN);
129 | }
130 |
131 | userAgent = userAgent.toLowerCase();
132 |
133 | /**
134 | * Tablet Detection
135 | */
136 |
137 | // Apple
138 | if (userAgent.includes('ipad')) {
139 | return new Device(DeviceType.TABLET, DevicePlatform.IOS);
140 | }
141 |
142 | const isMobile: boolean =
143 | userAgent.includes('mobile') ||
144 | DeviceService.KNOWN_MOBILE_USER_AGENT_KEYWORDS.some(mobileUserAgent => userAgent.includes(mobileUserAgent));
145 | if (!isMobile) {
146 | // Android
147 | if (userAgent.includes('android')) {
148 | return new Device(DeviceType.TABLET, DevicePlatform.ANDROID);
149 | }
150 | // Kindle Fire
151 | if (userAgent.includes('silk')) {
152 | return new Device(DeviceType.TABLET, DevicePlatform.UNKNOWN);
153 | }
154 | }
155 | // From keywords
156 | if (DeviceService.KNOWN_TABLET_USER_AGENT_KEYWORDS.some(tabletUserAgent => userAgent.includes(tabletUserAgent))) {
157 | return new Device(DeviceType.TABLET, DevicePlatform.UNKNOWN);
158 | }
159 |
160 | /**
161 | * Mobile detection
162 | */
163 | // From prefix
164 | if (
165 | userAgent.length >= 4 &&
166 | DeviceService.KNOWN_MOBILE_USER_AGENT_PREFIXES.indexOf(userAgent.substring(0, 4)) !== -1
167 | ) {
168 | return new Device(DeviceType.MOBILE, DevicePlatform.UNKNOWN);
169 | }
170 | // Android
171 | if (userAgent.includes('android')) {
172 | return new Device(DeviceType.MOBILE, DevicePlatform.ANDROID);
173 | }
174 | // Apple
175 | if (userAgent.includes('iphone') || userAgent.includes('ipod') || userAgent.includes('ipad')) {
176 | return new Device(DeviceType.MOBILE, DevicePlatform.IOS);
177 | }
178 | // From keywords
179 | if (isMobile) {
180 | return new Device(DeviceType.MOBILE, DevicePlatform.UNKNOWN);
181 | }
182 |
183 | /**
184 | * => Normal device
185 | */
186 | return new Device(DeviceType.NORMAL, DevicePlatform.UNKNOWN);
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/projects/device/src/lib/device.token.ts:
--------------------------------------------------------------------------------
1 | import {InjectionToken} from '@angular/core';
2 | import {Device} from './device.model';
3 |
4 | export const USER_AGENT = new InjectionToken('USER_AGENT');
5 | export const DEVICE = new InjectionToken('DEVICE');
6 |
--------------------------------------------------------------------------------
/projects/device/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/device/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of device
3 | */
4 |
5 | export {DeviceType, DevicePlatform, Device} from './lib/device.model';
6 | export {USER_AGENT, DEVICE} from './lib/device.token';
7 | export {DeviceModule} from './lib/device.module';
8 |
9 |
--------------------------------------------------------------------------------
/projects/device/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().map(context);
27 |
--------------------------------------------------------------------------------
/projects/device/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "skipTemplateCodegen": true,
16 | "strictMetadataEmit": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": [
20 | "src/test.ts",
21 | "**/*.spec.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/projects/device/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/device/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/logger/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json",
3 | "ignorePatterns": [
4 | "!**/*",
5 | "**/*.spec.ts"
6 | ],
7 | "overrides": [
8 | {
9 | "files": [
10 | "*.ts"
11 | ],
12 | "parserOptions": {
13 | "project": [
14 | "projects/logger/tsconfig.lib.json",
15 | "projects/logger/tsconfig.spec.json"
16 | ],
17 | "createDefaultProgram": true
18 | },
19 | "rules": {
20 | "@angular-eslint/directive-selector": [
21 | "error",
22 | {
23 | "type": "attribute",
24 | "prefix": "lib",
25 | "style": "camelCase"
26 | }
27 | ],
28 | "@angular-eslint/component-selector": [
29 | "error",
30 | {
31 | "type": "element",
32 | "prefix": "lib",
33 | "style": "kebab-case"
34 | }
35 | ],
36 | "@typescript-eslint/no-unsafe-member-access": "off",
37 | "@typescript-eslint/no-unsafe-assignment": "off",
38 | "@typescript-eslint/no-unsafe-call": "off",
39 | "@typescript-eslint/no-unsafe-return": "off",
40 | "@typescript-eslint/no-unsafe-argument": "off",
41 | "@typescript-eslint/no-empty-function": "off",
42 | "@typescript-eslint/no-unused-vars": "off",
43 | "@typescript-eslint/no-explicit-any": "off",
44 | "@typescript-eslint/restrict-template-expressions": "off",
45 | "@typescript-eslint/ban-types": "off"
46 | }
47 | },
48 | {
49 | "files": [
50 | "*.html"
51 | ],
52 | "rules": {}
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/projects/logger/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/@ngx-toolkit/logger)
2 | [](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
3 | [](https://travis-ci.org/dewizz/ngx-toolkit)
4 | [](https://coveralls.io/github/dewizz/ngx-toolkit?branch=master)
5 | [](https://gitter.im/ngx-toolkit/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | # @ngx-toolkit/logger
8 | > Angular LoggerService (with default `ConsoleLoggerService` implementation)
9 |
10 | # Table of contents:
11 | * [Installation](#installation)
12 | * [Usage](#usage)
13 | * [LoggerService](#loggerservice)
14 | * [Custom Implementation](#custom-implementation)
15 | * [Logger rxjs operator](#logger-rxjs-operator)
16 | * [Logger decorator](#logger-decorator)
17 | * [License](#license)
18 |
19 | ---
20 |
21 | # Installation
22 |
23 | Install the npm package.
24 |
25 | ```bash
26 | # To get the latest stable version and update package.json file:
27 | npm install @ngx-toolkit/logger --save
28 | # or
29 | yarn add @ngx-toolkit/logger
30 | ```
31 |
32 | Import `LoggerModule` in the root Module of your application with `forRoot(level?: Level)` to choose your log level. If you don't specify a level, you haven't any log.
33 |
34 | ```typescript
35 | import { NgModule, isDevMode } from '@angular/core';
36 | import { BrowserModule } from '@angular/platform-browser';
37 | import { LoggerModule, Level } from '@ngx-toolkit/logger';
38 |
39 | import { AppComponent } from './app.component';
40 |
41 | const LOG_LEVEL: Level = isDevMode() ? Level.INFO : Level.ERROR;
42 |
43 | @NgModule({
44 | imports: [ BrowserModule, LoggerModule.forRoot(LOG_LEVEL) ],
45 | declarations: [ AppComponent ],
46 | bootstrap: [ AppComponent ]
47 | })
48 | export class AppModule { }
49 | ```
50 |
51 | # Usage
52 |
53 | ```typescript
54 | import { Component, OnInit } from '@angular/core';
55 | import { LoggerService } from '@ngx-toolkit/logger';
56 |
57 | @Component({
58 | selector: 'app-root',
59 | templateUrl: './app.component.html',
60 | styleUrls: ['./app.component.css']
61 | })
62 | export class AppComponent implements OnInit {
63 | constructor(private logger: LoggerService) {}
64 |
65 | ngOnInit() {
66 | this.logger.info('OnInit my AppCommponent');
67 | }
68 | }
69 | ```
70 |
71 | # LoggerService
72 |
73 | The `LoggerSerivce` API:
74 |
75 | ```typescript
76 | /**
77 | * Outputs an error message.
78 | */
79 | error(message?: any, ...optionalParams: any[]) {}
80 |
81 | /**
82 | * Outputs a warning message.
83 | */
84 | warn(message?: any, ...optionalParams: any[]) {}
85 |
86 | /**
87 | * Outputs an informational message.
88 | */
89 | info(message?: any, ...optionalParams: any[]) {}
90 |
91 | /**
92 | * Outputs a debug message.
93 | */
94 | debug(message?: any, ...optionalParams: any[]) {}
95 |
96 | /**
97 | * Outputs a message.
98 | */
99 | log(message?: any, ...optionalParams: any[]) {}
100 | ```
101 |
102 | # Custom implementation
103 |
104 | You can create you own implementation. Our sample with the [winston](https://github.com/winstonjs/winston) API:
105 |
106 | ```typescript
107 | import { Inject, Injectable } from '@angular/core';
108 | import { LoggerService, LOGGER_LEVEL, Level } from '@ngx-toolkit/logger';
109 | import winston from 'winston';
110 |
111 | @Injectable()
112 | export class WinstonLoggerService extends LoggerService {
113 | private logger: any;
114 |
115 | constructor(@Inject(LOGGER_LEVEL) level: Level) {
116 | super();
117 |
118 | this.logger = winston.createLogger({
119 | transports: [
120 | new winston.transports.File({
121 | filename: 'combined.log',
122 | level: 'info'
123 | })
124 | ]
125 | });
126 | }
127 |
128 | info(message?: any, ...optionalParams: any[]) {
129 | this.logger.info(message, optionalParams);
130 | }
131 |
132 | ...
133 | }
134 | ```
135 |
136 | To register WinstonLoggerService in the Angular application, provide the `LoggerModule` with `forRoot(level?: Level, provider?: Type,)` as:
137 |
138 | ```typescript
139 | import { NgModule, isDevMode } from '@angular/core';
140 | import { BrowserModule } from '@angular/platform-browser';
141 | import { LoggerModule, Level } from '@ngx-toolkit/logger';
142 |
143 | import { WinstonLoggerService } from './winston-logger.service';
144 | import { AppComponent } from './app.component';
145 |
146 | const LOG_LEVEL: Level = isDevMode() ? Level.INFO : Level.ERROR;
147 |
148 | @NgModule({
149 | imports: [ BrowserModule, LoggerModule.forRoot(LOG_LEVEL, WinstonLoggerService) ],
150 | declarations: [ AppComponent ],
151 | bootstrap: [ AppComponent ]
152 | })
153 | export class AppModule { }
154 | ```
155 |
156 | # Logger rxjs operator
157 |
158 | - logger(message: string,
159 | nextLevel: Level = Level.INFO,
160 | errorLevel: Level = Level.ERROR,
161 | completeLevel?: Level): MonoTypeOperatorFunction
162 |
163 | ```typescript
164 | import { Component, OnInit } from '@angular/core';
165 | import { logger } from '@ngx-toolkit/logger';
166 | import { timer } from 'rxjs';
167 |
168 | @Component({
169 | selector: 'app-root',
170 | templateUrl: './app.component.html',
171 | styleUrls: ['./app.component.css']
172 | })
173 | export class AppComponent implements OnInit {
174 | constructor() {}
175 |
176 | ngOnInit() {
177 | timer(1000, 2000).pipe(
178 | logger('timer')
179 | ).subscribe();
180 | }
181 | }
182 | ```
183 |
184 | # Logger decorator
185 |
186 | - Log(message?: string, level: Level = Level.INFO)
187 | - Debug(message?: string) : Alias of Log(message?: string, Level.DEBUG)
188 |
189 | ```typescript
190 | import { Component, OnInit } from '@angular/core';
191 | import { Debug } from '@ngx-toolkit/logger';
192 |
193 | @Component({
194 | selector: 'app-root',
195 | templateUrl: './app.component.html',
196 | styleUrls: ['./app.component.css']
197 | })
198 | export class AppComponent implements OnInit {
199 | constructor() {}
200 |
201 | @Debug()
202 | action(param: string) {
203 | return "result";
204 | }
205 | }
206 | ```
207 |
208 | ----
209 |
210 | # License
211 | © 2018 Dewizz
212 |
213 | [MIT](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
214 |
--------------------------------------------------------------------------------
/projects/logger/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, '../../coverage/logger'),
29 | subdir: '.',
30 | reporters: [
31 | {type: 'html'},
32 | {type: 'lcovonly'},
33 | {type: 'text-summary'}
34 | ]
35 | },
36 | reporters: ['progress', 'kjhtml'],
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessCI'],
42 | customLaunchers: {
43 | ChromeHeadlessCI: {
44 | base: 'ChromeHeadless',
45 | flags: ['--no-sandbox']
46 | }
47 | },
48 | singleRun: false,
49 | restartOnFileChange: true
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/projects/logger/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/logger",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/logger/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-toolkit/logger",
3 | "version": "0.0.0-PLACEHOLDER",
4 | "description": "Angular logger abstraction",
5 | "homepage": "https://github.com/dewizz/ngx-toolkit",
6 | "license": "MIT",
7 | "author": "Dewizz",
8 | "keywords": [
9 | "angular",
10 | "ngx",
11 | "ngx-toolkit",
12 | "log",
13 | "logger"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/dewizz/ngx-toolkit"
18 | },
19 | "bugs": {
20 | "url": "https://github.com/dewizz/ngx-toolkit/issues"
21 | },
22 | "dependencies": {
23 | "tslib": "^2.0.0"
24 | },
25 | "peerDependencies": {
26 | "rxjs": "^6.5.0",
27 | "@angular/core": "^13.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/console-logger.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {ConsoleLoggerService} from './console-logger.service';
2 | import {Level} from './level.model';
3 | import {LoggerService} from './logger.service';
4 |
5 | describe('ConsoleLoggerService', () => {
6 | let service: LoggerService;
7 | const toSpy: any = {
8 | log: function(message?: any, ...optionalParams: any[]) {
9 | // NOTHING
10 | }
11 | };
12 |
13 | beforeEach(() => {
14 | // Override console
15 | ['error', 'warn', 'info', 'debug', 'log'].forEach(level => {
16 | console[level] = (...args: any[]) => {
17 | toSpy.log.apply(toSpy, args);
18 | };
19 | });
20 |
21 | spyOn(toSpy, 'log');
22 | });
23 |
24 | it('check console logger called', () => {
25 | service = new ConsoleLoggerService(Level.LOG);
26 |
27 | const consoleArgs: any = 'args';
28 |
29 | service.error('error', consoleArgs);
30 | expect(toSpy.log).toHaveBeenCalledWith('error', consoleArgs);
31 |
32 | service.warn('warn', consoleArgs);
33 | expect(toSpy.log).toHaveBeenCalledWith('warn', consoleArgs);
34 |
35 | service.info('info', consoleArgs);
36 | expect(toSpy.log).toHaveBeenCalledWith('info', consoleArgs);
37 |
38 | service.debug('debug', consoleArgs);
39 | expect(toSpy.log).toHaveBeenCalledWith('debug', consoleArgs);
40 |
41 | service.log('log', consoleArgs);
42 | expect(toSpy.log).toHaveBeenCalledWith('log', consoleArgs);
43 | });
44 |
45 | it('check console logger level', () => {
46 | service = new ConsoleLoggerService(Level.WARN);
47 |
48 | service.error('error');
49 | expect(toSpy.log).toHaveBeenCalledWith('error');
50 |
51 | service.warn('warn');
52 | expect(toSpy.log).toHaveBeenCalledWith('warn');
53 |
54 | service.info('info');
55 | expect(toSpy.log).not.toHaveBeenCalledWith('info');
56 |
57 | service.debug('debug');
58 | expect(toSpy.log).not.toHaveBeenCalledWith('debug');
59 |
60 | service.log('log');
61 | expect(toSpy.log).not.toHaveBeenCalledWith('log');
62 | });
63 |
64 | it('check console logger logLevel', () => {
65 | service = new ConsoleLoggerService(Level.LOG);
66 |
67 | const args: any = {data: {foo: 'bar'}};
68 | service.logLevel(Level.INFO, 'test', args);
69 | expect(toSpy.log).toHaveBeenCalledWith('test', args);
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/console-logger.service.ts:
--------------------------------------------------------------------------------
1 | import {Inject, Injectable, Optional} from '@angular/core';
2 | import {Level} from './level.model';
3 | import {LOGGER_LEVEL} from './level.token';
4 | import {LoggerService} from './logger.service';
5 |
6 | @Injectable()
7 | export class ConsoleLoggerService extends LoggerService {
8 | constructor(@Optional() @Inject(LOGGER_LEVEL) level: Level) {
9 | super();
10 |
11 | if (level) {
12 | Object.keys(Level)
13 | .filter(s => isNaN(+s) && level >= Level[s])
14 | .forEach(levelName => {
15 | const methodName: string = levelName.toLowerCase();
16 | this[methodName] = console[methodName].bind(console);
17 | });
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/level.model.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Log levels
3 | */
4 | export enum Level {
5 | ERROR,
6 | WARN,
7 | INFO,
8 | DEBUG,
9 | LOG
10 | }
11 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/level.token.ts:
--------------------------------------------------------------------------------
1 | import {InjectionToken} from '@angular/core';
2 | import {Level} from './level.model';
3 |
4 | export const LOGGER_LEVEL = new InjectionToken('LOGGER_LEVEL');
5 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/logger.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import {ConsoleLoggerService} from './console-logger.service';
2 | import {Level} from './level.model';
3 | import {Debug, LOGGER_DECORATOR_DATA} from './logger.decorator';
4 |
5 | describe('LoggerDecorator', () => {
6 | beforeEach(() => {
7 | LOGGER_DECORATOR_DATA.loggerService = new ConsoleLoggerService(Level.DEBUG);
8 |
9 | spyOn(LOGGER_DECORATOR_DATA.loggerService, 'debug');
10 | });
11 |
12 | it('should work', () => {
13 | class Test {
14 | @Debug('increment')
15 | toto(n: number): number {
16 | return n + 1;
17 | }
18 | }
19 |
20 | const test: Test = new Test();
21 | expect(test.toto(1)).toBe(2);
22 | expect(LOGGER_DECORATOR_DATA.loggerService.debug).toHaveBeenCalledWith('increment', [1], 2);
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/logger.decorator.ts:
--------------------------------------------------------------------------------
1 | import {Level} from './level.model';
2 | import {LoggerService} from './logger.service';
3 |
4 | export interface LoggerDecoratorData {
5 | loggerService?: LoggerService;
6 | }
7 |
8 | export const LOGGER_DECORATOR_DATA: LoggerDecoratorData = {};
9 |
10 | export function Debug(message?: string): MethodDecorator {
11 | return Log(message, Level.DEBUG);
12 | }
13 |
14 | export function Log(message?: string, level: Level = Level.INFO): MethodDecorator {
15 | return (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor => {
16 | const originalMethod = descriptor.value;
17 | descriptor.value = function(...args: any[]) {
18 | const result = originalMethod.apply(this, args);
19 | if (LOGGER_DECORATOR_DATA.loggerService) {
20 | LOGGER_DECORATOR_DATA.loggerService.logLevel(level, message || `Call ${propertyKey.toString()}`, args, result);
21 | }
22 | return result;
23 | };
24 | return descriptor;
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/logger.module.spec.ts:
--------------------------------------------------------------------------------
1 | import {ApplicationInitStatus} from '@angular/core';
2 | import {TestBed} from '@angular/core/testing';
3 | import {ConsoleLoggerService} from './console-logger.service';
4 | import {Level} from './level.model';
5 | import {LOGGER_DECORATOR_DATA} from './logger.decorator';
6 | import {LoggerModule} from './logger.module';
7 | import {LoggerService} from './logger.service';
8 |
9 | describe('LoggerModule', () => {
10 | it('should work', () => {
11 | expect(new LoggerModule()).toBeDefined();
12 | });
13 |
14 | it('should instantiate service', () => {
15 | TestBed.configureTestingModule({imports: [LoggerModule.forRoot(Level.LOG, ConsoleLoggerService)]});
16 |
17 | const service: LoggerService = TestBed.get(LoggerService);
18 | expect(service instanceof ConsoleLoggerService).toBeTruthy();
19 | });
20 |
21 | it('should configure LOGGER_DECORATOR_DATA', async (done: DoneFn) => {
22 | await TestBed.configureTestingModule({
23 | imports: [LoggerModule.forRoot()]
24 | })
25 | // https://github.com/angular/angular/issues/24218
26 | .get(ApplicationInitStatus).donePromise;
27 |
28 | expect(LOGGER_DECORATOR_DATA.loggerService).toBeDefined();
29 | done();
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/logger.module.ts:
--------------------------------------------------------------------------------
1 | import {APP_INITIALIZER, ModuleWithProviders, NgModule, Type} from '@angular/core';
2 | import {ConsoleLoggerService} from './console-logger.service';
3 | import {Level} from './level.model';
4 | import {LOGGER_LEVEL} from './level.token';
5 | import {LOGGER_DECORATOR_DATA} from './logger.decorator';
6 | import {LoggerService} from './logger.service';
7 |
8 | export function setupLoggerDecorator(loggerService: LoggerService) {
9 | LOGGER_DECORATOR_DATA.loggerService = loggerService;
10 | return () => null;
11 | }
12 |
13 | @NgModule()
14 | export class LoggerModule {
15 | static forRoot(level: Level = null, provider: Type = ConsoleLoggerService): ModuleWithProviders {
16 | return {
17 | ngModule: LoggerModule,
18 | providers: [
19 | {
20 | provide: LOGGER_LEVEL,
21 | useValue: level
22 | },
23 | {
24 | provide: LoggerService,
25 | useClass: provider
26 | },
27 | {
28 | provide: APP_INITIALIZER,
29 | useFactory: setupLoggerDecorator,
30 | deps: [LoggerService],
31 | multi: true
32 | }
33 | ]
34 | };
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/logger.rxjs.ts:
--------------------------------------------------------------------------------
1 | import {MonoTypeOperatorFunction} from 'rxjs';
2 | import {tap} from 'rxjs/operators';
3 | import {Level} from './level.model';
4 | import {LOGGER_DECORATOR_DATA} from './logger.decorator';
5 |
6 | export function logger(message: string,
7 | nextLevel: Level = Level.INFO,
8 | errorLevel: Level = Level.ERROR,
9 | completeLevel?: Level): MonoTypeOperatorFunction {
10 | return tap(x => {
11 | if (LOGGER_DECORATOR_DATA.loggerService) {
12 | LOGGER_DECORATOR_DATA.loggerService.logLevel(nextLevel, message, x);
13 | }
14 | }, e => {
15 | if (LOGGER_DECORATOR_DATA.loggerService) {
16 | LOGGER_DECORATOR_DATA.loggerService.logLevel(errorLevel, message, e);
17 | }
18 | }, () => {
19 | if (LOGGER_DECORATOR_DATA.loggerService && completeLevel) {
20 | LOGGER_DECORATOR_DATA.loggerService.logLevel(completeLevel, message);
21 | }
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/projects/logger/src/lib/logger.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {Level} from './level.model';
3 |
4 | @Injectable()
5 | export class LoggerService {
6 |
7 | /**
8 | * Outputs an error message.
9 | */
10 | error(message?: any, ...optionalParams: any[]) {
11 | }
12 |
13 | /**
14 | * Outputs a warning message.
15 | */
16 | warn(message?: any, ...optionalParams: any[]) {
17 | }
18 |
19 | /**
20 | * Outputs an informational message.
21 | */
22 | info(message?: any, ...optionalParams: any[]) {
23 | }
24 |
25 | /**
26 | * Outputs a debug message.
27 | */
28 | debug(message?: any, ...optionalParams: any[]) {
29 | }
30 |
31 | /**
32 | * Outputs a message.
33 | */
34 | log(message?: any, ...optionalParams: any[]) {
35 | }
36 |
37 | /**
38 | * Outputs a message.
39 | */
40 | logLevel(level: Level, message?: any, ...optionalParams: any[]) {
41 | switch (level) {
42 | case Level.ERROR :
43 | this.error(message, ...optionalParams);
44 | break;
45 | case Level.WARN :
46 | this.warn(message, ...optionalParams);
47 | break;
48 | case Level.INFO :
49 | this.info(message, ...optionalParams);
50 | break;
51 | case Level.DEBUG :
52 | this.debug(message, ...optionalParams);
53 | break;
54 | case Level.LOG :
55 | this.log(message, ...optionalParams);
56 | break;
57 | }
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/projects/logger/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/logger/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of logger
3 | */
4 |
5 | export {Level} from './lib/level.model';
6 | export {LOGGER_LEVEL} from './lib/level.token';
7 | export {LoggerService} from './lib/logger.service';
8 | export {ConsoleLoggerService} from './lib/console-logger.service';
9 | export {LoggerModule} from './lib/logger.module';
10 | export {Log, Debug} from './lib/logger.decorator';
11 | export {logger} from './lib/logger.rxjs';
12 |
--------------------------------------------------------------------------------
/projects/logger/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().map(context);
27 |
--------------------------------------------------------------------------------
/projects/logger/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "skipTemplateCodegen": true,
16 | "strictMetadataEmit": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": [
20 | "src/test.ts",
21 | "**/*.spec.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/projects/logger/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/logger/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/spring/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json",
3 | "ignorePatterns": [
4 | "!**/*",
5 | "**/*.spec.ts"
6 | ],
7 | "overrides": [
8 | {
9 | "files": [
10 | "*.ts"
11 | ],
12 | "parserOptions": {
13 | "project": [
14 | "projects/spring/tsconfig.lib.json",
15 | "projects/spring/tsconfig.spec.json"
16 | ],
17 | "createDefaultProgram": true
18 | },
19 | "rules": {
20 | "@angular-eslint/directive-selector": [
21 | "error",
22 | {
23 | "type": "attribute",
24 | "prefix": "lib",
25 | "style": "camelCase"
26 | }
27 | ],
28 | "@angular-eslint/component-selector": [
29 | "error",
30 | {
31 | "type": "element",
32 | "prefix": "lib",
33 | "style": "kebab-case"
34 | }
35 | ],
36 | "@typescript-eslint/no-unsafe-member-access": "off",
37 | "@typescript-eslint/no-unsafe-assignment": "off",
38 | "@typescript-eslint/no-unsafe-call": "off",
39 | "@typescript-eslint/no-unsafe-return": "off",
40 | "@typescript-eslint/no-unsafe-argument": "off",
41 | "@typescript-eslint/no-empty-function": "off",
42 | "@typescript-eslint/no-unused-vars": "off",
43 | "@typescript-eslint/no-explicit-any": "off",
44 | "@typescript-eslint/restrict-template-expressions": "off",
45 | "@typescript-eslint/ban-types": "off"
46 | }
47 | },
48 | {
49 | "files": [
50 | "*.html"
51 | ],
52 | "rules": {}
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/projects/spring/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/@ngx-toolkit/spring)
2 | [](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
3 | [](https://travis-ci.org/dewizz/ngx-toolkit)
4 | [](https://coveralls.io/github/dewizz/ngx-toolkit?branch=master)
5 | [](https://gitter.im/ngx-toolkit/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | # @ngx-toolkit/spring
8 | > Angular Spring utilities
9 |
10 | # Table of contents:
11 | * [Installation](#installation)
12 | * [Spring Data](#spring-data)
13 | * [License](#license)
14 |
15 | ---
16 |
17 | # Installation
18 |
19 | Install the npm package.
20 |
21 | ```bash
22 | # To get the latest stable version and update package.json file:
23 | npm install @ngx-toolkit/spring --save
24 | # or
25 | yarn add @ngx-toolkit/spring
26 | ```
27 |
28 | # Spring Data
29 |
30 | Some [Spring Data Commons](https://github.com/spring-projects/spring-data-commons) classes:
31 |
32 | - `Page` type
33 | - `Sort(orders: string | string[] | Order[], direction: Direction = 'ASC')`
34 | - `PageRequest(page: number = 0, size: number = 20, sort?: Sort)` with `toHttpParams(options: {
35 | fromString?: string;
36 | fromObject?: {
37 | [param: string]: string | string[];
38 | };
39 | encoder?: HttpParameterCodec;
40 | } = {} as {
41 | fromString?: string;
42 | fromObject?: {
43 | [param: string]: string | string[];
44 | };
45 | encoder?: HttpParameterCodec;
46 | })` method to convert value in request params
47 |
48 | ----
49 |
50 | # License
51 | © 2018 Dewizz
52 |
53 | [MIT](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
54 |
--------------------------------------------------------------------------------
/projects/spring/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, '../../coverage/spring'),
29 | subdir: '.',
30 | reporters: [
31 | {type: 'html'},
32 | {type: 'lcovonly'},
33 | {type: 'text-summary'}
34 | ]
35 | },
36 | reporters: ['progress', 'kjhtml'],
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessCI'],
42 | customLaunchers: {
43 | ChromeHeadlessCI: {
44 | base: 'ChromeHeadless',
45 | flags: ['--no-sandbox']
46 | }
47 | },
48 | singleRun: false,
49 | restartOnFileChange: true
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/projects/spring/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/spring",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/spring/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-toolkit/spring",
3 | "version": "0.0.0-PLACEHOLDER",
4 | "description": "Angular spring utilities",
5 | "homepage": "https://github.com/dewizz/ngx-toolkit",
6 | "license": "MIT",
7 | "author": "Dewizz",
8 | "keywords": [
9 | "angular",
10 | "ngx",
11 | "ngx-toolkit",
12 | "spring",
13 | "spring-data"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/dewizz/ngx-toolkit"
18 | },
19 | "bugs": {
20 | "url": "https://github.com/dewizz/ngx-toolkit/issues"
21 | },
22 | "dependencies": {
23 | "tslib": "^2.0.0"
24 | },
25 | "peerDependencies": {
26 | "@angular/common": "^13.2.0",
27 | "@angular/core": "^13.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/projects/spring/src/lib/spring-data.model.ts:
--------------------------------------------------------------------------------
1 | import {HttpParams, HttpParamsOptions} from '@angular/common/http';
2 |
3 | export const DEFAULT_PAGEABLE_SIZE = 25;
4 |
5 | export class PageRequest {
6 | readonly page: number;
7 | readonly size: number;
8 | readonly sort: Sort;
9 |
10 | constructor(page = 0, size: number = DEFAULT_PAGEABLE_SIZE, sort?: ISort) {
11 | this.page = page;
12 | this.size = size;
13 | this.sort = sort;
14 | }
15 |
16 | toHttpParams(
17 | options: HttpParamsOptions = {}
18 | ): HttpParams {
19 | let params: HttpParams = new HttpParams(options)
20 | // Add page & size
21 | .set('page', `${this.page}`)
22 | .set('size', `${this.size}`);
23 |
24 | // Add orders
25 | if (this.sort && this.sort.orders) {
26 | const groupedSort: { [key in Direction]: string[] } = this.sort.orders.reduce(
27 | (groupedProperty: any, order: Order) => {
28 | groupedProperty[order.direction] = groupedProperty[order.direction] || [];
29 | groupedProperty[order.direction].push(order.property);
30 | return groupedProperty;
31 | },
32 | {} as { [key in Direction]: string[] }
33 | );
34 |
35 | Object.keys(groupedSort).forEach(direction => {
36 | params = params.append('sort', `${groupedSort[direction].join(',')},${direction}`);
37 | });
38 | }
39 |
40 | return params;
41 | }
42 | }
43 |
44 | export interface Page {
45 | // the total amount of elements
46 | totalElements: number;
47 | // the number of total pages
48 | totalPages: number;
49 | // the page content
50 | content: T[];
51 | // the number of the current Slice
52 | number: number;
53 | // the number of elements currently on this Slice
54 | numberOfElements: number;
55 | // the size of the Slice
56 | size: number;
57 | // the sorting parameters for the Slice
58 | sort: Sort;
59 | // whether the current Slice is the first one
60 | first: boolean;
61 | // whether the current Slice is the last one
62 | last: boolean;
63 | }
64 |
65 | export type Direction = 'ASC' | 'DESC';
66 | export type NullHandling = 'NATIVE' | 'NULLS_FIRST' | 'NULLS_LAST';
67 | export const DEFAULT_DIRECTION: Direction = 'ASC';
68 |
69 | export class Sort implements ISort {
70 | readonly orders: Order[];
71 |
72 | constructor(orders: string | string[] | Order[], direction: Direction = DEFAULT_DIRECTION) {
73 | if (orders) {
74 | if (typeof orders === 'string') {
75 | this.orders = [
76 | {
77 | property: orders ,
78 | direction
79 | }
80 | ];
81 | } else if (orders.length > 0) {
82 | if (typeof orders[0] === 'string') {
83 | this.orders = (orders as string[]).map(property => {
84 | return {
85 | property,
86 | direction
87 | };
88 | });
89 | } else {
90 | this.orders = orders as Order[];
91 | }
92 | }
93 | }
94 | }
95 | }
96 |
97 | export interface ISort {
98 | orders: Order[];
99 | }
100 |
101 | export interface Order {
102 | property: string;
103 | direction: Direction;
104 | nullHandlingHint?: NullHandling;
105 | }
106 |
--------------------------------------------------------------------------------
/projects/spring/src/lib/spring-data.spec.ts:
--------------------------------------------------------------------------------
1 | import {DEFAULT_DIRECTION, DEFAULT_PAGEABLE_SIZE, Order, PageRequest, Sort} from './spring-data.model';
2 |
3 | describe('Spring Data', () => {
4 |
5 | describe('Sort', () => {
6 | it('should works with string property', () => {
7 | const sort = new Sort('property');
8 | expect(sort.orders.length).toBe(1);
9 | expect(sort.orders[0].property).toBe('property');
10 | expect(sort.orders[0].direction).toBe(DEFAULT_DIRECTION);
11 | });
12 |
13 | it('should works with string array properties', () => {
14 | const sort = new Sort(['property1', 'property2']);
15 | expect(sort.orders.length).toBe(2);
16 | expect(sort.orders[0].property).toBe('property1');
17 | expect(sort.orders[0].direction).toBe(DEFAULT_DIRECTION);
18 | });
19 |
20 | it('should works with order array properties', () => {
21 | const order: Order = {property: 'property', direction: 'DESC'};
22 | const sort = new Sort([order], 'DESC');
23 | expect(sort.orders.length).toBe(1);
24 | expect(sort.orders[0]).toBe(order);
25 | });
26 | });
27 |
28 | describe('PageRequest', () => {
29 | it('should works without param', () => {
30 | const pageRequest = new PageRequest();
31 | const httpParams = pageRequest.toHttpParams();
32 | expect(httpParams.toString()).toBe('page=0&size=25');
33 | });
34 |
35 | it('should works with default http param', () => {
36 | const pageRequest = new PageRequest(1, 50);
37 | const httpParams = pageRequest.toHttpParams({
38 | fromObject: {
39 | fakeParam: 'fakeValue'
40 | }
41 | });
42 | expect(httpParams).toBeDefined();
43 | expect(httpParams.has('fakeParam')).toBeTrue();
44 | expect(httpParams.toString()).toBe('fakeParam=fakeValue&page=1&size=50');
45 | });
46 |
47 | it('should works with sort param', () => {
48 | const pageRequest = new PageRequest(0, DEFAULT_PAGEABLE_SIZE, new Sort('sortProperty'));
49 | const httpParams = pageRequest.toHttpParams();
50 | expect(httpParams).toBeDefined();
51 | expect(httpParams.toString()).toBe('page=0&size=25&sort=sortProperty,ASC');
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/projects/spring/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/spring/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of spring
3 | */
4 |
5 | export * from './lib/spring-data.model';
6 |
7 |
--------------------------------------------------------------------------------
/projects/spring/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().map(context);
27 |
--------------------------------------------------------------------------------
/projects/spring/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "skipTemplateCodegen": true,
16 | "strictMetadataEmit": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": [
20 | "src/test.ts",
21 | "**/*.spec.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/projects/spring/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/spring/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/utils/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../.eslintrc.json",
3 | "ignorePatterns": [
4 | "!**/*",
5 | "**/*.spec.ts"
6 | ],
7 | "overrides": [
8 | {
9 | "files": [
10 | "*.ts"
11 | ],
12 | "parserOptions": {
13 | "project": [
14 | "projects/utils/tsconfig.lib.json",
15 | "projects/utils/tsconfig.spec.json"
16 | ],
17 | "createDefaultProgram": true
18 | },
19 | "rules": {
20 | "@angular-eslint/directive-selector": [
21 | "error",
22 | {
23 | "type": "attribute",
24 | "prefix": "lib",
25 | "style": "camelCase"
26 | }
27 | ],
28 | "@angular-eslint/component-selector": [
29 | "error",
30 | {
31 | "type": "element",
32 | "prefix": "lib",
33 | "style": "kebab-case"
34 | }
35 | ],
36 | "@typescript-eslint/no-unsafe-member-access": "off",
37 | "@typescript-eslint/no-unsafe-assignment": "off",
38 | "@typescript-eslint/no-unsafe-call": "off",
39 | "@typescript-eslint/no-unsafe-return": "off",
40 | "@typescript-eslint/no-unsafe-argument": "off",
41 | "@typescript-eslint/no-empty-function": "off",
42 | "@typescript-eslint/no-unused-vars": "off",
43 | "@typescript-eslint/no-explicit-any": "off",
44 | "@typescript-eslint/restrict-template-expressions": "off",
45 | "@typescript-eslint/ban-types": "off"
46 | }
47 | },
48 | {
49 | "files": [
50 | "*.html"
51 | ],
52 | "rules": {}
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/projects/utils/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/@ngx-toolkit/utils)
2 | [](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
3 | [](https://travis-ci.org/dewizz/ngx-toolkit)
4 | [](https://coveralls.io/github/dewizz/ngx-toolkit?branch=master)
5 | [](https://gitter.im/ngx-toolkit/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | # @ngx-toolkit/utils
8 | > Angular common utilities
9 |
10 | # Table of contents:
11 | * [Installation](#installation)
12 | * [Queue](#queue)
13 | * [Wait](#wait)
14 | * [Once](#once)
15 | * [License](#license)
16 |
17 | ---
18 |
19 | # Installation
20 |
21 | Install the npm package.
22 |
23 | ```bash
24 | # To get the latest stable version and update package.json file:
25 | npm install @ngx-toolkit/utils --save
26 | # or
27 | yarn add @ngx-toolkit/utils
28 | ```
29 |
30 | # Queue
31 |
32 | Queue annotation:
33 |
34 | ```typescript
35 | import { Queue } from '@ngx-toolkit/utils';
36 | ...
37 |
38 | class MyComponent {
39 | /**
40 | * Put the method call in a queue and wait for a Promise / Subscription / method execution
41 | * /!\ the method result is modified => Return a Promise
42 | * @param {number} queue limit (default: no limit)
43 | * @param {string} queue name (default: method name)
44 | */
45 | @Queue(limit?: number, name?: string)
46 | method(): Promise | Subscription | any | void;
47 | }
48 | ```
49 |
50 | # Wait
51 |
52 | Wait annotation (shortcut of @Queue(1)):
53 |
54 | ```typescript
55 | import { Wait } from '@ngx-toolkit/utils';
56 | ...
57 |
58 | class MyComponent {
59 | /**
60 | * Wait for a Promise / Subscription before to be re-executed
61 | * /!\ the method result is modified => Return a Promise
62 | * @param {string} wait name (default: method name)
63 | */
64 | @Wait(name?: string)
65 | method(): Promise | Subscription | any | void;
66 | }
67 | ```
68 |
69 | # Once
70 |
71 | Once annotation:
72 |
73 | ```typescript
74 | import { Once } from '@ngx-toolkit/utils';
75 | ...
76 |
77 | class MyComponent {
78 | /**
79 | * mark a method to be executed no more than once even if called several times
80 | * @param {string} name (default: method name)
81 | */
82 | @Once(name?: string)
83 | method(): Promise | Subscription | any | void;
84 | }
85 | ```
86 |
87 | ----
88 |
89 | # License
90 | © 2018 Dewizz
91 |
92 | [MIT](https://github.com/dewizz/ngx-toolkit/blob/master/LICENSE)
93 |
--------------------------------------------------------------------------------
/projects/utils/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | jasmineHtmlReporter: {
25 | suppressAll: true // removes the duplicated traces
26 | },
27 | coverageReporter: {
28 | dir: require('path').join(__dirname, '../../coverage/utils'),
29 | subdir: '.',
30 | reporters: [
31 | {type: 'html'},
32 | {type: 'lcovonly'},
33 | {type: 'text-summary'}
34 | ]
35 | },
36 | reporters: ['progress', 'kjhtml'],
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessCI'],
42 | customLaunchers: {
43 | ChromeHeadlessCI: {
44 | base: 'ChromeHeadless',
45 | flags: ['--no-sandbox']
46 | }
47 | },
48 | singleRun: false,
49 | restartOnFileChange: true
50 | });
51 | };
52 |
--------------------------------------------------------------------------------
/projects/utils/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/utils",
4 | "lib": {
5 | "entryFile": "src/public-api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ngx-toolkit/utils",
3 | "version": "0.0.0-PLACEHOLDER",
4 | "description": "Angular common utilities",
5 | "homepage": "https://github.com/dewizz/ngx-toolkit",
6 | "license": "MIT",
7 | "author": "Dewizz",
8 | "keywords": [
9 | "angular",
10 | "ngx",
11 | "ngx-toolkit",
12 | "annotation",
13 | "decorator",
14 | "utils",
15 | "utilities"
16 | ],
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/dewizz/ngx-toolkit"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/dewizz/ngx-toolkit/issues"
23 | },
24 | "dependencies": {
25 | "tslib": "^2.0.0"
26 | },
27 | "peerDependencies": {
28 | "@angular/core": "^13.2.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/projects/utils/src/lib/functions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Deferred
3 | */
4 | export class Deferred {
5 | resolve: (value?: T | PromiseLike) => void;
6 | reject: (reason?: any) => void;
7 | promise: Promise;
8 |
9 | constructor() {
10 | this.promise = new Promise((resolve, reject) => {
11 | this.resolve = resolve;
12 | this.reject = reject;
13 | });
14 |
15 | Object.freeze(> this);
16 | }
17 | }
18 |
19 | /**
20 | * Determine if the argument is shaped like a Promise
21 | */
22 | export function isPromise(obj: any): obj is Promise {
23 | return !!obj && typeof obj.then === 'function';
24 | }
25 |
--------------------------------------------------------------------------------
/projects/utils/src/lib/once.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import {Once} from './once.decorator';
2 |
3 | describe('OnceDecorator', () => {
4 | it('should executed once', () => {
5 | class Test {
6 | called = 0;
7 |
8 | @Once('name')
9 | func1() {
10 | this.called++;
11 | }
12 |
13 | @Once('name')
14 | func2() {
15 | this.called++;
16 | }
17 | }
18 |
19 | const test: Test = new Test();
20 |
21 | test.func1();
22 | test.func1();
23 | test.func2();
24 |
25 | expect(test.called).toEqual(1);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/projects/utils/src/lib/once.decorator.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * mark a method to be executed no more than once even if called several times
3 | */
4 | export function Once(name?: string): MethodDecorator {
5 | return function(target: Object,
6 | propertyKey: string | symbol,
7 | descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor {
8 |
9 | // Init
10 | name = name || propertyKey.toString();
11 | const originalMethod = descriptor.value;
12 |
13 | descriptor.value = function(...args: any[]) {
14 | if (!target[`_ngxQueue_${name}`]) {
15 | target[`_ngxQueue_${name}`] = true;
16 | return originalMethod.apply(this, args);
17 | }
18 | };
19 |
20 | return descriptor;
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/projects/utils/src/lib/queue.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import {Observable, Subscription, throwError, timer} from 'rxjs';
2 | import {concatMap, delay, tap} from 'rxjs/operators';
3 | import {Queue} from './queue.decorator';
4 |
5 | const WAIT_TIME = 50;
6 | const QUEUE_NUMBER = 2;
7 |
8 | class ClassTest {
9 | incr = 0;
10 |
11 | get waitObs(): Observable {
12 | return timer(WAIT_TIME);
13 | }
14 |
15 | @Queue(QUEUE_NUMBER)
16 | obsSuccessFunc(incr = 1): Subscription {
17 | return this.waitObs
18 | .subscribe(() => this.plus(incr));
19 | }
20 |
21 | @Queue(QUEUE_NUMBER)
22 | obsErrorFunc(incr = 1): Subscription {
23 | return this.waitObs.pipe(
24 | concatMap(() => throwError('error'))
25 | )
26 | .subscribe(() => {
27 | }, () => this.plus(incr));
28 | }
29 |
30 | @Queue(QUEUE_NUMBER)
31 | promiseSuccessFunc(incr = 1): Promise {
32 | return this.waitObs.toPromise().then(() => this.plus(incr));
33 | }
34 |
35 | @Queue(QUEUE_NUMBER)
36 | promiseErrorFunc(incr = 1): Promise {
37 | return this.promiseSuccessFunc().then(() => {
38 | throw new Error('error');
39 | });
40 | }
41 |
42 | @Queue(QUEUE_NUMBER)
43 | classicSuccessFunc(incr = 1): number {
44 | return this.plus(incr);
45 | }
46 |
47 | @Queue(QUEUE_NUMBER)
48 | classicErrorFunc(incr = 1): void {
49 | this.plus(incr);
50 | throw new Error('error');
51 | }
52 |
53 | private plus(n: number): number {
54 | this.incr += n;
55 | return this.incr;
56 | }
57 | }
58 |
59 | function testIt(classTest: ClassTest, fn: Function, done: DoneFn, firstPassValue: number = 2, secondPassValue: number = 3) {
60 | fn.apply(classTest);
61 | timer(WAIT_TIME / 2).subscribe(() => {
62 | fn.apply(classTest);
63 | fn.apply(classTest);
64 | });
65 |
66 | timer(WAIT_TIME * 3).pipe(
67 | tap(() => {
68 | expect(classTest.incr).toEqual(firstPassValue);
69 |
70 | fn.apply(classTest);
71 | }),
72 | delay(WAIT_TIME * 1.1)
73 | )
74 | .subscribe(() => {
75 | expect(classTest.incr).toEqual(secondPassValue);
76 | done();
77 | });
78 | }
79 |
80 | describe('QueueDecorator', () => {
81 | let klass: ClassTest;
82 |
83 | beforeEach(() => {
84 | klass = new ClassTest();
85 | });
86 |
87 | it('should works with observable success', (done: DoneFn) => {
88 | testIt(klass, klass.obsSuccessFunc, done);
89 | });
90 | it('should works with observable error', (done: DoneFn) => {
91 | testIt(klass, klass.obsErrorFunc, done);
92 | });
93 | it('should works with promise success', (done: DoneFn) => {
94 | testIt(klass, klass.promiseSuccessFunc, done);
95 | });
96 | it('should works with promise error', (done: DoneFn) => {
97 | testIt(klass, klass.promiseErrorFunc, done);
98 | });
99 | it('should works with function success', (done: DoneFn) => {
100 | testIt(klass, klass.classicSuccessFunc, done, 3, 4);
101 | });
102 | it('should works with function error', (done: DoneFn) => {
103 | testIt(klass, klass.classicErrorFunc, done, 3, 4);
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/projects/utils/src/lib/queue.decorator.ts:
--------------------------------------------------------------------------------
1 | import {Subscription} from 'rxjs';
2 | import {Deferred, isPromise} from './functions';
3 |
4 | export interface QueueData {
5 | queue: Function[];
6 | promise: Promise;
7 | }
8 |
9 | /**
10 | * Put the method call in a queue and wait for a Promise / Subscription / method execution
11 | * /!\ the method result is modified => Return a Promise
12 | */
13 | export function Queue(limit?: number, name?: string): MethodDecorator {
14 | return function(
15 | target: Object,
16 | propertyKey: string | symbol,
17 | descriptor: TypedPropertyDescriptor
18 | ): TypedPropertyDescriptor | void {
19 | // Init
20 | name = name || propertyKey.toString();
21 | const data: QueueData = (target[`_ngxQueue_${name}`] = target[`_ngxQueue_${name}`] || {
22 | queue: [],
23 | promise: Promise.resolve()
24 | });
25 | const originalMethod = descriptor.value;
26 |
27 | // Change method
28 | descriptor.value = function(...args: any[]) {
29 | // Push Future call
30 | const deferred: Deferred = new Deferred();
31 |
32 | // Ignore next call
33 | if (limit && data.queue.length >= limit) {
34 | deferred.resolve();
35 | } else {
36 | data.queue.push(() => {
37 | try {
38 | const result: any = originalMethod.apply(this, args);
39 |
40 | if (isPromise(result)) {
41 | result.then(r => deferred.resolve(r), e => deferred.reject(e));
42 | } else if (result instanceof Subscription) {
43 | result.add(() => deferred.resolve());
44 | } else {
45 | deferred.resolve(result);
46 | }
47 | } catch (e) {
48 | deferred.reject(e);
49 | }
50 |
51 | return deferred.promise;
52 | });
53 |
54 | // Queue
55 | data.promise = data.promise
56 | // Call
57 | .then(() => data.queue[0].apply(this))
58 | // Pop
59 | .then(() => data.queue.pop(), () => data.queue.pop());
60 | }
61 |
62 | // Return promise
63 | return deferred.promise;
64 | };
65 |
66 | return descriptor;
67 | };
68 | }
69 |
--------------------------------------------------------------------------------
/projects/utils/src/lib/wait.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import {Observable, Subscription, timer} from 'rxjs';
2 | import {delay, tap} from 'rxjs/operators';
3 | import {Wait} from './wait.decorator';
4 |
5 | const WAIT_TIME = 50;
6 |
7 | class ClassTest {
8 | incr = 0;
9 |
10 | get waitObs(): Observable {
11 | return timer(WAIT_TIME);
12 | }
13 |
14 | @Wait()
15 | obsSuccessFunc(incr = 1): Subscription {
16 | return this.waitObs
17 | .subscribe(() => this.plus(incr));
18 | }
19 |
20 | private plus(n: number): number {
21 | this.incr += n;
22 | return this.incr;
23 | }
24 | }
25 |
26 | function testIt(classTest: ClassTest, fn: Function, done: DoneFn, firstPassValue: number = 2, secondPassValue: number = 3) {
27 | fn.apply(classTest);
28 |
29 | timer(WAIT_TIME * 1.1).subscribe(() => {
30 | fn.apply(classTest);
31 | fn.apply(classTest);
32 | });
33 |
34 | timer(WAIT_TIME * 2.2).pipe(
35 | tap(() => {
36 | expect(classTest.incr).toEqual(firstPassValue);
37 |
38 | fn.apply(classTest);
39 | }),
40 | delay(WAIT_TIME * 1.1)
41 | ).subscribe(() => {
42 | expect(classTest.incr).toEqual(secondPassValue);
43 | done();
44 | });
45 | }
46 |
47 | describe('WaitDecorator', () => {
48 | let klass: ClassTest;
49 |
50 | beforeEach(() => {
51 | klass = new ClassTest();
52 | });
53 |
54 | it('should works with observable success', (done: DoneFn) => {
55 | testIt(klass, klass.obsSuccessFunc, done);
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/projects/utils/src/lib/wait.decorator.ts:
--------------------------------------------------------------------------------
1 | import {Subscription} from 'rxjs';
2 | import {Deferred, isPromise} from './functions';
3 |
4 | export interface WaitData {
5 | wait?: Function;
6 | promise: Promise;
7 | }
8 |
9 | /**
10 | * Wait for a Promise / Subscription before to be re-executed
11 | * /!\ the method result is modified => Return a Promise
12 | */
13 | export function Wait(name?: string): MethodDecorator {
14 | return function(
15 | target: Object,
16 | propertyKey: string | symbol,
17 | descriptor: TypedPropertyDescriptor
18 | ): TypedPropertyDescriptor | void {
19 | // Init
20 | name = name || propertyKey.toString();
21 | const data: WaitData = (target[`_ngxWait_${name}`] = target[`_ngxWait_${name}`] || {
22 | promise: Promise.resolve()
23 | });
24 | const originalMethod = descriptor.value;
25 |
26 | // Change method
27 | descriptor.value = function(...args: any[]) {
28 | // Push Future call
29 | const deferred: Deferred = new Deferred();
30 |
31 | // Ignore next call
32 | if (!data.wait) {
33 | data.wait = () => {
34 | try {
35 | const result: any = originalMethod.apply(this, args);
36 |
37 | if (isPromise(result)) {
38 | result.then(r => deferred.resolve(r), e => deferred.reject(e));
39 | } else if (result instanceof Subscription) {
40 | result.add(() => deferred.resolve());
41 | } else {
42 | deferred.resolve(result);
43 | }
44 | } catch (e) {
45 | deferred.reject(e);
46 | }
47 |
48 | return deferred.promise;
49 | };
50 |
51 | // Execute
52 | data.promise = data.promise
53 | // Call
54 | .then(() => data.wait.apply(this))
55 | // Pop
56 | .then(() => data.wait = null, () => data.wait = null);
57 | } else {
58 | deferred.resolve();
59 | }
60 |
61 | // Return promise
62 | return deferred.promise;
63 | };
64 |
65 | return descriptor;
66 | };
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/projects/utils/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes recent versions of Safari, Chrome (including
12 | * Opera), Edge on the desktop, and iOS and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/utils/src/public-api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of utils
3 | */
4 |
5 | export {Queue} from './lib/queue.decorator';
6 | export {Wait} from './lib/wait.decorator';
7 | export {Once} from './lib/once.decorator';
8 |
--------------------------------------------------------------------------------
/projects/utils/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | (id: string): T;
13 | keys(): string[];
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(),
21 | );
22 |
23 | // Then we find all the tests.
24 | const context = require.context('./', true, /\.spec\.ts$/);
25 | // And load the modules.
26 | context.keys().map(context);
27 |
--------------------------------------------------------------------------------
/projects/utils/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/lib",
5 | "target": "es2015",
6 | "declaration": true,
7 | "inlineSources": true,
8 | "types": [],
9 | "lib": [
10 | "dom",
11 | "es2018"
12 | ]
13 | },
14 | "angularCompilerOptions": {
15 | "skipTemplateCodegen": true,
16 | "strictMetadataEmit": true,
17 | "enableResourceInlining": true
18 | },
19 | "exclude": [
20 | "src/test.ts",
21 | "**/*.spec.ts"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/projects/utils/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.lib.json",
3 | "angularCompilerOptions": {
4 | "compilationMode": "partial"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/utils/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/publish.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -x
4 |
5 | # get current version
6 | PACKAGE_VERSION=$(cat package.json \
7 | | grep version \
8 | | head -1 \
9 | | awk -F: '{ print $2 }' \
10 | | sed 's/[",]//g' \
11 | | tr -d '[[:space:]]')
12 | PLACEHOLDER="0.0.0-PLACEHOLDER"
13 | TFILE="/tmp/out.tmp.$$"
14 |
15 | # change version & publish each lib
16 | for module in dist/*
17 | do
18 | sed "s/$PLACEHOLDER/$PACKAGE_VERSION/g" "$module/package.json" > $TFILE && mv $TFILE "$module/package.json"
19 | cp LICENSE "$module/LICENSE"
20 | npm publish --access public "./$module"
21 | done
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "downlevelIteration": true,
9 | "experimentalDecorators": true,
10 | "module": "esnext",
11 | "moduleResolution": "node",
12 | "importHelpers": true,
13 | "target": "es2015",
14 | "typeRoots": [
15 | "node_modules/@types"
16 | ],
17 | "lib": [
18 | "es2018",
19 | "dom"
20 | ],
21 | "paths": {
22 | "cache": [
23 | "dist/cache/cache",
24 | "dist/cache"
25 | ],
26 | "cookie": [
27 | "dist/cookie/cookie",
28 | "dist/cookie"
29 | ],
30 | "device": [
31 | "dist/device/device",
32 | "dist/device"
33 | ],
34 | "logger": [
35 | "dist/logger/logger",
36 | "dist/logger"
37 | ],
38 | "spring": [
39 | "dist/spring/spring",
40 | "dist/spring"
41 | ],
42 | "utils": [
43 | "dist/utils/utils",
44 | "dist/utils"
45 | ]
46 | }
47 | },
48 | "angularCompilerOptions": {
49 | "fullTemplateTypeCheck": true,
50 | "strictInjectionParameters": true
51 | }
52 | }
--------------------------------------------------------------------------------