Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
4277ace8cd | |||
f08544d53a | |||
ffe324a9dc | |||
70a6ad040a | |||
31f0789648 | |||
e395a059a6 | |||
442bc5a9d9 | |||
5b0f09361b | |||
8d2d9c3806 | |||
a9ec4449d9 | |||
862b0d87ce | |||
1e095d325b | |||
515be51253 | |||
64eb16880a | |||
2d2166cf40 | |||
2dd803fdc2 | |||
bbcfa63944 | |||
015d2903b2 | |||
f66deb9cd4 | |||
b786e44bf7 | |||
11376cbca2 | |||
4d62773747 | |||
69d8293972 | |||
e25c885ddf | |||
93a9bf437d | |||
a7c6f75cc5 | |||
f67750ff0f | |||
ee25cfc78f | |||
ff5ab4def0 | |||
b418c11827 | |||
e643cf714e | |||
5a82307afe | |||
9abbcac166 | |||
5db2cd1077 | |||
3de9178cd0 | |||
2b1f8a2718 | |||
2117c2f5d9 | |||
ca0daf2454 | |||
ad6d18d306 | |||
7ddcd88862 | |||
9d6b7f3b29 | |||
8be9fc38b6 | |||
6bb3bb7d9a | |||
7fcbd39166 | |||
1e38d6564d | |||
9b3c0e88da | |||
9ba1d442b4 | |||
b130f09a80 | |||
31ea0863e8 | |||
0210bdf4df | |||
7b3734f79e | |||
1244956187 | |||
390e0f7b9e | |||
a63c26c05d | |||
3782b93837 | |||
8ccc5e93b1 |
66
.gitea/workflows/default_nottags.yaml
Normal file
66
.gitea/workflows/default_nottags.yaml
Normal file
@ -0,0 +1,66 @@
|
||||
name: Default (not tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install pnpm and npmci
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
|
||||
- name: Run npm prepare
|
||||
run: npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
124
.gitea/workflows/default_tags.yaml
Normal file
124
.gitea/workflows/default_tags.yaml
Normal file
@ -0,0 +1,124 @@
|
||||
name: Default (tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
||||
|
||||
release:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Release
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm publish
|
||||
|
||||
metadata:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @shipzone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Code quality
|
||||
run: |
|
||||
npmci command npm install -g typescript
|
||||
npmci npm install
|
||||
|
||||
- name: Trigger
|
||||
run: npmci trigger
|
||||
|
||||
- name: Build docs and upload artifacts
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
pnpm install -g @git.zone/tsdoc
|
||||
npmci command tsdoc
|
||||
continue-on-error: true
|
17
.gitignore
vendored
17
.gitignore
vendored
@ -1,6 +1,21 @@
|
||||
.nogit/
|
||||
node_modules/
|
||||
|
||||
# artifacts
|
||||
coverage/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
|
||||
# caches
|
||||
.yarn/
|
||||
.cache/
|
||||
.rpt2_cache
|
||||
|
||||
# builds
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
||||
**/.claude/settings.local.json
|
||||
|
147
.gitlab-ci.yml
147
.gitlab-ci.yml
@ -1,147 +0,0 @@
|
||||
# gitzone standard
|
||||
image: hosttoday/ht-docker-node:npmci
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .npmci_cache/
|
||||
key: "$CI_BUILD_STAGE"
|
||||
|
||||
stages:
|
||||
- security
|
||||
- test
|
||||
- release
|
||||
- metadata
|
||||
|
||||
# ====================
|
||||
# security stage
|
||||
# ====================
|
||||
mirror:
|
||||
stage: security
|
||||
script:
|
||||
- npmci git mirror
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
snyk:
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install -g snyk
|
||||
- npmci command npm install --ignore-scripts
|
||||
- npmci command snyk test
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
# ====================
|
||||
# test stage
|
||||
# ====================
|
||||
testLEGACY:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install legacy
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
allow_failure: true
|
||||
|
||||
testLTS:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install lts
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
testSTABLE:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
release:
|
||||
stage: release
|
||||
script:
|
||||
- npmci node install stable
|
||||
- npmci npm publish
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
# ====================
|
||||
# metadata stage
|
||||
# ====================
|
||||
codequality:
|
||||
stage: metadata
|
||||
image: docker:stable
|
||||
allow_failure: true
|
||||
services:
|
||||
- docker:stable-dind
|
||||
script:
|
||||
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
|
||||
- docker run
|
||||
--env SOURCE_CODE="$PWD"
|
||||
--volume "$PWD":/code
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock
|
||||
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
|
||||
artifacts:
|
||||
paths: [codeclimate.json]
|
||||
tags:
|
||||
- docker
|
||||
- priv
|
||||
|
||||
trigger:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci trigger
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
pages:
|
||||
image: hosttoday/ht-docker-node:npmci
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci command npm install -g typedoc typescript
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/
|
||||
tags:
|
||||
- docker
|
||||
- notpriv
|
||||
only:
|
||||
- tags
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
paths:
|
||||
- public
|
||||
allow_failure: true
|
||||
|
||||
windowsCompatibility:
|
||||
image: stefanscherer/node-windows:10-build-tools
|
||||
stage: metadata
|
||||
script:
|
||||
- npm install & npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- windows
|
||||
allow_failure: true
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "npm test",
|
||||
"name": "Run npm test",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
26
.vscode/settings.json
vendored
Normal file
26
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": ["/npmextra.json"],
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"npmci": {
|
||||
"type": "object",
|
||||
"description": "settings for npmci"
|
||||
},
|
||||
"gitzone": {
|
||||
"type": "object",
|
||||
"description": "settings for gitzone",
|
||||
"properties": {
|
||||
"projectType": {
|
||||
"type": "string",
|
||||
"enum": ["website", "element", "service", "npm", "wcc"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
10478
assets/domains.json
Normal file
10478
assets/domains.json
Normal file
File diff suppressed because it is too large
Load Diff
48
changelog.md
Normal file
48
changelog.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-05-07 - 2.1.0 - feat(smartmail)
|
||||
Add new email validation helper methods (getMxRecords, isDisposableEmail, isRoleAccount) and an applyVariables method to Smartmail for dynamic templating.
|
||||
|
||||
- Introduced getMxRecords in EmailAddressValidator to extract MX records from DNS responses.
|
||||
- Added isDisposableEmail to determine if an email is from a disposable domain.
|
||||
- Added isRoleAccount to identify role-based email addresses.
|
||||
- Implemented applyVariables in Smartmail to update subject, body, and htmlBody templates with provided data.
|
||||
|
||||
## 2025-05-07 - 2.0.1 - fix(readme)
|
||||
Update documentation to include usage of creation object reference and update API details.
|
||||
|
||||
- Added a new section explaining how to use the creationObjectRef in Smartmail.
|
||||
- Included detailed examples on retrieving and using the creation object reference.
|
||||
- Updated the API reference to document the getCreationObject method.
|
||||
|
||||
## 2025-05-07 - 2.0.0 - BREAKING CHANGE(smartmail)
|
||||
Improve email validation and Smartmail features: add detailed validation for email parts, caching for MX lookups, multi-recipient support, custom headers, and update dependency imports and build scripts.
|
||||
|
||||
- Updated dependency references from '@gitzone/*' to '@git.zone/*'
|
||||
- Enhanced EmailAddressValidator with RFC 5322 compliance, local part, domain part validation, and detailed result fields
|
||||
- Implemented caching mechanism for MX record lookups
|
||||
- Expanded Smartmail class: added support for multiple recipients, reply-to, custom headers, and MIME formatting
|
||||
- Updated build scripts, package configuration, and documentation with more comprehensive usage examples and tests
|
||||
|
||||
## 2024-05-29 - 1.0.24 - misc
|
||||
This release improved several project configurations and documentation details.
|
||||
|
||||
- docs: Updated the project description.
|
||||
- config: Revised tsconfig settings.
|
||||
- npmextra: Adjusted npmextra.json with updated githost values (applied on multiple commits).
|
||||
|
||||
## 2023-07-28 - 1.0.23 - misc
|
||||
A couple of targeted fixes and organizational changes were introduced.
|
||||
|
||||
- core: Applied a core fix update.
|
||||
- org: Switched to the new organization scheme.
|
||||
|
||||
## 2023-06-13 - 1.0.22 - core
|
||||
Over a longer period (versions 1.0.22 down to 1.0.4), a series of minor core fixes were rolled out to improve stability and maintainability.
|
||||
These commits—which, aside from their version‐marking commits, were solely “fix(core): update”—were consolidated into this summary.
|
||||
|
||||
## 2018-09-29 - 1.0.1 - initial
|
||||
In the very early releases (from versions 1.0.3 to 1.0.1), the initial stabilization work set the stage for the project.
|
||||
- npm: Adjusted access level (fix(npm): access level in 1.0.3).
|
||||
- packagename: Updated package name details (fix(packagename): update in 1.0.2).
|
||||
- core: Laid the groundwork with the initial core fix (fix(core): initial in 1.0.1).
|
21
license
Normal file
21
license
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Lossless GmbH
|
||||
Copyright (c) Romain SIMON
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CON
|
@ -1,8 +1,35 @@
|
||||
{
|
||||
"npmci": {
|
||||
"npmGlobalTools": [
|
||||
"@gitzone/npmts",
|
||||
"@git.zone/npmts",
|
||||
"ts-node"
|
||||
]
|
||||
],
|
||||
"npmAccessLevel": "public"
|
||||
},
|
||||
"gitzone": {
|
||||
"projectType": "npm",
|
||||
"module": {
|
||||
"githost": "code.foss.global",
|
||||
"gitscope": "push.rocks",
|
||||
"gitrepo": "smartmail",
|
||||
"description": "A unified format for representing and dealing with emails, with support for attachments and email validation.",
|
||||
"npmPackagename": "@push.rocks/smartmail",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"email handling",
|
||||
"email validation",
|
||||
"email formatting",
|
||||
"typescript",
|
||||
"email attachment",
|
||||
"smartmail",
|
||||
"email development",
|
||||
"email template",
|
||||
"disposable email detection",
|
||||
"freemail detection"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tsdoc": {
|
||||
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
|
||||
}
|
||||
}
|
||||
}
|
856
package-lock.json
generated
856
package-lock.json
generated
@ -1,856 +0,0 @@
|
||||
{
|
||||
"name": "smartmail",
|
||||
"version": "1.0.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@airbnb/node-memwatch": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@airbnb/node-memwatch/-/node-memwatch-1.0.2.tgz",
|
||||
"integrity": "sha512-2R+MEEMSTUdKwQ6NFWkyA/UNoSjL1tMldZqJbZpgXSwNMBzlNlkUWEXKu9RqTTMkDqJRfGJ2VDs8gPlPK2APDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bindings": "^1.3.0",
|
||||
"nan": "^2.9.2"
|
||||
}
|
||||
},
|
||||
"@gitzone/tsbuild": {
|
||||
"version": "2.0.22",
|
||||
"resolved": "https://registry.npmjs.org/@gitzone/tsbuild/-/tsbuild-2.0.22.tgz",
|
||||
"integrity": "sha512-H0rqGVUKXWgxXhkY62kF92WpbS9GSJW27jQXaoyMsQptTQN4HIYKHWZMdO4egkk0/gDmKnBjk8MXg5Rx6efItA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/smartfile": "^6.0.6",
|
||||
"@pushrocks/smartlog": "^2.0.1",
|
||||
"@pushrocks/smartpath": "^4.0.1",
|
||||
"@pushrocks/smartpromise": "^2.0.5",
|
||||
"typescript": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"@gitzone/tsrun": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/@gitzone/tsrun/-/tsrun-1.1.12.tgz",
|
||||
"integrity": "sha512-DOxqOg+evoxhgbzhzH4u6LaPF+6bpMsnBVl1QQaHzKPGBlNjaIY4yJ0RsGnWMgX1hlNLvbgHtl0Ky4A2MDvyrg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@gitzone/tsbuild": "^2.0.22",
|
||||
"@pushrocks/smartfile": "^6.0.6",
|
||||
"ts-node": "^7.0.0",
|
||||
"typescript": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"@gitzone/tstest": {
|
||||
"version": "1.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@gitzone/tstest/-/tstest-1.0.15.tgz",
|
||||
"integrity": "sha512-+t5fvYK4a0JkwwH0Fokh5aOxVzrax5OjDUL4zmhBk7KFmXt7fdvcqsSNaEp9iyqC52dLiDybdAXqHYZypXTIYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@gitzone/tsrun": "^1.1.12",
|
||||
"@pushrocks/consolecolor": "^2.0.1",
|
||||
"@pushrocks/smartfile": "^6.0.6",
|
||||
"@pushrocks/smartlog": "^2.0.1",
|
||||
"@pushrocks/smartpromise": "^2.0.5",
|
||||
"@pushrocks/smartshell": "^2.0.6",
|
||||
"@types/figures": "^2.0.0",
|
||||
"figures": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@pushrocks/consolecolor": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/consolecolor/-/consolecolor-2.0.1.tgz",
|
||||
"integrity": "sha512-iOFCHVeFZ2OywbdwSxVI4/wokkcLrXVdHLgvMmkNhJ220eeLgjNZWx3EJo3vNW3zq5ybCSCUIq0878djBxrWpw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-256-colors": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"@pushrocks/early": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/early/-/early-3.0.3.tgz",
|
||||
"integrity": "sha512-71/nwxTpqdp1glmHz4YaGusNl/XOOcPelAxC9RA6rpS/6280QyY2u4yx+mRdMrCzn7ruLYF5awbkS8llNZ94Pg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/consolecolor": "^2.0.1",
|
||||
"@pushrocks/smartpromise": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"@pushrocks/smartdelay": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartdelay/-/smartdelay-2.0.2.tgz",
|
||||
"integrity": "sha512-4xf6tMKwZcxBynKgXrM4SQKgeASfRvx43LUmR5DkStp26ZHAsarCXUdKJS6y8QIPygEOTOCP8we97JAcCzBuMg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/smartpromise": "^2.0.5"
|
||||
}
|
||||
},
|
||||
"@pushrocks/smartfile": {
|
||||
"version": "6.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartfile/-/smartfile-6.0.8.tgz",
|
||||
"integrity": "sha512-YkAovvQ0pcVphXLynlQ1D4nhEoUqALqqUn2pRXc2vXNy0RDSU22puLlLVhoXT0NPAqYNMazhqc4OAixgEOpdFw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/smartpath": "^4.0.1",
|
||||
"@pushrocks/smartpromise": "^2.0.5",
|
||||
"@pushrocks/smartrequest": "^1.1.14",
|
||||
"@types/fs-extra": "^5.0.4",
|
||||
"@types/vinyl": "^2.0.2",
|
||||
"fs-extra": "^7.0.0",
|
||||
"glob": "^7.1.2",
|
||||
"js-yaml": "^3.10.0",
|
||||
"vinyl-file": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@pushrocks/smartlog": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartlog/-/smartlog-2.0.1.tgz",
|
||||
"integrity": "sha512-GtsDTGIUF3VuWPyF8FV5dF31ZCEIcaJ56ZlvJsWxjnyJq57X25mk5/K0QAaRE9IIeHg6fORcukFomb5C+AOQrg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/smartlog-interfaces": "^1.0.9"
|
||||
}
|
||||
},
|
||||
"@pushrocks/smartlog-interfaces": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartlog-interfaces/-/smartlog-interfaces-1.0.9.tgz",
|
||||
"integrity": "sha512-0qwpomrRN0kFjmhR9m1iHYXoISoNuXtRP0Wr+JtkYyURLwKHMaW8Xoznf8MzXJptRfqufJi3Fxh5HodpPrIZUA==",
|
||||
"dev": true
|
||||
},
|
||||
"@pushrocks/smartpath": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartpath/-/smartpath-4.0.1.tgz",
|
||||
"integrity": "sha512-MaI0+uLQPCr2V3WGnbdgb0pWa9xkWyrP4qYcbsHIjeismGLbn9s3jmP/HIXU8LkgzRgaVb+BJxmZJHOwl32DyA==",
|
||||
"dev": true
|
||||
},
|
||||
"@pushrocks/smartpromise": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartpromise/-/smartpromise-2.0.5.tgz",
|
||||
"integrity": "sha512-9j/chLtIiNkR0MDw7Mpxg9slxAVvAQwUZuiaPYX5KpHdKxQaHLI1VZ8IN0vPhwlfgNO4i4vGXV0wB8BvSDj03g==",
|
||||
"dev": true
|
||||
},
|
||||
"@pushrocks/smartrequest": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartrequest/-/smartrequest-1.1.14.tgz",
|
||||
"integrity": "sha512-+sDQB4Mxvpn8BIMPUQ7TPSCKUVMln3tHC4rp4pmfEHmBQK+g1XwtNr59aMA9kEoBDMt7li1hu+1cs+SNsWt6Gw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/smartpromise": "^2.0.5",
|
||||
"@types/form-data": "^2.2.1",
|
||||
"form-data": "^2.3.2"
|
||||
}
|
||||
},
|
||||
"@pushrocks/smartshell": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/smartshell/-/smartshell-2.0.6.tgz",
|
||||
"integrity": "sha512-D48KB3DDqLfMjOXGEutqJi+v3Z4RcWacu5BJXxUwrecvd6oetbKobfmNGxeHSQPmNGb7U3ISfKwV6c5T5EZkJg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/smartpromise": "^2.0.5",
|
||||
"@types/which": "^1.3.1",
|
||||
"which": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"@pushrocks/tapbundle": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@pushrocks/tapbundle/-/tapbundle-3.0.7.tgz",
|
||||
"integrity": "sha512-ZI4fhXPy8XMfiy/QofP0ZDuFdv3cErm+FP/+AXaNKdgCCx01MXNTYSUdER1GPUXW3ZbiXdurkLJlxlhIzD1ZJw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@pushrocks/early": "^3.0.3",
|
||||
"@pushrocks/smartdelay": "^2.0.2",
|
||||
"@pushrocks/smartpromise": "^2.0.5",
|
||||
"leakage": "^0.4.0",
|
||||
"smartchai": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"@types/chai": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.6.tgz",
|
||||
"integrity": "sha512-CBk7KTZt3FhPsEkYioG6kuCIpWISw+YI8o+3op4+NXwTpvAPxE1ES8+PY8zfaK2L98b1z5oq03UHa4VYpeUxnw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/chai-as-promised": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.0.tgz",
|
||||
"integrity": "sha512-MFiW54UOSt+f2bRw8J7LgQeIvE/9b4oGvwU7XW30S9QGAiHGnU/fmiOprsyMkdmH2rl8xSPc0/yrQw8juXU6bQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/chai": "*"
|
||||
}
|
||||
},
|
||||
"@types/chai-string": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.1.tgz",
|
||||
"integrity": "sha512-aRNMs6TKgjgPlCHwDfq/YNy5VtRR2hJ4AUWByddrT0TRVVD8eX4MiHW6/iHvmQHRlVuuPZcwnTUE7b4yFt7bEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/chai": "*"
|
||||
}
|
||||
},
|
||||
"@types/figures": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/figures/-/figures-2.0.0.tgz",
|
||||
"integrity": "sha512-mcRgJ+ncKuNI+Dwac7omO18B8C8u+YBS+AU/oyLhEyjAnT3cUUThhHgZpbiIvu5ZqSvdD30BXtrqg9nxc3OKMg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/form-data": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
|
||||
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.4.tgz",
|
||||
"integrity": "sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.3.tgz",
|
||||
"integrity": "sha512-3AvcEJAh9EMatxs+OxAlvAEs7OTy6AG94mcH1iqyVDwVVndekLxzwkWQ/Z4SDbY6GO2oyUXyWW8tQ4rENSSQVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/vinyl": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.2.tgz",
|
||||
"integrity": "sha512-2iYpNuOl98SrLPBZfEN9Mh2JCJ2EI9HU35SfgBEb51DcmaHkhp8cKMblYeBqMQiwXMgAD3W60DbQ4i/UdLiXhw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/which/-/which-1.3.1.tgz",
|
||||
"integrity": "sha512-ZrJDWpvg75LTGX4XwuneY9s6bF3OeZcGTpoGh3zDV9ytzcHMFsRrMIaLBRJZQMBoGyKs6unBQfVdrLZiYfb1zQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-256-colors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-256-colors/-/ansi-256-colors-1.1.0.tgz",
|
||||
"integrity": "sha1-kQ3lDvzHwJ49gvL4er1rcAwYgYo=",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"arrify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
|
||||
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
|
||||
"dev": true
|
||||
},
|
||||
"assertion-error": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
||||
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
|
||||
"dev": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
|
||||
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"chai": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
|
||||
"integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assertion-error": "^1.1.0",
|
||||
"check-error": "^1.0.2",
|
||||
"deep-eql": "^3.0.1",
|
||||
"get-func-name": "^2.0.0",
|
||||
"pathval": "^1.1.0",
|
||||
"type-detect": "^4.0.5"
|
||||
}
|
||||
},
|
||||
"chai-as-promised": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz",
|
||||
"integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"check-error": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"chai-string": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz",
|
||||
"integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==",
|
||||
"dev": true
|
||||
},
|
||||
"check-error": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
||||
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
||||
"dev": true
|
||||
},
|
||||
"clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
|
||||
"dev": true
|
||||
},
|
||||
"clone-buffer": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
|
||||
"integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
|
||||
"dev": true
|
||||
},
|
||||
"clone-stats": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
|
||||
"integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
|
||||
"dev": true
|
||||
},
|
||||
"cloneable-readable": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz",
|
||||
"integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
"process-nextick-args": "^2.0.0",
|
||||
"readable-stream": "^2.3.5"
|
||||
}
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
|
||||
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
||||
"dev": true
|
||||
},
|
||||
"deep-eql": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
|
||||
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-detect": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"dev": true
|
||||
},
|
||||
"es6-error": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
|
||||
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"dev": true
|
||||
},
|
||||
"figures": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
|
||||
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"first-chunk-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz",
|
||||
"integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"readable-stream": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
|
||||
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz",
|
||||
"integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"get-func-name": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"is-utf8": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
|
||||
"integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
|
||||
"dev": true
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
|
||||
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"leakage": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/leakage/-/leakage-0.4.0.tgz",
|
||||
"integrity": "sha512-x7gYK5n5dPkHDZWJ2Kh8Ag1hZNzUh+HtXn8Bv1aDdN6o6ONPCJ8sOfFq+kxcULJFp3lXaCjXb3iXOLmQRbBLwA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@airbnb/node-memwatch": "^1.0.2",
|
||||
"es6-error": "^4.0.2",
|
||||
"left-pad": "^1.1.3",
|
||||
"minimist": "^1.2.0",
|
||||
"pretty-bytes": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"left-pad": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
|
||||
"integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==",
|
||||
"dev": true
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
|
||||
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.36.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
|
||||
"integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.20",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz",
|
||||
"integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "~1.36.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
|
||||
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==",
|
||||
"dev": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"pathval": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
|
||||
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
|
||||
"dev": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-bytes": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
|
||||
"integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=",
|
||||
"dev": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
|
||||
"dev": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"remove-trailing-separator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
|
||||
"dev": true
|
||||
},
|
||||
"replace-ext": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
|
||||
"integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
|
||||
"dev": true
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
},
|
||||
"smartchai": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/smartchai/-/smartchai-2.0.1.tgz",
|
||||
"integrity": "sha512-9M+R56OhAHXScxgr2vzQqxGx0XMS0QXriNZuP7hjlbVbo2FUT+l60iEzbwPt9Ga+5u2cEEjSSoZEQVqlROaddA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/chai": "^4.1.2",
|
||||
"@types/chai-as-promised": "^7.1.0",
|
||||
"@types/chai-string": "^1.4.0",
|
||||
"chai": "^4.1.2",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-string": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
|
||||
"integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-utf8": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"strip-bom-buf": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz",
|
||||
"integrity": "sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-utf8": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"strip-bom-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz",
|
||||
"integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"first-chunk-stream": "^2.0.0",
|
||||
"strip-bom": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
|
||||
"integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arrify": "^1.0.0",
|
||||
"buffer-from": "^1.1.0",
|
||||
"diff": "^3.1.0",
|
||||
"make-error": "^1.1.1",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"source-map-support": "^0.5.6",
|
||||
"yn": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"type-detect": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.1.tgz",
|
||||
"integrity": "sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ==",
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
},
|
||||
"vinyl": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz",
|
||||
"integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone": "^2.1.1",
|
||||
"clone-buffer": "^1.0.0",
|
||||
"clone-stats": "^1.0.0",
|
||||
"cloneable-readable": "^1.0.0",
|
||||
"remove-trailing-separator": "^1.0.1",
|
||||
"replace-ext": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"vinyl-file": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz",
|
||||
"integrity": "sha1-sQTZ5ECf+jJfqt1SBkLQo7SIs2U=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"pify": "^2.3.0",
|
||||
"strip-bom-buf": "^1.0.0",
|
||||
"strip-bom-stream": "^2.0.0",
|
||||
"vinyl": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
},
|
||||
"yn": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
|
||||
"integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
65
package.json
65
package.json
@ -1,23 +1,64 @@
|
||||
{
|
||||
"name": "smartmail",
|
||||
"version": "1.0.2",
|
||||
"name": "@push.rocks/smartmail",
|
||||
"version": "2.1.0",
|
||||
"private": false,
|
||||
"description": "a unified format for representing and dealing with mails",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"description": "A unified format for representing and dealing with emails, with support for attachments and email validation.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"type": "module",
|
||||
"author": "Lossless GmbH",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"test": "(tstest test/)",
|
||||
"format": "(gitzone format)",
|
||||
"build": "(tsbuild)"
|
||||
"build": "(tsbuild tsfolders --allowimplicitany)",
|
||||
"buildDocs": "tsdoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gitzone/tsbuild": "^2.0.22",
|
||||
"@gitzone/tsrun": "^1.1.12",
|
||||
"@gitzone/tstest": "^1.0.15",
|
||||
"@pushrocks/tapbundle": "^3.0.7",
|
||||
"@types/node": "^10.11.3"
|
||||
"@git.zone/tsbuild": "^2.3.2",
|
||||
"@git.zone/tsrun": "^1.3.3",
|
||||
"@git.zone/tstest": "^1.0.96",
|
||||
"@push.rocks/tapbundle": "^6.0.3",
|
||||
"@types/node": "^22.15.14"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"@push.rocks/smartdns": "^6.2.2",
|
||||
"@push.rocks/smartfile": "^11.2.0",
|
||||
"@push.rocks/smartmustache": "^3.0.2",
|
||||
"@push.rocks/smartpath": "^5.0.11",
|
||||
"@push.rocks/smartrequest": "^2.0.18"
|
||||
},
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
"ts_web/**/*",
|
||||
"dist/**/*",
|
||||
"dist_*/**/*",
|
||||
"dist_ts/**/*",
|
||||
"dist_ts_web/**/*",
|
||||
"assets/**/*",
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
"readme.md"
|
||||
],
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
],
|
||||
"keywords": [
|
||||
"email handling",
|
||||
"email validation",
|
||||
"email formatting",
|
||||
"typescript",
|
||||
"email attachment",
|
||||
"smartmail",
|
||||
"email development",
|
||||
"email template",
|
||||
"disposable email detection",
|
||||
"freemail detection"
|
||||
],
|
||||
"homepage": "https://code.foss.global/push.rocks/smartmail",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.foss.global/push.rocks/smartmail.git"
|
||||
},
|
||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||
}
|
||||
|
10211
pnpm-lock.yaml
generated
Normal file
10211
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
readme.hints.md
Normal file
1
readme.hints.md
Normal file
@ -0,0 +1 @@
|
||||
|
302
readme.md
Normal file
302
readme.md
Normal file
@ -0,0 +1,302 @@
|
||||
# @push.rocks/smartmail
|
||||
A unified format for representing and dealing with emails
|
||||
|
||||
## Install
|
||||
|
||||
To install `@push.rocks/smartmail`, you'll need Node.js installed on your system. With Node.js installed, run the following command in your terminal:
|
||||
|
||||
```bash
|
||||
pnpm add @push.rocks/smartmail
|
||||
```
|
||||
|
||||
This will add `@push.rocks/smartmail` to your project's dependencies.
|
||||
|
||||
## Features
|
||||
|
||||
- **Advanced Email Address Validation**: Validate email format, check for disposable/free domains, and verify MX records
|
||||
- **Rich Email Representation**: Create emails with multiple recipients, attachments, HTML content, and custom headers
|
||||
- **Template Support**: Use mustache templates for dynamic content in subject, body, and HTML
|
||||
- **MIME Formatting**: Convert emails to standard MIME format for sending
|
||||
- **Caching & Performance**: Smart caching of DNS lookups for better performance
|
||||
|
||||
## Usage
|
||||
|
||||
`@push.rocks/smartmail` provides a unified format for representing and dealing with emails in a Node.js environment. Below, you will find several examples showcasing how to use its main features.
|
||||
|
||||
### Importing the Module
|
||||
|
||||
First, ensure you're using ESM (ECMAScript Modules) syntax in your TypeScript project. Then, import the necessary classes:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Smartmail,
|
||||
EmailAddressValidator
|
||||
} from '@push.rocks/smartmail';
|
||||
```
|
||||
|
||||
## Email Address Validation
|
||||
|
||||
### Basic Email Validation
|
||||
|
||||
```typescript
|
||||
// Create validator with default options
|
||||
const emailValidator = new EmailAddressValidator();
|
||||
|
||||
// Validate an email address
|
||||
const result = await emailValidator.validate('user@example.com');
|
||||
console.log(result);
|
||||
/*
|
||||
{
|
||||
valid: true, // Overall validity
|
||||
formatValid: true, // Email format is valid
|
||||
localPartValid: true, // Local part (before @) is valid
|
||||
domainPartValid: true, // Domain part (after @) is valid
|
||||
mxValid: true, // Domain has valid MX records
|
||||
disposable: false, // Not a disposable email domain
|
||||
freemail: false, // Not a free email provider
|
||||
reason: 'Email is valid'
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
### Advanced Validation Options
|
||||
|
||||
```typescript
|
||||
// Create validator with custom options
|
||||
const validator = new EmailAddressValidator({
|
||||
skipOnlineDomainFetch: true, // Use only local domain list
|
||||
cacheDnsResults: true, // Cache DNS lookups
|
||||
cacheExpiryMs: 7200000 // Cache expires after 2 hours
|
||||
});
|
||||
|
||||
// Validate each part of an email separately
|
||||
const isValidFormat = validator.isValidEmailFormat('user@example.com');
|
||||
const isValidLocalPart = validator.isValidLocalPart('user');
|
||||
const isValidDomain = validator.isValidDomainPart('example.com');
|
||||
|
||||
// Check for disposable or free email providers
|
||||
const result = await validator.validate('user@gmail.com');
|
||||
if (result.freemail) {
|
||||
console.log('This is a free email provider');
|
||||
}
|
||||
```
|
||||
|
||||
## Creating and Using Smartmail Objects
|
||||
|
||||
### Basic Email Creation
|
||||
|
||||
```typescript
|
||||
// Create a simple email
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Hello from SmartMail',
|
||||
body: 'This is a plain text email'
|
||||
});
|
||||
```
|
||||
|
||||
### Using Creation Object Reference
|
||||
|
||||
The Smartmail constructor accepts a generic type parameter that lets you associate any additional data with your email, accessible later via the `getCreationObject()` method. This is useful for tracking, referencing original data sources, or maintaining context:
|
||||
|
||||
```typescript
|
||||
// Define your custom reference type
|
||||
interface OrderNotification {
|
||||
orderId: string;
|
||||
customerName: string;
|
||||
items: string[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
// Create email with typed creation object reference
|
||||
const orderEmail = new Smartmail<OrderNotification>({
|
||||
from: 'orders@example.com',
|
||||
to: ['customer@example.com'],
|
||||
subject: 'Your Order #{{orderId}} Confirmation',
|
||||
body: 'Thank you for your order, {{customerName}}!',
|
||||
// Store the full order data as reference
|
||||
creationObjectRef: {
|
||||
orderId: '12345',
|
||||
customerName: 'John Smith',
|
||||
items: ['Product A', 'Product B'],
|
||||
total: 99.95
|
||||
}
|
||||
});
|
||||
|
||||
// Later, retrieve the original reference data
|
||||
const orderData = orderEmail.getCreationObject();
|
||||
console.log(`Processing email for order ${orderData.orderId}`);
|
||||
console.log(`Order total: $${orderData.total}`);
|
||||
|
||||
// Use the reference data for templating
|
||||
const subject = orderEmail.getSubject(orderData); // "Your Order #12345 Confirmation"
|
||||
const body = orderEmail.getBody(orderData); // "Thank you for your order, John Smith!"
|
||||
```
|
||||
|
||||
This powerful feature allows you to:
|
||||
- Maintain a link to original data sources
|
||||
- Pass the email object between systems while preserving context
|
||||
- Avoid duplicating data between email content and your application
|
||||
- Use the reference data to fill template variables
|
||||
- Access metadata about the email that doesn't get included in the actual message
|
||||
|
||||
### Adding Recipients
|
||||
|
||||
```typescript
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
subject: 'Meeting Invitation',
|
||||
body: 'Please join our meeting'
|
||||
});
|
||||
|
||||
// Add recipients in various ways
|
||||
email.addRecipient('primary@example.com');
|
||||
email.addRecipients(['user1@example.com', 'user2@example.com']);
|
||||
email.addRecipient('manager@example.com', 'cc');
|
||||
email.addRecipients(['observer1@example.com', 'observer2@example.com'], 'bcc');
|
||||
```
|
||||
|
||||
### Template Variables in Subject and Body
|
||||
|
||||
```typescript
|
||||
const template = new Smartmail({
|
||||
from: 'notifications@example.com',
|
||||
subject: 'Welcome, {{name}}!',
|
||||
body: 'Hello {{name}},\n\nWelcome to our service. Your account ({{email}}) has been activated.',
|
||||
htmlBody: '<h1>Welcome, {{name}}!</h1><p>Hello {{name}},<br><br>Welcome to our service. Your account (<strong>{{email}}</strong>) has been activated.</p>'
|
||||
});
|
||||
|
||||
// Apply template variables
|
||||
const subject = template.getSubject({ name: 'John Doe' });
|
||||
const plainBody = template.getBody({ name: 'John Doe', email: 'john@example.com' });
|
||||
const htmlBody = template.getHtmlBody({ name: 'John Doe', email: 'john@example.com' });
|
||||
```
|
||||
|
||||
### Adding Attachments
|
||||
|
||||
```typescript
|
||||
import { Smartfile } from '@push.rocks/smartfile';
|
||||
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Report Attached',
|
||||
body: 'Please find the attached report.'
|
||||
});
|
||||
|
||||
// Add file attachments
|
||||
const report = new Smartfile.fromLocalPath('/path/to/report.pdf');
|
||||
const image = new Smartfile.fromLocalPath('/path/to/image.png');
|
||||
email.addAttachment(report);
|
||||
email.addAttachment(image);
|
||||
```
|
||||
|
||||
### Setting Email Importance and Headers
|
||||
|
||||
```typescript
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Urgent: System Alert',
|
||||
body: 'Critical system alert requires immediate attention.'
|
||||
});
|
||||
|
||||
// Set high priority
|
||||
email.setPriority('high');
|
||||
|
||||
// Add custom headers
|
||||
email.addHeader('X-Custom-ID', '12345');
|
||||
email.addHeader('X-System-Alert', 'Critical');
|
||||
```
|
||||
|
||||
### Converting to MIME Format for Sending
|
||||
|
||||
```typescript
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Hello {{name}}',
|
||||
body: 'Text version: Hello {{name}}',
|
||||
htmlBody: '<p>HTML version: Hello <strong>{{name}}</strong></p>',
|
||||
validateEmails: true // Will validate all emails before converting
|
||||
});
|
||||
|
||||
// Convert to MIME format with template data
|
||||
const mimeObj = await email.toMimeFormat({ name: 'John' });
|
||||
|
||||
// Result can be used with nodemailer or other email sending libraries
|
||||
console.log(mimeObj);
|
||||
/*
|
||||
{
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Hello John',
|
||||
text: 'Text version: Hello John',
|
||||
html: '<p>HTML version: Hello <strong>John</strong></p>',
|
||||
attachments: [...],
|
||||
headers: {...}
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### EmailAddressValidator
|
||||
|
||||
#### Constructor Options
|
||||
- `skipOnlineDomainFetch`: Boolean (default: false) - Skip fetching domain list from online source
|
||||
- `cacheDnsResults`: Boolean (default: true) - Cache DNS lookup results
|
||||
- `cacheExpiryMs`: Number (default: 3600000) - Cache expiry in milliseconds
|
||||
|
||||
#### Methods
|
||||
- `validate(email: string)`: Validates email address completeness
|
||||
- `isValidEmailFormat(email: string)`: Checks if email format is valid
|
||||
- `isValidLocalPart(localPart: string)`: Validates local part of email
|
||||
- `isValidDomainPart(domainPart: string)`: Validates domain part of email
|
||||
- `checkMxRecords(domain: string)`: Checks MX records for a domain
|
||||
|
||||
### Smartmail
|
||||
|
||||
#### Constructor Options
|
||||
- `from`: Email address of sender
|
||||
- `to`, `cc`, `bcc`: Optional arrays of recipient email addresses
|
||||
- `subject`: Email subject line
|
||||
- `body`: Plain text email body
|
||||
- `htmlBody`: Optional HTML version of email body
|
||||
- `replyTo`: Optional reply-to email address
|
||||
- `headers`: Optional key-value pairs of custom headers
|
||||
- `priority`: 'high' | 'normal' | 'low' (default: 'normal')
|
||||
- `validateEmails`: Boolean (default: false) - Validate all emails
|
||||
- `creationObjectRef`: Optional reference data of any type (generic T) - Store arbitrary data with the email
|
||||
|
||||
#### Methods
|
||||
- `addRecipient(email, type?)`: Add a single recipient (to/cc/bcc)
|
||||
- `addRecipients(emails, type?)`: Add multiple recipients
|
||||
- `setReplyTo(email)`: Set reply-to address
|
||||
- `setPriority(priority)`: Set email priority
|
||||
- `addHeader(name, value)`: Add custom header
|
||||
- `getSubject(data?)`: Get processed subject with template variables
|
||||
- `getBody(data?)`: Get processed plain text body
|
||||
- `getHtmlBody(data?)`: Get processed HTML body
|
||||
- `validateAllEmails()`: Validate all email addresses
|
||||
- `toMimeFormat(data?)`: Convert to MIME format object
|
||||
- `getCreationObject()`: Get the stored reference data of type T
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
### Trademarks
|
||||
|
||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
||||
|
||||
### Company Information
|
||||
|
||||
Task Venture Capital GmbH
|
||||
Registered at District court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
92
readme.plan.md
Normal file
92
readme.plan.md
Normal file
@ -0,0 +1,92 @@
|
||||
# Improvement Plan for SmartMail
|
||||
|
||||
## Current Code Analysis
|
||||
|
||||
### Core Components
|
||||
- **EmailAddressValidator**: Validates email addresses checking for validity, whether they are disposable or free
|
||||
- Uses MX record checks to validate domains
|
||||
- Has a domain map from external source that classifies domains as 'disposable' or 'freemail'
|
||||
- Has placeholder 'reason' field in results that's not implemented ("todo")
|
||||
- Fetches domain list from GitHub or falls back to local copy
|
||||
- No validation of email format before DNS checks
|
||||
|
||||
- **Smartmail**: Email representation class
|
||||
- Basic email structure with from, subject, body
|
||||
- Supports file attachments via smartfile
|
||||
- Uses mustache templates for subject and body
|
||||
- Lacks recipient handling (to, cc, bcc)
|
||||
- No method to send emails
|
||||
- No HTML body support
|
||||
|
||||
### Dependencies
|
||||
- Uses several @push.rocks packages:
|
||||
- smartdns: For DNS lookups
|
||||
- smartfile: For file handling/attachments
|
||||
- smartmustache: For template processing
|
||||
- smartpath: For path operations
|
||||
- smartrequest: For HTTP requests
|
||||
|
||||
### Testing
|
||||
- Basic tests for EmailAddressValidator
|
||||
- Tests validation of regular, free, and disposable emails
|
||||
- No tests for invalid email formats or edge cases
|
||||
- Minimal test for Smartmail creation
|
||||
- No tests for attachments, templates, or other features
|
||||
|
||||
## Enhanced Improvement Plan
|
||||
|
||||
1. [x] Complete the EmailAddressValidator implementation
|
||||
- [x] Implement proper reason messages for validation results
|
||||
- [x] Add email format validation before DNS checks (RFC 5322 compliance)
|
||||
- [x] Add local part validation (check for illegal characters, proper format)
|
||||
- [x] Improve domain validation (check syntax before DNS lookup)
|
||||
- [x] Add email normalization (handle case sensitivity, plus addressing)
|
||||
- [x] Implement caching mechanism for DNS lookups to improve performance
|
||||
- [x] Add option to disable online domain list fetching
|
||||
|
||||
2. [x] Enhance the Smartmail class
|
||||
- [x] Add support for multiple recipients (to, cc, bcc arrays)
|
||||
- [x] Add email preparing capabilities via MIME format
|
||||
- [x] Support HTML email bodies with plain text fallback
|
||||
- [x] Add reply-to and headers support
|
||||
- [x] Implement method to convert to standard email formats (MIME)
|
||||
- [x] Add email priority and importance flags
|
||||
- [x] Add validation of email addresses used in from/to/cc/bcc
|
||||
|
||||
3. [x] Improve testing
|
||||
- [x] Add tests for email format validation (valid/invalid formats)
|
||||
- [x] Test domain validation edge cases (non-existent domains, etc.)
|
||||
- [x] Add tests for attachment handling
|
||||
- [x] Test template processing with different data structures
|
||||
- [x] Add tests for HTML emails and conversion
|
||||
- [x] Test recipient handling with multiple addresses
|
||||
|
||||
4. [x] Performance & security improvements
|
||||
- [x] Optimize domain list handling
|
||||
- [x] Implement intelligent caching strategy for validation results
|
||||
- [x] Add configuration options for external service calls
|
||||
- [x] Ensure secure handling of email data and attachments
|
||||
|
||||
5. [x] Documentation improvements
|
||||
- [x] Update README with comprehensive examples
|
||||
- [x] Add detailed API documentation with JSDoc
|
||||
- [x] Document all configuration options
|
||||
- [x] Add usage examples for common scenarios
|
||||
- [x] Document security considerations
|
||||
- [x] Add TypeScript type documentation
|
||||
|
||||
6. [ ] Advanced features
|
||||
- [ ] DKIM/SPF validation support
|
||||
- [ ] Implement email address suggestions for typos
|
||||
- [ ] Add disposable email detection improvements
|
||||
- [ ] Support for internationalized email addresses (IDN)
|
||||
- [ ] Email address reputation checking
|
||||
- [ ] Add email deliverability scoring
|
||||
- [ ] Implement bounce address validation
|
||||
|
||||
7. [x] Code quality
|
||||
- [x] Add more TypeScript interfaces for clearer API definitions
|
||||
- [x] Improve error handling with specific error types
|
||||
- [x] Add configuration options via constructor
|
||||
- [x] Make domain list updates configurable
|
||||
- [x] Improve code organization with better separation of concerns
|
148
test/test.ts
148
test/test.ts
@ -1,9 +1,147 @@
|
||||
import { expect, tap } from '@pushrocks/tapbundle';
|
||||
import * as smartmail from '../ts/index';
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as smartmail from '../ts/index.js';
|
||||
import * as plugins from '../ts/smartmail.plugins.js';
|
||||
|
||||
tap.test('first test', async () => {
|
||||
const testSmartmail = new smartmail.Smartmail();
|
||||
expect(testSmartmail).to.be.instanceof(smartmail.Smartmail);
|
||||
let emailAddressValidatorInstance: smartmail.EmailAddressValidator;
|
||||
|
||||
// EmailAddressValidator Tests
|
||||
tap.test('should create an instance of EmailAddressValidator', async () => {
|
||||
emailAddressValidatorInstance = new smartmail.EmailAddressValidator();
|
||||
expect(emailAddressValidatorInstance).toBeInstanceOf(smartmail.EmailAddressValidator);
|
||||
});
|
||||
|
||||
tap.test('should validate an email with detailed information', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('sandbox@bleu.de');
|
||||
expect(result.freemail).toBeFalse();
|
||||
expect(result.disposable).toBeFalse();
|
||||
expect(result.formatValid).toBeTrue();
|
||||
expect(result.localPartValid).toBeTrue();
|
||||
expect(result.domainPartValid).toBeTrue();
|
||||
expect(result.reason).toBeDefined();
|
||||
});
|
||||
|
||||
tap.test('should recognize an email as freemail', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('sandbox@gmail.com');
|
||||
expect(result.freemail).toBeTrue();
|
||||
expect(result.disposable).toBeFalse();
|
||||
expect(result.formatValid).toBeTrue();
|
||||
expect(result.valid).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('should recognize an email as disposable', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('sandbox@gmx.de');
|
||||
expect(result.freemail).toBeFalse();
|
||||
expect(result.disposable).toBeTrue();
|
||||
expect(result.formatValid).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('should detect invalid email format', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('invalid-email');
|
||||
expect(result.formatValid).toBeFalse();
|
||||
expect(result.valid).toBeFalse();
|
||||
expect(result.reason).toEqual('Invalid email format');
|
||||
});
|
||||
|
||||
tap.test('should validate email format parts separately', async () => {
|
||||
expect(emailAddressValidatorInstance.isValidEmailFormat('valid.email@example.com')).toBeTrue();
|
||||
expect(emailAddressValidatorInstance.isValidEmailFormat('invalid-email')).toBeFalse();
|
||||
expect(emailAddressValidatorInstance.isValidLocalPart('valid.local.part')).toBeTrue();
|
||||
expect(emailAddressValidatorInstance.isValidLocalPart('invalid..part')).toBeFalse();
|
||||
expect(emailAddressValidatorInstance.isValidDomainPart('example.com')).toBeTrue();
|
||||
expect(emailAddressValidatorInstance.isValidDomainPart('invalid')).toBeFalse();
|
||||
});
|
||||
|
||||
// Smartmail Tests
|
||||
let testSmartmail: smartmail.Smartmail<any>;
|
||||
|
||||
tap.test('should create a SmartMail instance', async () => {
|
||||
testSmartmail = new smartmail.Smartmail({
|
||||
body: 'hi there',
|
||||
from: 'noreply@mail.lossless.com',
|
||||
subject: 'hi from here',
|
||||
});
|
||||
expect(testSmartmail).toBeInstanceOf(smartmail.Smartmail);
|
||||
});
|
||||
|
||||
tap.test('should handle email recipients', async () => {
|
||||
testSmartmail.addRecipient('user1@example.com');
|
||||
testSmartmail.addRecipients(['user2@example.com', 'user3@example.com'], 'cc');
|
||||
testSmartmail.addRecipient('user4@example.com', 'bcc');
|
||||
|
||||
expect(testSmartmail.options.to!.length).toEqual(1);
|
||||
expect(testSmartmail.options.cc!.length).toEqual(2);
|
||||
expect(testSmartmail.options.bcc!.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('should set reply-to and priority', async () => {
|
||||
testSmartmail.setReplyTo('replies@example.com');
|
||||
testSmartmail.setPriority('high');
|
||||
|
||||
expect(testSmartmail.options.replyTo).toEqual('replies@example.com');
|
||||
expect(testSmartmail.options.priority).toEqual('high');
|
||||
});
|
||||
|
||||
tap.test('should apply template data to subject and body', async () => {
|
||||
const templateMailer = new smartmail.Smartmail({
|
||||
subject: 'Hello {{name}}',
|
||||
body: 'Welcome, {{name}}! Your ID is {{userId}}.',
|
||||
from: 'noreply@example.com'
|
||||
});
|
||||
|
||||
const data = { name: 'John Doe', userId: '12345' };
|
||||
|
||||
expect(templateMailer.getSubject(data)).toEqual('Hello John Doe');
|
||||
expect(templateMailer.getBody(data)).toEqual('Welcome, John Doe! Your ID is 12345.');
|
||||
});
|
||||
|
||||
tap.test('should handle HTML email body', async () => {
|
||||
const htmlMailer = new smartmail.Smartmail({
|
||||
subject: 'HTML Test',
|
||||
body: 'Plain text version',
|
||||
htmlBody: '<h1>{{title}}</h1><p>This is an HTML email with {{variable}}.</p>',
|
||||
from: 'noreply@example.com'
|
||||
});
|
||||
|
||||
const data = { title: 'Welcome', variable: 'dynamic content' };
|
||||
const htmlContent = htmlMailer.getHtmlBody(data);
|
||||
|
||||
expect(htmlContent).toEqual('<h1>Welcome</h1><p>This is an HTML email with dynamic content.</p>');
|
||||
});
|
||||
|
||||
tap.test('should convert to MIME format', async () => {
|
||||
const mimeMailer = new smartmail.Smartmail({
|
||||
subject: 'MIME Test',
|
||||
body: 'Plain text content',
|
||||
htmlBody: '<p>HTML content</p>',
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
headers: { 'X-Custom': 'Value' }
|
||||
});
|
||||
|
||||
const mimeObj = await mimeMailer.toMimeFormat();
|
||||
|
||||
expect(mimeObj.from).toEqual('sender@example.com');
|
||||
expect(mimeObj.to).toInclude('recipient@example.com');
|
||||
expect(mimeObj.subject).toEqual('MIME Test');
|
||||
expect(mimeObj.text).toEqual('Plain text content');
|
||||
expect(mimeObj.html).toEqual('<p>HTML content</p>');
|
||||
expect(mimeObj.headers['X-Custom']).toEqual('Value');
|
||||
});
|
||||
|
||||
tap.test('should add email headers', async () => {
|
||||
const headerMailer = new smartmail.Smartmail({
|
||||
subject: 'Header Test',
|
||||
body: 'Test body',
|
||||
from: 'noreply@example.com'
|
||||
});
|
||||
|
||||
headerMailer.addHeader('X-Test-Header', 'TestValue');
|
||||
headerMailer.addHeader('X-Tracking-ID', '12345');
|
||||
|
||||
const mimeObj = await headerMailer.toMimeFormat();
|
||||
|
||||
expect(mimeObj.headers['X-Test-Header']).toEqual('TestValue');
|
||||
expect(mimeObj.headers['X-Tracking-ID']).toEqual('12345');
|
||||
});
|
||||
|
||||
tap.start();
|
||||
|
8
ts/00_commitinfo_data.ts
Normal file
8
ts/00_commitinfo_data.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartmail',
|
||||
version: '2.1.0',
|
||||
description: 'A unified format for representing and dealing with emails, with support for attachments and email validation.'
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './smartmail.classes.smartmail';
|
||||
export * from './smartmail.classes.smartmail.js';
|
||||
export * from './smartmail.classes.emailaddressvalidator.js';
|
||||
|
276
ts/smartmail.classes.emailaddressvalidator.ts
Normal file
276
ts/smartmail.classes.emailaddressvalidator.ts
Normal file
@ -0,0 +1,276 @@
|
||||
import * as plugins from './smartmail.plugins.js';
|
||||
import * as paths from './smartmail.paths.js';
|
||||
|
||||
export interface IEmailValidationResult {
|
||||
valid: boolean;
|
||||
disposable: boolean;
|
||||
freemail: boolean;
|
||||
reason: string;
|
||||
formatValid: boolean;
|
||||
mxValid: boolean;
|
||||
localPartValid: boolean;
|
||||
domainPartValid: boolean;
|
||||
}
|
||||
|
||||
export interface IEmailAddressValidatorOptions {
|
||||
skipOnlineDomainFetch?: boolean;
|
||||
cacheDnsResults?: boolean;
|
||||
cacheExpiryMs?: number;
|
||||
}
|
||||
|
||||
export class EmailAddressValidator {
|
||||
public domainMap: { [key: string]: 'disposable' | 'freemail' };
|
||||
public smartdns = new plugins.smartdns.Smartdns({});
|
||||
private dnsCache: Map<string, { result: any; timestamp: number }> = new Map();
|
||||
private options: IEmailAddressValidatorOptions;
|
||||
|
||||
constructor(optionsArg: IEmailAddressValidatorOptions = {}) {
|
||||
this.options = {
|
||||
skipOnlineDomainFetch: false,
|
||||
cacheDnsResults: true,
|
||||
cacheExpiryMs: 3600000, // 1 hour
|
||||
...optionsArg
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an email address format according to RFC 5322
|
||||
* @param emailArg The email address to validate
|
||||
* @returns True if the format is valid
|
||||
*/
|
||||
public isValidEmailFormat(emailArg: string): boolean {
|
||||
if (!emailArg) return false;
|
||||
|
||||
// RFC 5322 compliant regex pattern
|
||||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return emailRegex.test(emailArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the local part of an email address (before the @)
|
||||
* @param localPart The local part of the email address
|
||||
* @returns True if the local part is valid
|
||||
*/
|
||||
public isValidLocalPart(localPart: string): boolean {
|
||||
if (!localPart) return false;
|
||||
if (localPart.length > 64) return false;
|
||||
|
||||
// Check for illegal characters and patterns
|
||||
const illegalChars = /[^\w.!#$%&'*+/=?^`{|}~-]/;
|
||||
if (illegalChars.test(localPart)) return false;
|
||||
|
||||
// Check for consecutive dots or leading/trailing dots
|
||||
if (localPart.includes('..') || localPart.startsWith('.') || localPart.endsWith('.')) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the domain part of an email address (after the @)
|
||||
* @param domainPart The domain part of the email address
|
||||
* @returns True if the domain part is valid
|
||||
*/
|
||||
public isValidDomainPart(domainPart: string): boolean {
|
||||
if (!domainPart) return false;
|
||||
if (domainPart.length > 255) return false;
|
||||
|
||||
// Domain name validation regex
|
||||
const domainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
|
||||
// Must have at least one dot
|
||||
if (!domainPart.includes('.')) return false;
|
||||
|
||||
// Must end with a valid TLD (at least 2 chars)
|
||||
const parts = domainPart.split('.');
|
||||
const tld = parts[parts.length - 1];
|
||||
if (tld.length < 2) return false;
|
||||
|
||||
return domainRegex.test(domainPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs DNS MX record lookup for a domain
|
||||
* @param domain The domain to check
|
||||
* @returns MX records or null if none exist
|
||||
*/
|
||||
public async checkMxRecords(domain: string): Promise<any> {
|
||||
if (this.options.cacheDnsResults) {
|
||||
const cached = this.dnsCache.get(domain);
|
||||
if (cached && (Date.now() - cached.timestamp) < this.options.cacheExpiryMs!) {
|
||||
return cached.result;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.smartdns.getRecords(domain, 'MX');
|
||||
|
||||
if (this.options.cacheDnsResults) {
|
||||
this.dnsCache.set(domain, { result, timestamp: Date.now() });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MX records for a domain
|
||||
* @param domain The domain to get MX records for
|
||||
* @returns Array of MX records as strings
|
||||
*/
|
||||
public async getMxRecords(domain: string): Promise<string[]> {
|
||||
const mxRecords = await this.checkMxRecords(domain);
|
||||
|
||||
if (!mxRecords || !Array.isArray(mxRecords)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extract exchange values from MX records
|
||||
return mxRecords.map((record: any) => {
|
||||
if (record && record.exchange) {
|
||||
return record.exchange;
|
||||
}
|
||||
return '';
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an email is from a disposable domain
|
||||
* @param email The email address to check
|
||||
* @returns True if the email is from a disposable domain
|
||||
*/
|
||||
public async isDisposableEmail(email: string): Promise<boolean> {
|
||||
await this.fetchDomains();
|
||||
|
||||
if (!this.isValidEmailFormat(email)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const domainPart = email.split('@')[1];
|
||||
return this.domainMap[domainPart] === 'disposable';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an email is a role account (e.g. info@, support@, etc.)
|
||||
* @param email The email address to check
|
||||
* @returns True if the email is a role account
|
||||
*/
|
||||
public isRoleAccount(email: string): boolean {
|
||||
if (!this.isValidEmailFormat(email)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const localPart = email.split('@')[0].toLowerCase();
|
||||
const roleAccounts = [
|
||||
'admin', 'administrator', 'webmaster', 'hostmaster', 'postmaster',
|
||||
'info', 'support', 'sales', 'marketing', 'contact', 'help',
|
||||
'abuse', 'noc', 'security', 'billing', 'donations', 'donate',
|
||||
'staff', 'office', 'hr', 'jobs', 'careers', 'team',
|
||||
'enquiry', 'enquiries', 'feedback', 'no-reply', 'noreply'
|
||||
];
|
||||
|
||||
return roleAccounts.includes(localPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an email address
|
||||
* @param emailArg The email address to validate
|
||||
* @returns Validation result with details
|
||||
*/
|
||||
public async validate(emailArg: string): Promise<IEmailValidationResult> {
|
||||
await this.fetchDomains();
|
||||
|
||||
// Initialize result
|
||||
const result: IEmailValidationResult = {
|
||||
valid: false,
|
||||
reason: '',
|
||||
disposable: false,
|
||||
freemail: false,
|
||||
formatValid: false,
|
||||
mxValid: false,
|
||||
localPartValid: false,
|
||||
domainPartValid: false
|
||||
};
|
||||
|
||||
// Check overall email format
|
||||
const formatValid = this.isValidEmailFormat(emailArg);
|
||||
result.formatValid = formatValid;
|
||||
|
||||
if (!formatValid) {
|
||||
result.reason = 'Invalid email format';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Split email into local and domain parts
|
||||
const [localPart, domainPart] = emailArg.split('@');
|
||||
|
||||
// Validate local part
|
||||
const localPartValid = this.isValidLocalPart(localPart);
|
||||
result.localPartValid = localPartValid;
|
||||
|
||||
if (!localPartValid) {
|
||||
result.reason = 'Invalid local part (username)';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate domain part
|
||||
const domainPartValid = this.isValidDomainPart(domainPart);
|
||||
result.domainPartValid = domainPartValid;
|
||||
|
||||
if (!domainPartValid) {
|
||||
result.reason = 'Invalid domain part';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check MX records
|
||||
const mxRecords = await this.checkMxRecords(domainPart);
|
||||
result.mxValid = !!mxRecords;
|
||||
|
||||
if (!mxRecords) {
|
||||
result.reason = 'Domain does not have valid MX records';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if domain is disposable or free
|
||||
result.disposable = this.domainMap[domainPart] === 'disposable';
|
||||
result.freemail = this.domainMap[domainPart] === 'freemail';
|
||||
|
||||
if (result.disposable) {
|
||||
result.reason = 'Domain is a disposable email provider';
|
||||
} else if (result.freemail) {
|
||||
result.reason = 'Domain is a free email provider';
|
||||
} else {
|
||||
result.reason = 'Email is valid';
|
||||
}
|
||||
|
||||
// Email is valid if it has proper format and MX records
|
||||
result.valid = result.formatValid && result.mxValid;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the domain list for checking disposable and free email providers
|
||||
*/
|
||||
public async fetchDomains() {
|
||||
if (!this.domainMap) {
|
||||
const localFileString = plugins.smartfile.fs.toStringSync(
|
||||
plugins.path.join(paths.assetDir, 'domains.json')
|
||||
);
|
||||
const localFileObject = JSON.parse(localFileString);
|
||||
|
||||
if (this.options.skipOnlineDomainFetch) {
|
||||
this.domainMap = localFileObject;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const onlineFileObject = (
|
||||
await plugins.smartrequest.getJson(
|
||||
'https://raw.githubusercontent.com/romainsimon/emailvalid/master/domains.json'
|
||||
)
|
||||
).body;
|
||||
this.domainMap = onlineFileObject;
|
||||
} catch (e) {
|
||||
this.domainMap = localFileObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,283 @@
|
||||
import * as plugins from './smartmail.plugins';
|
||||
import * as plugins from './smartmail.plugins.js';
|
||||
import { EmailAddressValidator } from './smartmail.classes.emailaddressvalidator.js';
|
||||
|
||||
export type EmailAddress = string;
|
||||
export type EmailAddressList = EmailAddress[];
|
||||
|
||||
export interface ISmartmailOptions<T> {
|
||||
from: EmailAddress;
|
||||
to?: EmailAddressList;
|
||||
cc?: EmailAddressList;
|
||||
bcc?: EmailAddressList;
|
||||
replyTo?: EmailAddress;
|
||||
subject: string;
|
||||
body: string;
|
||||
htmlBody?: string;
|
||||
creationObjectRef?: T;
|
||||
headers?: Record<string, string>;
|
||||
priority?: 'high' | 'normal' | 'low';
|
||||
validateEmails?: boolean;
|
||||
}
|
||||
|
||||
export interface IMimeAttachment {
|
||||
filename: string;
|
||||
content: Buffer;
|
||||
contentType: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* a standard representation for mails
|
||||
* A standard representation for emails with advanced features
|
||||
*/
|
||||
export class Smartmail {
|
||||
from: string;
|
||||
to: string;
|
||||
body: string;
|
||||
attachments: any[];
|
||||
export class Smartmail<T> {
|
||||
public options: ISmartmailOptions<T>;
|
||||
public attachments: plugins.smartfile.SmartFile[] = [];
|
||||
private emailValidator: EmailAddressValidator;
|
||||
|
||||
constructor(optionsArg: ISmartmailOptions<T>) {
|
||||
// Set default options
|
||||
this.options = {
|
||||
validateEmails: false,
|
||||
to: [],
|
||||
cc: [],
|
||||
bcc: [],
|
||||
headers: {},
|
||||
priority: 'normal',
|
||||
...optionsArg
|
||||
};
|
||||
|
||||
this.emailValidator = new EmailAddressValidator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attachment to the email
|
||||
* @param smartfileArg The file to attach
|
||||
*/
|
||||
public addAttachment(smartfileArg: plugins.smartfile.SmartFile) {
|
||||
this.attachments.push(smartfileArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the creation object reference
|
||||
* @returns The creation object reference
|
||||
*/
|
||||
public getCreationObject(): T {
|
||||
return this.options.creationObjectRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the processed subject with template variables applied
|
||||
* @param dataArg Data to apply to the template
|
||||
* @returns Processed subject
|
||||
*/
|
||||
public getSubject(dataArg: any = {}): string {
|
||||
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.subject);
|
||||
return smartmustache.applyData(dataArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies variables to all template strings in the email
|
||||
* @param variables Variables to apply to templates
|
||||
*/
|
||||
public applyVariables(variables: Record<string, any>): void {
|
||||
if (!variables || typeof variables !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the subject, body, and HTML body with the provided variables
|
||||
if (this.options.subject) {
|
||||
const subjectMustache = new plugins.smartmustache.SmartMustache(this.options.subject);
|
||||
this.options.subject = subjectMustache.applyData(variables);
|
||||
}
|
||||
|
||||
if (this.options.body) {
|
||||
const bodyMustache = new plugins.smartmustache.SmartMustache(this.options.body);
|
||||
this.options.body = bodyMustache.applyData(variables);
|
||||
}
|
||||
|
||||
if (this.options.htmlBody) {
|
||||
const htmlBodyMustache = new plugins.smartmustache.SmartMustache(this.options.htmlBody);
|
||||
this.options.htmlBody = htmlBodyMustache.applyData(variables);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the processed plain text body with template variables applied
|
||||
* @param dataArg Data to apply to the template
|
||||
* @returns Processed body
|
||||
*/
|
||||
public getBody(dataArg: any = {}): string {
|
||||
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.body);
|
||||
return smartmustache.applyData(dataArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the processed HTML body with template variables applied
|
||||
* @param dataArg Data to apply to the template
|
||||
* @returns Processed HTML body or null if not set
|
||||
*/
|
||||
public getHtmlBody(dataArg: any = {}): string | null {
|
||||
if (!this.options.htmlBody) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.htmlBody);
|
||||
return smartmustache.applyData(dataArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient to the email
|
||||
* @param email Email address to add
|
||||
* @param type Type of recipient (to, cc, bcc)
|
||||
*/
|
||||
public addRecipient(email: EmailAddress, type: 'to' | 'cc' | 'bcc' = 'to'): void {
|
||||
if (!this.options[type]) {
|
||||
this.options[type] = [];
|
||||
}
|
||||
|
||||
this.options[type]!.push(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple recipients to the email
|
||||
* @param emails Email addresses to add
|
||||
* @param type Type of recipients (to, cc, bcc)
|
||||
*/
|
||||
public addRecipients(emails: EmailAddressList, type: 'to' | 'cc' | 'bcc' = 'to'): void {
|
||||
if (!this.options[type]) {
|
||||
this.options[type] = [];
|
||||
}
|
||||
|
||||
this.options[type] = [...this.options[type]!, ...emails];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the reply-to address
|
||||
* @param email Email address for reply-to
|
||||
*/
|
||||
public setReplyTo(email: EmailAddress): void {
|
||||
this.options.replyTo = email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority of the email
|
||||
* @param priority Priority level
|
||||
*/
|
||||
public setPriority(priority: 'high' | 'normal' | 'low'): void {
|
||||
this.options.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom header to the email
|
||||
* @param name Header name
|
||||
* @param value Header value
|
||||
*/
|
||||
public addHeader(name: string, value: string): void {
|
||||
if (!this.options.headers) {
|
||||
this.options.headers = {};
|
||||
}
|
||||
|
||||
this.options.headers[name] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all email addresses in the email
|
||||
* @returns Promise resolving to validation results
|
||||
*/
|
||||
public async validateAllEmails(): Promise<Record<string, boolean>> {
|
||||
const results: Record<string, boolean> = {};
|
||||
const emails: EmailAddress[] = [];
|
||||
|
||||
// Collect all emails
|
||||
if (this.options.from) emails.push(this.options.from);
|
||||
if (this.options.replyTo) emails.push(this.options.replyTo);
|
||||
if (this.options.to) emails.push(...this.options.to);
|
||||
if (this.options.cc) emails.push(...this.options.cc);
|
||||
if (this.options.bcc) emails.push(...this.options.bcc);
|
||||
|
||||
// Validate each email
|
||||
for (const email of emails) {
|
||||
const validationResult = await this.emailValidator.validate(email);
|
||||
results[email] = validationResult.valid;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the email to a MIME format object for sending
|
||||
* @param dataArg Data to apply to templates
|
||||
* @returns MIME format object
|
||||
*/
|
||||
public async toMimeFormat(dataArg: any = {}): Promise<any> {
|
||||
// Validate emails if option is enabled
|
||||
if (this.options.validateEmails) {
|
||||
const validationResults = await this.validateAllEmails();
|
||||
const invalidEmails = Object.entries(validationResults)
|
||||
.filter(([_, valid]) => !valid)
|
||||
.map(([email]) => email);
|
||||
|
||||
if (invalidEmails.length > 0) {
|
||||
throw new Error(`Invalid email addresses: ${invalidEmails.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Build MIME parts
|
||||
const subject = this.getSubject(dataArg);
|
||||
const textBody = this.getBody(dataArg);
|
||||
const htmlBody = this.getHtmlBody(dataArg);
|
||||
|
||||
// Convert attachments to MIME format
|
||||
const mimeAttachments: IMimeAttachment[] = await Promise.all(
|
||||
this.attachments.map(async (file) => {
|
||||
return {
|
||||
filename: file.path.split('/').pop()!,
|
||||
content: file.contentBuffer,
|
||||
contentType: 'application/octet-stream'
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Build email format object
|
||||
const mimeObj: any = {
|
||||
from: this.options.from,
|
||||
subject,
|
||||
text: textBody,
|
||||
attachments: mimeAttachments,
|
||||
headers: { ...this.options.headers }
|
||||
};
|
||||
|
||||
// Add optional fields
|
||||
if (this.options.to && this.options.to.length > 0) {
|
||||
mimeObj.to = this.options.to;
|
||||
}
|
||||
|
||||
if (this.options.cc && this.options.cc.length > 0) {
|
||||
mimeObj.cc = this.options.cc;
|
||||
}
|
||||
|
||||
if (this.options.bcc && this.options.bcc.length > 0) {
|
||||
mimeObj.bcc = this.options.bcc;
|
||||
}
|
||||
|
||||
if (this.options.replyTo) {
|
||||
mimeObj.replyTo = this.options.replyTo;
|
||||
}
|
||||
|
||||
if (htmlBody) {
|
||||
mimeObj.html = htmlBody;
|
||||
}
|
||||
|
||||
// Add priority headers if specified
|
||||
if (this.options.priority === 'high') {
|
||||
mimeObj.headers['X-Priority'] = '1';
|
||||
mimeObj.headers['X-MSMail-Priority'] = 'High';
|
||||
mimeObj.headers['Importance'] = 'High';
|
||||
} else if (this.options.priority === 'low') {
|
||||
mimeObj.headers['X-Priority'] = '5';
|
||||
mimeObj.headers['X-MSMail-Priority'] = 'Low';
|
||||
mimeObj.headers['Importance'] = 'Low';
|
||||
}
|
||||
|
||||
return mimeObj;
|
||||
}
|
||||
}
|
||||
|
7
ts/smartmail.paths.ts
Normal file
7
ts/smartmail.paths.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as plugins from './smartmail.plugins.js';
|
||||
|
||||
export const packageDir = plugins.path.join(
|
||||
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
||||
'../'
|
||||
);
|
||||
export const assetDir = plugins.path.join(packageDir, './assets');
|
@ -1,2 +1,13 @@
|
||||
const removeme = {};
|
||||
export { removeme };
|
||||
// node native scope
|
||||
import * as path from 'path';
|
||||
|
||||
export { path };
|
||||
|
||||
// pushrocks scope
|
||||
import * as smartdns from '@push.rocks/smartdns/client';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartmustache from '@push.rocks/smartmustache';
|
||||
import * as smartpath from '@push.rocks/smartpath';
|
||||
import * as smartrequest from '@push.rocks/smartrequest';
|
||||
|
||||
export { smartdns, smartfile, smartmustache, smartpath, smartrequest };
|
||||
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist_ts",
|
||||
"rootDir": "./ts",
|
||||
"strict": false,
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"skipLibCheck": false
|
||||
},
|
||||
"include": [
|
||||
"ts/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"dist_ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": ["tslint:latest", "tslint-config-prettier"],
|
||||
"rules": {
|
||||
"semicolon": [true, "always"]
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user