├── .editorconfig ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── pr.yml │ └── release.yml ├── .gitignore ├── .releaserc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── angular.json ├── package-lock.json ├── package.json ├── prettier.config.js ├── projects ├── demo │ ├── .gitignore │ ├── angular.json │ ├── karma.conf.js │ ├── package.json │ ├── prerender.ts │ ├── server.ts │ ├── src │ │ ├── app │ │ │ ├── app.browser.module.ts │ │ │ ├── app.component.html │ │ │ ├── app.component.less │ │ │ ├── app.component.ts │ │ │ ├── app.routes.ts │ │ │ ├── app.server.module.ts │ │ │ └── clock.component.ts │ │ ├── assets │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── browserconfig.xml │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── logo.svg │ │ │ ├── mstile-150x150.png │ │ │ ├── safari-pinned-tab.svg │ │ │ ├── site.webmanifest │ │ │ └── web-api.svg │ │ ├── index.html │ │ ├── main.browser.ts │ │ ├── main.server.ts │ │ ├── polyfills.ts │ │ ├── styles.css │ │ └── typings.d.ts │ ├── static.paths.ts │ ├── tsconfig.demo.json │ ├── tsconfig.json │ ├── tsconfig.server.json │ ├── tsconfig.spec.json │ ├── tsconfig.ssr.json │ └── tslint.json └── workers │ ├── LICENSE │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── public-api.ts │ ├── test.ts │ └── worker │ │ ├── classes │ │ ├── web-worker.spec.ts │ │ └── web-worker.ts │ │ ├── consts │ │ └── worker-fn-template.ts │ │ ├── operators │ │ └── to-data.ts │ │ ├── pipes │ │ ├── worker.pipe.spec.ts │ │ └── worker.pipe.ts │ │ ├── types │ │ ├── typed-message-event.ts │ │ └── worker-function.ts │ │ └── worker.module.ts │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── scripts ├── postbuild.js └── syncVersions.js ├── tsconfig.json └── tslint.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 = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | 2 | # ================================================================================== 3 | # ================================================================================== 4 | # @ng-web-apis/workers codeowners 5 | # ================================================================================== 6 | # ================================================================================== 7 | # 8 | # Configuration of code ownership and review approvals for the @ng-web-apis/workers repo. 9 | # 10 | # More info: https://help.github.com/articles/about-codeowners/ 11 | # 12 | 13 | * @IKatsuba 14 | # will be requested for review when someone opens a pull request 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | open_collective: ng-web-apis 4 | issuehunt: ng-web-apis 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐞 Bug report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: '' 6 | assignee: IKatsuba 7 | --- 8 | 9 | # 🐞 Bug report 10 | 11 | ### Description 12 | 13 | 14 | 15 | ### Reproduction 16 | 17 | 18 | 19 | http://www.stackblitz.com/... 20 | 21 | ### Expected behavior 22 | 23 | 24 | 25 | ### Versions 26 | 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Angular [e.g. 8] 30 | 31 | ### Additional context 32 | 33 | 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE]' 5 | labels: '' 6 | assignee: IKatsuba 7 | --- 8 | 9 | # 🚀 Feature request 10 | 11 | ### Is your feature request related to a problem? 12 | 13 | 14 | I'm always frustrated when... 15 | 16 | ### Describe the solution you'd like 17 | 18 | 19 | 20 | 21 | ### Describe alternatives you've considered 22 | 23 | 24 | 25 | 26 | ### Additional context 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | 3 | Please check if your PR fulfills the following requirements: 4 | 5 | - [ ] The commit message follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) 6 | - [ ] Tests for the changes have been added (for bug fixes / features) 7 | - [ ] Docs have been added / updated (for bug fixes / features) 8 | 9 | ## PR Type 10 | 11 | What kind of change does this PR introduce? 12 | 13 | 14 | 15 | - [ ] Bugfix 16 | - [ ] Feature 17 | - [ ] Refactoring (no functional changes, no api changes) 18 | - [ ] Other... Please describe: 19 | 20 | ## What is the current behavior? 21 | 22 | 23 | 24 | Issue Number: N/A 25 | 26 | ## What is the new behavior? 27 | 28 | ## Does this PR introduce a breaking change? 29 | 30 | - [ ] Yes 31 | - [ ] No 32 | 33 | 34 | 35 | ## Other information 36 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - next 8 | - beta 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Use Node.js 12.x 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: 12.x 19 | - name: Build 20 | run: | 21 | npm ci 22 | npm run build 23 | npm run build:demo:client 24 | env: 25 | CI: true 26 | lint: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v2 30 | - name: Use Node.js 12.x 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: 12.x 34 | - name: Lint 35 | run: | 36 | npm ci 37 | npm run lint 38 | env: 39 | CI: true 40 | test: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v2 44 | - name: Use Node.js 12.x 45 | uses: actions/setup-node@v1 46 | with: 47 | node-version: 12.x 48 | - name: Test 49 | run: | 50 | npm ci 51 | npm test 52 | env: 53 | CI: true 54 | - name: Coveralls 55 | uses: coverallsapp/github-action@master 56 | with: 57 | github-token : ${{secrets.GITHUB_TOKEN}} 58 | path-to-lcov: ./coverage/workers/lcov.info 59 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build and Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - next 8 | - beta 9 | 10 | jobs: 11 | release: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Use Node.js 12.x 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: 12.x 19 | - name: npm install 20 | run: | 21 | npm ci 22 | env: 23 | CI: true 24 | - name: Release 25 | run: | 26 | npm run build 27 | npm run release 28 | env: 29 | CI: true 30 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 31 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled schematics 2 | schematics/library-starter/*.js 3 | schematics/library-starter/*.js.map 4 | schematics/library-starter/*.d.ts 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /out-tsc 10 | # Only exists if Bazel was run 11 | /bazel-out 12 | 13 | # dependencies 14 | /node_modules 15 | 16 | # profiling files 17 | chrome-profiler-events.json 18 | speed-measure-plugin.json 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | .history/* 36 | 37 | # misc 38 | /.sass-cache 39 | /connect.lock 40 | /coverage 41 | /libpeerconnection.log 42 | npm-debug.log 43 | yarn-error.log 44 | testem.log 45 | /typings 46 | 47 | # System Files 48 | .DS_Store 49 | Thumbs.db 50 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | [ 6 | "@semantic-release/npm", 7 | { 8 | "pkgRoot": "./dist/workers" 9 | } 10 | ], 11 | "@semantic-release/github" 12 | ] 13 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project at ng.web.apis@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | > Thank you for considering contributing to our project. Your help if very welcome! 4 | 5 | When contributing, it's better to first discuss the change you wish to make via issue, 6 | email, or any other method with the owners of this repository before making a change. 7 | 8 | All members of our community are expected to follow our [Code of Conduct](CODE_OF_CONDUCT.md). 9 | Please make sure you are welcoming and friendly in all of our spaces. 10 | 11 | ## Getting started 12 | 13 | In order to make your contribution please make a fork of the repository. After you've pulled 14 | the code, follow these steps to kick start the development: 15 | 16 | 1. Run `npm ci` to install dependencies 17 | 2. Run `npm start` to launch demo project where you could test your changes 18 | 3. Use following commands to ensure code quality 19 | 20 | ``` 21 | npm run lint 22 | npm run build 23 | npm run test 24 | ``` 25 | 26 | ## Pull Request Process 27 | 28 | 1. We follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) 29 | in our commit messages, i.e. `feat(core): improve typing` 30 | 2. Update [README.md](README.md) to reflect changes related to public API and everything relevant 31 | 3. Make sure you cover all code changes with unit tests 32 | 4. When you are ready, create Pull Request of your fork into original repository 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexander Inkin 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 | ___ 2 | ___ 3 | **Attention!** This repository is archived and the library has been moved to [tinkoff/ng-web-apis](https://github.com/Tinkoff/ng-web-apis) monorepository 4 | ___ 5 | ___ 6 | # ![ng-web-apis logo](projects/demo/src/assets/logo.svg) Web Workers API for Angular 7 | 8 | > Part of [Web APIs for Angular](https://ng-web-apis.github.io/) 9 | 10 | [![npm version](https://img.shields.io/npm/v/@ng-web-apis/workers.svg)](https://npmjs.com/package/@ng-web-apis/workers) 11 | [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@ng-web-apis/workers)](https://bundlephobia.com/result?p=@ng-web-apis/workers) 12 | [![Build and Release](https://github.com/ng-web-apis/workers/workflows/Build%20and%20Release/badge.svg?workflow=build)](https://github.com/ng-web-apis/workers/actions?query=workflow%3ABuild%20and%20Release) 13 | [![Coveralls github](https://img.shields.io/coveralls/github/ng-web-apis/workers)](https://coveralls.io/github/ng-web-apis/workers?branch=master) 14 | [![angular-open-source-starter](https://img.shields.io/badge/made%20with-angular--open--source--starter-d81676?logo=angular)](https://github.com/TinkoffCreditSystems/angular-open-source-starter) 15 | 16 | This is a library for comfortable use of 17 | [Web Workers API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) 18 | with Angular. 19 | 20 | ## Install 21 | 22 | If you do not have [@ng-web-apis/common](https://github.com/ng-web-apis/common): 23 | 24 | ``` 25 | npm i @ng-web-apis/common 26 | ``` 27 | 28 | Now install the package: 29 | 30 | ``` 31 | npm i @ng-web-apis/workers 32 | ``` 33 | 34 | ## How it use 35 | 36 | Create a worker and use it in a template with `AsyncPipe`: 37 | 38 | ```typescript 39 | import {WebWorker} from '@ng-web-apis/workers'; 40 | 41 | function compute(data: number): number { 42 | return data ** 2; 43 | } 44 | 45 | @Component({ 46 | template: ` 47 | Computed Result: 48 | {{ event.data }} 49 |
50 | 51 | 52 |
53 | `, 54 | }) 55 | class SomeComponent { 56 | readonly worker = WebWorker.fromFunction(compute); 57 | } 58 | ``` 59 | 60 | To get data from the worker event, use the toData operator 61 | 62 | ```typescript 63 | import {toData, WebWorker} from '@ng-web-apis/workers'; 64 | 65 | function compute(data: number): number { 66 | return data ** 2; 67 | } 68 | 69 | @Component({ 70 | template: ` 71 | Computed Result: {{ workerData$ | async }} 72 |
73 | 74 | 75 |
76 | `, 77 | }) 78 | class SomeComponent { 79 | readonly worker = WebWorker.fromFunction(compute); 80 | readonly workerData$ = this.worker.pipe(toData()); 81 | } 82 | ``` 83 | 84 | It's the same with `WorkerPipe` only: 85 | 86 | ```typescript 87 | import {WorkerModule} from '@ng-web-apis/workers'; 88 | import {NgModule} from '@angular/core'; 89 | 90 | @NgModule({ 91 | imports: [WorkerModule], 92 | declarations: [SomeComponent], 93 | }) 94 | class SomeModule {} 95 | ``` 96 | 97 | ```typescript 98 | import {WorkerExecutor, WebWorker} from '@ng-web-apis/workers'; 99 | import {FormControl} from '@angular/forms'; 100 | 101 | @Component({ 102 | template: ` 103 | Computed Result: {{ value | waWorker: changeData | async }} 104 | 105 | 106 | `, 107 | }) 108 | class SomeComponent { 109 | value: string; 110 | 111 | changeData(data: string): string { 112 | return `${data} (changed)`; 113 | } 114 | } 115 | ``` 116 | 117 | ## See also 118 | 119 | Other [Web APIs for Angular](https://ng-web-apis.github.io/) by [@ng-web-apis](https://github.com/ng-web-apis) 120 | 121 | ## Open-source 122 | 123 | Do you also want to open-source something, but hate the collateral work? 124 | Check out this [Angular Open-source Library Starter](https://github.com/TinkoffCreditSystems/angular-open-source-starter) 125 | we’ve created for our projects. It got you covered on continuous integration, 126 | pre-commit checks, linting, versioning + changelog, code coverage and all that jazz. 127 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "demo": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "projects/demo", 10 | "sourceRoot": "projects/demo/src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "baseHref": "/workers/", 17 | "deployUrl": "/workers/", 18 | "outputPath": "dist/demo/browser", 19 | "index": "projects/demo/src/index.html", 20 | "main": "projects/demo/src/main.browser.ts", 21 | "polyfills": "projects/demo/src/polyfills.ts", 22 | "tsConfig": "tsconfig.json", 23 | "aot": false, 24 | "assets": [ 25 | { 26 | "glob": "**/*", 27 | "input": "projects/demo/src/assets/", 28 | "output": "./assets/" 29 | }, 30 | "projects/demo/src/favicon.ico" 31 | ], 32 | "styles": ["projects/demo/src/styles.css"], 33 | "scripts": [] 34 | }, 35 | "configurations": { 36 | "production": { 37 | "baseHref": "/workers/", 38 | "deployUrl": "/workers/", 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "aot": true, 45 | "extractLicenses": true, 46 | "vendorChunk": false, 47 | "buildOptimizer": true, 48 | "budgets": [ 49 | { 50 | "type": "initial", 51 | "maximumWarning": "2mb", 52 | "maximumError": "5mb" 53 | } 54 | ] 55 | } 56 | } 57 | }, 58 | "serve": { 59 | "builder": "@angular-devkit/build-angular:dev-server", 60 | "options": { 61 | "browserTarget": "demo:build" 62 | }, 63 | "configurations": { 64 | "production": { 65 | "browserTarget": "demo:build:production" 66 | } 67 | } 68 | }, 69 | "server": { 70 | "builder": "@angular-devkit/build-angular:server", 71 | "options": { 72 | "outputPath": "dist/demo/server", 73 | "main": "projects/demo/src/main.server.ts", 74 | "tsConfig": "projects/demo/tsconfig.server.json" 75 | } 76 | }, 77 | "lint": { 78 | "builder": "@angular-devkit/build-angular:tslint", 79 | "options": { 80 | "tsConfig": ["tsconfig.json"], 81 | "exclude": ["**/node_modules/**"] 82 | } 83 | } 84 | } 85 | }, 86 | "workers": { 87 | "projectType": "library", 88 | "root": "projects/workers", 89 | "sourceRoot": "projects/workers/src", 90 | "architect": { 91 | "build": { 92 | "builder": "@angular-devkit/build-ng-packagr:build", 93 | "options": { 94 | "tsConfig": "projects/workers/tsconfig.lib.json", 95 | "project": "projects/workers/ng-package.json" 96 | } 97 | }, 98 | "test": { 99 | "builder": "@angular-devkit/build-angular:karma", 100 | "options": { 101 | "main": "projects/workers/src/test.ts", 102 | "tsConfig": "projects/workers/tsconfig.spec.json", 103 | "karmaConfig": "projects/workers/karma.conf.js", 104 | "codeCoverage": true, 105 | "browsers": "ChromeHeadless" 106 | } 107 | }, 108 | "lint": { 109 | "builder": "@angular-devkit/build-angular:tslint", 110 | "options": { 111 | "tsConfig": [ 112 | "projects/workers/tsconfig.lib.json", 113 | "projects/workers/tsconfig.spec.json" 114 | ], 115 | "exclude": ["**/node_modules/**"] 116 | } 117 | } 118 | } 119 | } 120 | }, 121 | "defaultProject": "workers" 122 | } 123 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ng-web-apis/workers", 3 | "version": "0.0.0-development", 4 | "description": "A library for comfortable use of Web Workers API in Angular", 5 | "keywords": ["angular", "ng", "worker", "web", "service", "shared"], 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "start:aot": "ng serve --configuration production", 10 | "start:ssr": "npm run build:demo:ssr && npm run serve:ssr", 11 | "start:prerender": "npm run build:demo:prerender && npm run serve:prerender", 12 | "build": "ng build", 13 | "postbuild": "node scripts/postbuild.js", 14 | "build:demo:client": "ng run demo:build", 15 | "build:demo:server": "ng run demo:server", 16 | "build:demo:ssr": "npm run build:demo:client && npm run build:demo:server && npm run compile:server", 17 | "build:demo:prerender": "npm run build:demo:ssr && npm run generate:prerender", 18 | "serve:ssr": "node dist/demo/ssr/server", 19 | "serve:prerender": "cd dist/demo/browser && http-server", 20 | "compile:server": "tsc -p ./projects/demo/tsconfig.ssr.json", 21 | "generate:prerender": "node dist/demo/ssr/prerender", 22 | "test": "ng test", 23 | "lint": "ng lint", 24 | "typecheck": "tsc --noEmit --skipLibCheck", 25 | "release": "semantic-release", 26 | "publish": "npm run build && npm publish ./dist/workers" 27 | }, 28 | "license": "MIT", 29 | "author": { 30 | "name": "Igor Katsuba", 31 | "email": "katsuba.igor@gmail.com" 32 | }, 33 | "contributors": [ 34 | "Alexander Inkin ", 35 | "Roman Sedov <79601794011@ya.ru>" 36 | ], 37 | "repository": "https://github.com/ng-web-apis/workers", 38 | "bugs": "https://github.com/ng-web-apis/workers/issues", 39 | "homepage": "https://github.com/ng-web-apis/workers#README", 40 | "dependencies": { 41 | "@angular/animations": "^7.2.15", 42 | "@angular/common": "^7.2.15", 43 | "@angular/compiler": "^7.2.15", 44 | "@angular/core": "^7.2.15", 45 | "@angular/forms": "^7.2.15", 46 | "@angular/http": "^7.2.15", 47 | "@angular/platform-browser": "^7.2.15", 48 | "@angular/platform-browser-dynamic": "^7.2.15", 49 | "@angular/platform-server": "^7.2.15", 50 | "@angular/router": "^7.2.15", 51 | "@ng-web-apis/common": "^1.1.0", 52 | "@nguniversal/common": "^7.1.1", 53 | "@nguniversal/express-engine": "^7.1.1", 54 | "@nguniversal/module-map-ngfactory-loader": "^7.1.1", 55 | "core-js": "^2.5.4", 56 | "rxjs": "^6.5.2", 57 | "tslib": "^1.10.0", 58 | "zone.js": "~0.8.29" 59 | }, 60 | "devDependencies": { 61 | "@angular-devkit/build-angular": "~0.13.9", 62 | "@angular-devkit/build-ng-packagr": "~0.13.9", 63 | "@angular-devkit/core": "^7.3.9", 64 | "@angular/cli": "^7.3.9", 65 | "@angular/compiler-cli": "^7.2.15", 66 | "@angular/language-service": "^7.2.15", 67 | "@semantic-release/commit-analyzer": "^8.0.1", 68 | "@semantic-release/github": "^7.2.0", 69 | "@semantic-release/npm": "^7.0.8", 70 | "@semantic-release/release-notes-generator": "^9.0.1", 71 | "@tinkoff/linters": "^0.4.0", 72 | "@types/express": "4.16.1", 73 | "@types/express-serve-static-core": "4.16.2", 74 | "@types/jasmine": "^3.3.16", 75 | "@types/jasminewd2": "^2.0.6", 76 | "@types/node": "~8.9.4", 77 | "codelyzer": "^5.1.0", 78 | "coveralls": "^3.0.5", 79 | "ecstatic": "^4.1.2", 80 | "express": "^4.17.1", 81 | "http-server": "^0.11.1", 82 | "husky": "^3.0.2", 83 | "jasmine-core": "~3.5.0", 84 | "jasmine-spec-reporter": "~4.2.1", 85 | "karma": "^4.2.0", 86 | "karma-chrome-launcher": "^3.0.0", 87 | "karma-coverage-istanbul-reporter": "^2.1.0", 88 | "karma-jasmine": "~2.0.1", 89 | "karma-jasmine-html-reporter": "^1.4.2", 90 | "lint-staged": "^9.2.1", 91 | "lodash": "^4.17.15", 92 | "ng-packagr": "4.7.1", 93 | "prettier": "^1.18.2", 94 | "semantic-release": "^17.2.4", 95 | "ts-node": "^8.3.0", 96 | "tsickle": "0.34.0", 97 | "tslint": "^5.18.0", 98 | "tsutils": "^3.17.1", 99 | "typescript": "~3.2.2" 100 | }, 101 | "husky": { 102 | "hooks": { 103 | "pre-commit": "lint-staged && npm run typecheck" 104 | } 105 | }, 106 | "lint-staged": { 107 | "*.{js,ts,html,md,less,json}": ["prettier --write", "git add"], 108 | "*.ts": "tslint" 109 | }, 110 | "engines": { 111 | "node": ">= 10", 112 | "npm": ">= 3" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require('@tinkoff/linters/prettier/prettier.config'), 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | /out-tsc 5 | # Only exists if Bazel was run 6 | /bazel-out 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | .history/* 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /projects/demo/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "demo": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/demo", 17 | "index": "src/index.html", 18 | "main": "src/main.browser.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.demo.json", 21 | "aot": false, 22 | "assets": [ 23 | { 24 | "glob": "**/*", 25 | "input": "projects/demo/src/assets/", 26 | "output": "./assets/" 27 | }, 28 | "src/favicon.ico" 29 | ], 30 | "styles": ["src/styles.css"], 31 | "scripts": [] 32 | }, 33 | "configurations": { 34 | "production": { 35 | "optimization": true, 36 | "outputHashing": "all", 37 | "sourceMap": false, 38 | "extractCss": true, 39 | "namedChunks": false, 40 | "aot": true, 41 | "extractLicenses": true, 42 | "vendorChunk": false, 43 | "buildOptimizer": true 44 | } 45 | } 46 | }, 47 | "serve": { 48 | "builder": "@angular-devkit/build-angular:dev-server", 49 | "options": { 50 | "browserTarget": "demo:build" 51 | }, 52 | "configurations": { 53 | "production": { 54 | "browserTarget": "demo:build:production" 55 | } 56 | } 57 | } 58 | } 59 | } 60 | }, 61 | "defaultProject": "demo" 62 | } 63 | -------------------------------------------------------------------------------- /projects/demo/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-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma'), 14 | ], 15 | client: { 16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/demo'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true, 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['ChromeHeadless'], 29 | singleRun: true, 30 | customLaunchers: { 31 | ChromeHeadless: { 32 | base: 'Chrome', 33 | flags: [ 34 | '--no-sandbox', 35 | '--headless', 36 | '--disable-gpu', 37 | '--remote-debugging-port=9222', 38 | ], 39 | }, 40 | }, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /projects/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build" 9 | }, 10 | "dependencies": { 11 | "@angular/common": "^8.2.4", 12 | "@angular/compiler": "^8.2.4", 13 | "@angular/core": "^8.2.4", 14 | "@angular/forms": "^8.2.4", 15 | "@angular/platform-browser": "^8.2.4", 16 | "@angular/platform-browser-dynamic": "8.2.0", 17 | "@angular/router": "8.2.0", 18 | "@ng-web-apis/workers": "latest", 19 | "@ng-web-apis/common": "latest", 20 | "core-js": "2.6.9", 21 | "rxjs": "6.5.2", 22 | "zone.js": "0.9.1" 23 | }, 24 | "devDependencies": { 25 | "@angular-devkit/build-angular": "^0.803.2", 26 | "@angular/cli": "^8.3.2", 27 | "@angular/compiler-cli": "^8.2.4", 28 | "@angular/language-service": "^8.2.4", 29 | "@types/node": "~8.9.4", 30 | "ts-node": "~7.0.0", 31 | "tslint": "~5.11.0", 32 | "typescript": "~3.4.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /projects/demo/prerender.ts: -------------------------------------------------------------------------------- 1 | // Load zone.js for the server. 2 | import 'reflect-metadata'; 3 | import 'zone.js/dist/zone-node'; 4 | 5 | import {APP_BASE_HREF} from '@angular/common'; 6 | import {enableProdMode} from '@angular/core'; 7 | import {renderModuleFactory} from '@angular/platform-server'; 8 | import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader'; 9 | import {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs'; 10 | import {join} from 'path'; 11 | import {PRERENDERED_ROUTES} from './static.paths'; 12 | 13 | enableProdMode(); 14 | 15 | const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('../server/main'); 16 | const DEMO_FOLDER = join(process.cwd(), 'dist', 'demo', 'browser'); 17 | const index = readFileSync( 18 | join(process.cwd(), 'dist', 'demo', 'browser', 'index.html'), 19 | 'utf8', 20 | ); 21 | const localFallback = 'http://localhost:4200/'; 22 | 23 | let previousRender = Promise.resolve(); 24 | 25 | // Iterate each route path 26 | PRERENDERED_ROUTES.forEach(route => { 27 | const fullPath = join(DEMO_FOLDER, route); 28 | 29 | // Make sure the directory structure is there 30 | if (!existsSync(fullPath)) { 31 | mkdirSync(fullPath); 32 | } 33 | 34 | // Writes rendered HTML to index.html, replacing the file if it already exists. 35 | previousRender = previousRender 36 | .then(() => 37 | renderModuleFactory(AppServerModuleNgFactory, { 38 | document: index, 39 | url: route, 40 | extraProviders: [ 41 | provideModuleMap(LAZY_MODULE_MAP), 42 | { 43 | provide: APP_BASE_HREF, 44 | useValue: process.env.ORIGIN || localFallback, 45 | }, 46 | ], 47 | }), 48 | ) 49 | .then(html => writeFileSync(join(fullPath, 'index.html'), html)); 50 | }); 51 | -------------------------------------------------------------------------------- /projects/demo/server.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-console */ 2 | import 'reflect-metadata'; 3 | import 'zone.js/dist/zone-node'; 4 | 5 | import {enableProdMode} from '@angular/core'; 6 | import {ngExpressEngine} from '@nguniversal/express-engine'; 7 | import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader'; 8 | import * as express from 'express'; 9 | import {join} from 'path'; 10 | 11 | enableProdMode(); 12 | 13 | const app = express(); 14 | const PORT = process.env.PORT || 3333; 15 | const DIST_FOLDER = join(process.cwd(), 'dist'); 16 | const DEMO_FOLDER = join(DIST_FOLDER, 'demo', 'browser'); 17 | const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('../server/main'); 18 | 19 | app.engine( 20 | 'html', 21 | ngExpressEngine({ 22 | bootstrap: AppServerModuleNgFactory, 23 | providers: [provideModuleMap(LAZY_MODULE_MAP)], 24 | }), 25 | ); 26 | 27 | app.set('view engine', 'html'); 28 | app.set('views', DEMO_FOLDER); 29 | 30 | // Example Express Rest API endpoints 31 | // app.get('/api/**', (req, res) => { }); 32 | 33 | // Server static files from /browser 34 | app.get( 35 | '*.*', 36 | express.static(DEMO_FOLDER, { 37 | maxAge: '1y', 38 | }), 39 | ); 40 | 41 | // All regular routes use the Universal engine 42 | app.get('*', (req, res) => { 43 | // Add information on current browser and location 44 | addToGlobal( 45 | 'location', 46 | new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`), 47 | ); 48 | addToGlobal('navigator', {userAgent: req.get('user-agent')}); 49 | 50 | res.render('index', {req}); 51 | }); 52 | 53 | // Start up the Node server 54 | app.listen(PORT, () => { 55 | console.log(`Node Express server listening on http://localhost:${PORT}`); 56 | }); 57 | 58 | function addToGlobal(key: string, value: any) { 59 | (global as any)[key] = value; 60 | } 61 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.browser.module.ts: -------------------------------------------------------------------------------- 1 | import { 2 | APP_BASE_HREF, 3 | CommonModule, 4 | LocationStrategy, 5 | PathLocationStrategy, 6 | } from '@angular/common'; 7 | import {NgModule} from '@angular/core'; 8 | import {FormsModule} from '@angular/forms'; 9 | import {BrowserModule} from '@angular/platform-browser'; 10 | import {WorkerModule} from '@ng-web-apis/workers'; 11 | import {AppComponent} from './app.component'; 12 | import {AppRoutingModule} from './app.routes'; 13 | import {ClockComponent} from './clock.component'; 14 | 15 | @NgModule({ 16 | bootstrap: [AppComponent], 17 | imports: [ 18 | CommonModule, 19 | FormsModule, 20 | BrowserModule.withServerTransition({appId: 'demo'}), 21 | AppRoutingModule, 22 | WorkerModule, 23 | ], 24 | declarations: [AppComponent, ClockComponent], 25 | providers: [ 26 | { 27 | provide: LocationStrategy, 28 | useClass: PathLocationStrategy, 29 | }, 30 | { 31 | provide: APP_BASE_HREF, 32 | useValue: '', 33 | }, 34 | ], 35 | }) 36 | export class AppBrowserModule {} 37 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |

Execution time: {{ workerData$ | async }}

6 |
7 |
8 | 9 |

Execution time: {{ result$ | async }}

10 |
11 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.less: -------------------------------------------------------------------------------- 1 | :host { 2 | perspective: 150vw; 3 | user-select: none; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .example { 9 | min-width: 360px; 10 | border-top: 1px solid gainsboro; 11 | margin-top: 16px; 12 | padding-top: 16px; 13 | } 14 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {ChangeDetectionStrategy, Component} from '@angular/core'; 2 | import {toData, WebWorker} from '@ng-web-apis/workers'; 3 | import {Subject} from 'rxjs'; 4 | import {map} from 'rxjs/operators'; 5 | 6 | @Component({ 7 | selector: 'main', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.less'], 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | }) 12 | export class AppComponent { 13 | readonly workerThread = WebWorker.fromFunction(this.startCompute); 14 | readonly workerData$ = this.workerThread.pipe(toData()); 15 | readonly emitter = new Subject(); 16 | readonly result$ = this.emitter.pipe(map(this.startCompute)); 17 | 18 | startCompute(): number { 19 | const start = performance.now(); 20 | 21 | Array.from({length: 16000}).forEach((_, index) => 22 | Array.from({length: index}).reduce((sum: number) => sum + 1, 0), 23 | ); 24 | 25 | return performance.now() - start; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {RouterModule} from '@angular/router'; 3 | import {AppComponent} from './app.component'; 4 | 5 | export const appRoutes = [ 6 | { 7 | path: '**', 8 | component: AppComponent, 9 | }, 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forRoot(appRoutes)], 14 | exports: [RouterModule], 15 | }) 16 | export class AppRoutingModule {} 17 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {ServerModule} from '@angular/platform-server'; 3 | import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader'; 4 | import {AppBrowserModule} from './app.browser.module'; 5 | import {AppComponent} from './app.component'; 6 | 7 | @NgModule({ 8 | imports: [AppBrowserModule, ServerModule, ModuleMapLoaderModule], 9 | bootstrap: [AppComponent], 10 | }) 11 | export class AppServerModule {} 12 | -------------------------------------------------------------------------------- /projects/demo/src/app/clock.component.ts: -------------------------------------------------------------------------------- 1 | import {ChangeDetectionStrategy, Component} from '@angular/core'; 2 | import {Observable, timer} from 'rxjs'; 3 | import {map} from 'rxjs/operators'; 4 | 5 | @Component({ 6 | selector: 'app-clock', 7 | template: ` 8 | {{ date$ | async | date: 'mediumTime' }} 9 | `, 10 | changeDetection: ChangeDetectionStrategy.OnPush, 11 | }) 12 | export class ClockComponent { 13 | readonly date$: Observable = timer(0, 1000).pipe(map(() => Date.now())); 14 | } 15 | -------------------------------------------------------------------------------- /projects/demo/src/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng-web-apis/workers/b35f39d9a0ede91ade41a5b4f1745cfbff7ba8ea/projects/demo/src/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /projects/demo/src/assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng-web-apis/workers/b35f39d9a0ede91ade41a5b4f1745cfbff7ba8ea/projects/demo/src/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /projects/demo/src/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng-web-apis/workers/b35f39d9a0ede91ade41a5b4f1745cfbff7ba8ea/projects/demo/src/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /projects/demo/src/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /projects/demo/src/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng-web-apis/workers/b35f39d9a0ede91ade41a5b4f1745cfbff7ba8ea/projects/demo/src/assets/favicon-16x16.png -------------------------------------------------------------------------------- /projects/demo/src/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng-web-apis/workers/b35f39d9a0ede91ade41a5b4f1745cfbff7ba8ea/projects/demo/src/assets/favicon-32x32.png -------------------------------------------------------------------------------- /projects/demo/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng-web-apis/workers/b35f39d9a0ede91ade41a5b4f1745cfbff7ba8ea/projects/demo/src/assets/favicon.ico -------------------------------------------------------------------------------- /projects/demo/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | background 6 | 7 | 8 | 9 | 10 | 11 | 12 | Layer 1 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /projects/demo/src/assets/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng-web-apis/workers/b35f39d9a0ede91ade41a5b4f1745cfbff7ba8ea/projects/demo/src/assets/mstile-150x150.png -------------------------------------------------------------------------------- /projects/demo/src/assets/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /projects/demo/src/assets/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/workers/assets/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/workers/assets/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /projects/demo/src/assets/web-api.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | background 6 | 7 | 8 | 9 | 10 | 11 | 12 | Layer 1 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /projects/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Web Workers API for Angular 4 | 9 | 15 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 43 |
44 |

Web Workers API for Angular

45 | Part of 46 | 47 | Web APIs logo 55 | Web APIs for Angular 56 | 57 |
58 |
59 |
loading
60 |
61 | Get it here: 62 | GitHub | 63 | NPM 64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /projects/demo/src/main.browser.ts: -------------------------------------------------------------------------------- 1 | import './polyfills'; 2 | 3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; 4 | import {AppBrowserModule} from './app/app.browser.module'; 5 | 6 | platformBrowserDynamic() 7 | .bootstrapModule(AppBrowserModule) 8 | .then(ref => { 9 | const windowRef: any = window; 10 | 11 | // Ensure Angular destroys itself on hot reloads for Stackblitz 12 | if (windowRef['ngRef']) { 13 | windowRef['ngRef'].destroy(); 14 | } 15 | 16 | windowRef['ngRef'] = ref; 17 | }) 18 | .catch(err => console.error(err)); 19 | -------------------------------------------------------------------------------- /projects/demo/src/main.server.ts: -------------------------------------------------------------------------------- 1 | export {AppServerModule} from './app/app.server.module'; 2 | -------------------------------------------------------------------------------- /projects/demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/es6/array'; 2 | import 'core-js/es6/date'; 3 | import 'core-js/es6/function'; 4 | import 'core-js/es6/map'; 5 | import 'core-js/es6/math'; 6 | import 'core-js/es6/number'; 7 | import 'core-js/es6/object'; 8 | import 'core-js/es6/parse-float'; 9 | import 'core-js/es6/parse-int'; 10 | import 'core-js/es6/regexp'; 11 | import 'core-js/es6/set'; 12 | import 'core-js/es6/string'; 13 | import 'core-js/es6/symbol'; 14 | import 'core-js/es6/weak-map'; 15 | 16 | import 'core-js/es6/reflect'; 17 | import 'core-js/es7/reflect'; 18 | import 'zone.js/dist/zone'; 19 | -------------------------------------------------------------------------------- /projects/demo/src/styles.css: -------------------------------------------------------------------------------- 1 | /* Base demo styles */ 2 | body, 3 | html { 4 | display: flex; 5 | flex-direction: column; 6 | margin: 0; 7 | height: 100%; 8 | font-family: Roboto, 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 9 | 'Lucida Grande', sans-serif; 10 | color: #444; 11 | } 12 | 13 | header { 14 | display: flex; 15 | width: 100%; 16 | max-width: 800px; 17 | margin: 0 auto; 18 | padding: 40px 10px; 19 | box-sizing: border-box; 20 | border-bottom: 1px solid gainsboro; 21 | } 22 | 23 | main { 24 | flex: 1; 25 | display: flex; 26 | justify-content: center; 27 | padding: 40px 0; 28 | } 29 | 30 | footer { 31 | padding: 16px; 32 | font-size: 12px; 33 | border-top: 1px solid gainsboro; 34 | text-align: center; 35 | } 36 | 37 | a { 38 | color: #1976D2; 39 | text-decoration: none; 40 | } 41 | 42 | .logo { 43 | margin-right: 20px; 44 | } 45 | -------------------------------------------------------------------------------- /projects/demo/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*'; 2 | -------------------------------------------------------------------------------- /projects/demo/static.paths.ts: -------------------------------------------------------------------------------- 1 | import {appRoutes} from './src/app/app.routes'; 2 | 3 | export const PRERENDERED_ROUTES: ReadonlyArray = appRoutes 4 | .map(route => route.path || '') 5 | .filter(path => path && path !== '**'); 6 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.demo.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "typeRoots": [], 6 | "paths": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | }, 6 | "angularCompilerOptions": { 7 | "entryModule": "src/app/app.server.module#AppServerModule" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["src/test.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.ssr.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/demo/ssr", 5 | "module": "commonjs" 6 | }, 7 | "include": ["server.ts", "prerender.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /projects/demo/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "no-console": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /projects/workers/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexander Inkin 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 | -------------------------------------------------------------------------------- /projects/workers/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-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma'), 14 | ], 15 | client: { 16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/workers'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true, 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['ChromeHeadless'], 29 | singleRun: true, 30 | customLaunchers: { 31 | ChromeHeadless: { 32 | base: 'Chrome', 33 | flags: [ 34 | '--no-sandbox', 35 | '--headless', 36 | '--disable-gpu', 37 | '--disable-web-security', 38 | '--remote-debugging-port=9222', 39 | ], 40 | }, 41 | }, 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /projects/workers/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/workers", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/workers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ng-web-apis/workers", 3 | "version": "0.0.0-development", 4 | "peerDependencies": { 5 | "@angular/core": ">=6.0.0", 6 | "@ng-web-apis/common": ">=1.1.0" 7 | }, 8 | "description": "A library for comfortable use of Web Workers API in Angular", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "keywords": ["angular", "ng", "worker", "web", "service", "shared"], 13 | "license": "MIT", 14 | "author": { 15 | "name": "Igor Katsuba", 16 | "email": "katsuba.igor@gmail.com" 17 | }, 18 | "contributors": [ 19 | "Alexander Inkin ", 20 | "Roman Sedov <79601794011@ya.ru>" 21 | ], 22 | "repository": "https://github.com/ng-web-apis/workers", 23 | "bugs": "https://github.com/ng-web-apis/workers/issues", 24 | "homepage": "https://github.com/ng-web-apis/workers#README" 25 | } 26 | -------------------------------------------------------------------------------- /projects/workers/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Public API Surface of @ng-web-apis/workers 3 | */ 4 | export * from './worker/classes/web-worker'; 5 | export * from './worker/operators/to-data'; 6 | 7 | export * from './worker/pipes/worker.pipe'; 8 | 9 | export * from './worker/types/worker-function'; 10 | export * from './worker/types/typed-message-event'; 11 | 12 | export * from './worker/worker.module'; 13 | -------------------------------------------------------------------------------- /projects/workers/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | import 'zone.js/dist/zone'; 3 | import 'zone.js/dist/zone-testing'; 4 | 5 | import {getTestBed} from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting, 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | declare const require: any; 12 | 13 | // First, initialize the Angular testing environment. 14 | getTestBed().initTestEnvironment( 15 | BrowserDynamicTestingModule, 16 | platformBrowserDynamicTesting(), 17 | ); 18 | 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | 22 | // And load the modules. 23 | context.keys().map(context); 24 | -------------------------------------------------------------------------------- /projects/workers/src/worker/classes/web-worker.spec.ts: -------------------------------------------------------------------------------- 1 | import {Observable} from 'rxjs'; 2 | import {take} from 'rxjs/operators'; 3 | import {TypedMessageEvent} from '../types/typed-message-event'; 4 | import {WebWorker} from './web-worker'; 5 | 6 | // it is needed to ignore web worker errors 7 | window.onerror = () => {}; 8 | 9 | describe('WebWorker', () => { 10 | it('should fail if a worker is not available', async () => { 11 | const OriginalWorker = Worker; 12 | 13 | delete (window as any).Worker; 14 | 15 | const worker = WebWorker.fromFunction(d => d); 16 | 17 | expect(() => worker.terminate()).not.toThrow(); 18 | expect(() => worker.postMessage()).not.toThrow(); 19 | 20 | await expectAsync(worker.toPromise()).toBeRejected(); 21 | 22 | (window as any).Worker = OriginalWorker; 23 | }); 24 | 25 | it('should create worker from a function', () => { 26 | const worker = WebWorker.fromFunction(d => d); 27 | 28 | expect(worker instanceof WebWorker).toEqual(true); 29 | expect((worker as any).worker instanceof Worker).toEqual(true); 30 | }); 31 | 32 | it('should trigger an error if URL was not found', async () => { 33 | const worker = new WebWorker('some/wrong/url'); 34 | 35 | await expectAsync(worker.toPromise()).toBeRejected(); 36 | }); 37 | 38 | it('should resolve the last value before completing', async () => { 39 | const worker = WebWorker.fromFunction((data: string) => Promise.resolve(data)); 40 | 41 | const promise = worker 42 | .pipe(source => { 43 | return new Observable(subscriber => { 44 | source.subscribe({ 45 | next({data}: TypedMessageEvent) { 46 | (source as WebWorker).terminate(); 47 | subscriber.next(data); 48 | subscriber.complete(); 49 | }, 50 | }); 51 | }); 52 | }) 53 | .toPromise(); 54 | 55 | worker.postMessage('a'); 56 | worker.postMessage('b'); 57 | expect(await promise).toEqual('a'); 58 | }); 59 | 60 | it('should run a worker and return a correct data', async () => { 61 | const workerPromise: Promise> = WebWorker.execute< 62 | string, 63 | string 64 | >(data => Promise.resolve().then(() => data), 'some data'); 65 | 66 | expect((await workerPromise).data).toEqual('some data'); 67 | }, 10000); 68 | 69 | it('should create worker', async () => { 70 | const thread = WebWorker.fromFunction(data => 71 | Promise.resolve(data), 72 | ); 73 | 74 | const workerPromise = thread.pipe(take(1)).toPromise(); 75 | 76 | thread.postMessage('some data'); 77 | 78 | expect((await workerPromise).data).toEqual('some data'); 79 | }, 10000); 80 | 81 | it('should fail if an inner promise is rejected', async () => { 82 | const worker = WebWorker.fromFunction(() => 83 | Promise.reject('reason'), 84 | ); 85 | 86 | worker.postMessage(); 87 | 88 | expect(await worker.toPromise().catch(err => err.message)).toEqual( 89 | 'Uncaught reason', 90 | ); 91 | }); 92 | 93 | it('should close all subscriptions, if the worker was terminated', async () => { 94 | const worker = WebWorker.fromFunction(() => 'some data'); 95 | 96 | const subscriptions = [ 97 | worker.subscribe(), 98 | worker.subscribe(), 99 | worker.subscribe(), 100 | ]; 101 | 102 | worker.terminate(); 103 | expect(subscriptions.map(s => s.closed)).toEqual([true, true, true]); 104 | }); 105 | 106 | it("shouldn't throw any errors, if the worker was terminated twice", async () => { 107 | const worker = WebWorker.fromFunction(() => 'some data'); 108 | 109 | worker.terminate(); 110 | worker.terminate(); 111 | expect(await worker.toPromise()).toBeUndefined(); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /projects/workers/src/worker/classes/web-worker.ts: -------------------------------------------------------------------------------- 1 | import {EMPTY, fromEvent, merge, Observable, Subject} from 'rxjs'; 2 | import {take, takeUntil, tap} from 'rxjs/operators'; 3 | import {WORKER_BLANK_FN} from '../consts/worker-fn-template'; 4 | import {TypedMessageEvent} from '../types/typed-message-event'; 5 | import {WorkerFunction} from '../types/worker-function'; 6 | 7 | export class WebWorker extends Observable> { 8 | private readonly worker: Worker | undefined; 9 | private readonly url: string; 10 | private readonly destroy$: Subject; 11 | 12 | constructor(url: string, options?: WorkerOptions) { 13 | let worker: Worker | undefined; 14 | let error: any; 15 | 16 | try { 17 | worker = new Worker(url, options); 18 | } catch (e) { 19 | error = e; 20 | } 21 | 22 | super(subscriber => { 23 | let eventStream$: Observable | ErrorEvent> = EMPTY; 24 | 25 | if (error) { 26 | subscriber.error(error); 27 | } else if (this.destroy$.isStopped) { 28 | subscriber.complete(); 29 | } else if (worker) { 30 | eventStream$ = merge( 31 | fromEvent>(worker, 'message').pipe( 32 | tap(event => subscriber.next(event)), 33 | ), 34 | fromEvent(worker, 'error').pipe( 35 | tap(event => subscriber.error(event)), 36 | ), 37 | ).pipe(takeUntil(this.destroy$)); 38 | } 39 | 40 | eventStream$.subscribe().add(subscriber); 41 | }); 42 | 43 | this.worker = worker; 44 | this.url = url; 45 | this.destroy$ = new Subject(); 46 | } 47 | 48 | static fromFunction( 49 | fn: WorkerFunction, 50 | options?: WorkerOptions, 51 | ): WebWorker { 52 | return new WebWorker(WebWorker.createFnUrl(fn), options); 53 | } 54 | 55 | static execute( 56 | fn: WorkerFunction, 57 | data: T, 58 | ): Promise> { 59 | const worker = WebWorker.fromFunction(fn); 60 | const promise = worker.pipe(take(1)).toPromise(); 61 | 62 | worker.postMessage(data); 63 | 64 | return promise.then(result => { 65 | worker.terminate(); 66 | 67 | return result; 68 | }); 69 | } 70 | 71 | private static createFnUrl(fn: WorkerFunction): string { 72 | const script = `(${WORKER_BLANK_FN})(${fn});`; 73 | 74 | const blob = new Blob([script], {type: 'text/javascript'}); 75 | 76 | return URL.createObjectURL(blob); 77 | } 78 | 79 | terminate() { 80 | if (this.destroy$.isStopped) { 81 | return; 82 | } 83 | 84 | if (this.worker) { 85 | this.worker.terminate(); 86 | } 87 | 88 | URL.revokeObjectURL(this.url); 89 | 90 | this.destroy$.next(); 91 | this.destroy$.complete(); 92 | } 93 | 94 | postMessage(value: T) { 95 | if (this.worker) { 96 | this.worker.postMessage(value); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /projects/workers/src/worker/consts/worker-fn-template.ts: -------------------------------------------------------------------------------- 1 | // throw an error using the `setTimeout` function 2 | // because web worker doesn't emit ErrorEvent from promises 3 | export const WORKER_BLANK_FN = ` 4 | function(fn){ 5 | function isFunction(type){ 6 | return type === 'function'; 7 | } 8 | 9 | self.addEventListener('message', function(e) { 10 | var result = fn.call(null, e.data); 11 | if (result && [typeof result.then, typeof result.catch].every(isFunction)){ 12 | result 13 | .then(postMessage) 14 | .catch(function(error) { 15 | setTimeout(function(){throw error}, 0) 16 | }) 17 | } else { 18 | postMessage(result); 19 | } 20 | }) 21 | } 22 | `; 23 | -------------------------------------------------------------------------------- /projects/workers/src/worker/operators/to-data.ts: -------------------------------------------------------------------------------- 1 | import {OperatorFunction} from 'rxjs'; 2 | import {map} from 'rxjs/operators'; 3 | import {TypedMessageEvent} from '../types/typed-message-event'; 4 | 5 | export function toData(): OperatorFunction, T> { 6 | return map, T>(({data}) => data); 7 | } 8 | -------------------------------------------------------------------------------- /projects/workers/src/worker/pipes/worker.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import {take} from 'rxjs/operators'; 2 | import {WorkerPipe} from './worker.pipe'; 3 | 4 | describe('WorkerPipe', () => { 5 | let pipe: WorkerPipe; 6 | 7 | beforeEach(() => { 8 | pipe = new WorkerPipe(); 9 | }); 10 | 11 | it('should emit the first value', async () => { 12 | const result = await pipe 13 | .transform('a', data => data) 14 | .pipe(take(1)) 15 | .toPromise(); 16 | 17 | expect(await result).toEqual('a'); 18 | }); 19 | 20 | it('should return the same worker for the same function', async () => { 21 | const workerFn = (data: unknown) => data; 22 | 23 | const worker = await pipe.transform('a', workerFn); 24 | const theSameWorker = await pipe.transform('a', workerFn); 25 | 26 | expect(worker).toEqual(theSameWorker); 27 | }); 28 | 29 | it('should return a different worker for a different function', async () => { 30 | const worker = await pipe.transform('a', (data: unknown) => data); 31 | const differentWorker = await pipe.transform('a', (data: unknown) => data); 32 | 33 | expect(worker).not.toEqual(differentWorker); 34 | }); 35 | 36 | it('should terminate a previous worker', async () => { 37 | const worker = await pipe.transform('a', (data: unknown) => data); 38 | 39 | await pipe.transform('a', (data: unknown) => data); 40 | await expectAsync(worker.toPromise()).toBeResolved(); 41 | }); 42 | 43 | it('should terminate a worker then a pipe is destroyed', async () => { 44 | const worker = await pipe.transform('a', (data: unknown) => data); 45 | 46 | pipe.ngOnDestroy(); 47 | await expectAsync(worker.toPromise()).toBeResolved(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /projects/workers/src/worker/pipes/worker.pipe.ts: -------------------------------------------------------------------------------- 1 | import {OnDestroy, Pipe, PipeTransform} from '@angular/core'; 2 | import {Observable} from 'rxjs'; 3 | import {WebWorker} from '../classes/web-worker'; 4 | import {toData} from '../operators/to-data'; 5 | import {WorkerFunction} from '../types/worker-function'; 6 | 7 | @Pipe({ 8 | name: 'waWorker', 9 | }) 10 | export class WorkerPipe implements PipeTransform, OnDestroy { 11 | private fn!: WorkerFunction; 12 | private worker!: WebWorker; 13 | private observer!: Observable; 14 | 15 | transform(value: T, fn: WorkerFunction): Observable { 16 | if (this.fn !== fn) { 17 | this.terminateWorker(); 18 | this.initNewWorker(fn); 19 | } 20 | 21 | this.worker.postMessage(value); 22 | 23 | return this.observer; 24 | } 25 | 26 | ngOnDestroy(): void { 27 | this.terminateWorker(); 28 | } 29 | 30 | private terminateWorker() { 31 | if (this.worker) { 32 | this.worker.terminate(); 33 | } 34 | } 35 | 36 | private initNewWorker(fn: WorkerFunction) { 37 | this.fn = fn; 38 | this.worker = WebWorker.fromFunction(fn); 39 | this.observer = this.worker.pipe(toData()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /projects/workers/src/worker/types/typed-message-event.ts: -------------------------------------------------------------------------------- 1 | export interface TypedMessageEvent extends MessageEvent { 2 | data: T; 3 | } 4 | -------------------------------------------------------------------------------- /projects/workers/src/worker/types/worker-function.ts: -------------------------------------------------------------------------------- 1 | export type WorkerFunction = (data: T) => R | Promise; 2 | -------------------------------------------------------------------------------- /projects/workers/src/worker/worker.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {WorkerPipe} from './pipes/worker.pipe'; 3 | 4 | @NgModule({ 5 | declarations: [WorkerPipe], 6 | exports: [WorkerPipe], 7 | }) 8 | export class WorkerModule {} 9 | -------------------------------------------------------------------------------- /projects/workers/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 | "lib": ["dom", "es2018"] 9 | }, 10 | "angularCompilerOptions": { 11 | "annotateForClosureCompiler": true, 12 | "skipTemplateCodegen": true, 13 | "strictMetadataEmit": true, 14 | "fullTemplateTypeCheck": true, 15 | "strictInjectionParameters": true, 16 | "enableResourceInlining": true 17 | }, 18 | "exclude": ["src/test.ts", "**/*.spec.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /projects/workers/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["src/test.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /scripts/postbuild.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const DIST_LIB_PATH = 'dist/workers/'; 4 | const README_PATH = 'README.md'; 5 | const PATH_TO_README = DIST_LIB_PATH + README_PATH; 6 | 7 | copyExtraFiles(); 8 | 9 | function copyExtraFiles() { 10 | if (!fs.existsSync(README_PATH)) { 11 | throw new Error('Requested files do not exit'); 12 | } else { 13 | copyReadmeIntoDistFolder(README_PATH, PATH_TO_README); 14 | } 15 | } 16 | 17 | function copyReadmeIntoDistFolder(srcPath, toPath) { 18 | const fileBody = fs.readFileSync(srcPath).toString(); 19 | const withoutLogos = fileBody 20 | .replace('![ng-web-apis logo](projects/demo/src/assets/logo.svg) ', '') 21 | .replace(' ', ''); 22 | 23 | fs.writeFileSync(toPath, withoutLogos); 24 | } 25 | -------------------------------------------------------------------------------- /scripts/syncVersions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const glob = require('glob'); 3 | const JSON_INDENTATION_LEVEL = 4; 4 | const {version} = require('../package.json'); 5 | 6 | // Sync libraries package.json versions with main package.json 7 | syncVersions('projects'); 8 | 9 | function syncVersions(root) { 10 | glob(root + '/**/package.json', (_, files) => { 11 | files.forEach(file => { 12 | const packageJson = JSON.parse(fs.readFileSync(file)); 13 | 14 | fs.writeFileSync( 15 | file, 16 | JSON.stringify( 17 | { 18 | ...packageJson, 19 | version, 20 | }, 21 | null, 22 | JSON_INDENTATION_LEVEL, 23 | ), 24 | ); 25 | }); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "strict": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noUnusedParameters": true, 17 | "noUnusedLocals": true, 18 | "target": "es5", 19 | "typeRoots": ["node_modules/@types"], 20 | "lib": ["es2018", "dom"], 21 | "paths": { 22 | "@ng-web-apis/workers": ["projects/workers/src/public-api"] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@tinkoff/linters/tslint/bases/prettier.tslint.json"], 3 | "rules": { 4 | "ordered-imports": true, 5 | "interface-name": false 6 | } 7 | } 8 | --------------------------------------------------------------------------------