56 Commits

Author SHA1 Message Date
4277ace8cd 2.1.0 2025-05-07 14:57:50 +00:00
f08544d53a feat(smartmail): Add new email validation helper methods (getMxRecords, isDisposableEmail, isRoleAccount) and an applyVariables method to Smartmail for dynamic templating. 2025-05-07 14:57:50 +00:00
ffe324a9dc 2.0.1 2025-05-07 13:50:10 +00:00
70a6ad040a fix(readme): Update documentation to include usage of creation object reference and update API details. 2025-05-07 13:50:10 +00:00
31f0789648 2.0.0 2025-05-07 13:18:41 +00:00
e395a059a6 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. 2025-05-07 13:18:41 +00:00
442bc5a9d9 update description 2024-05-29 14:14:32 +02:00
5b0f09361b update tsconfig 2024-04-14 17:55:42 +02:00
8d2d9c3806 update npmextra.json: githost 2024-04-01 21:36:24 +02:00
a9ec4449d9 update npmextra.json: githost 2024-04-01 19:58:52 +02:00
862b0d87ce update npmextra.json: githost 2024-03-30 21:47:52 +01:00
1e095d325b 1.0.24 2023-07-28 05:49:45 +02:00
515be51253 fix(core): update 2023-07-28 05:49:44 +02:00
64eb16880a switch to new org scheme 2023-07-10 10:16:18 +02:00
2d2166cf40 1.0.23 2023-06-13 23:23:12 +02:00
2dd803fdc2 fix(core): update 2023-06-13 23:23:11 +02:00
bbcfa63944 1.0.22 2022-08-07 18:49:18 +02:00
015d2903b2 fix(core): update 2022-08-07 18:49:18 +02:00
f66deb9cd4 1.0.21 2022-08-07 18:09:20 +02:00
b786e44bf7 fix(core): update 2022-08-07 18:09:19 +02:00
11376cbca2 1.0.20 2022-08-07 11:41:06 +02:00
4d62773747 fix(core): update 2022-08-07 11:41:05 +02:00
69d8293972 1.0.19 2022-08-07 11:38:08 +02:00
e25c885ddf fix(core): update 2022-08-07 11:38:07 +02:00
93a9bf437d 1.0.18 2020-08-10 21:05:08 +00:00
a7c6f75cc5 fix(core): update 2020-08-10 21:05:07 +00:00
f67750ff0f 1.0.17 2020-06-18 21:04:52 +00:00
ee25cfc78f fix(core): update 2020-06-18 21:04:52 +00:00
ff5ab4def0 1.0.16 2020-06-18 16:24:28 +00:00
b418c11827 fix(core): update 2020-06-18 16:24:28 +00:00
e643cf714e 1.0.15 2020-06-18 16:21:06 +00:00
5a82307afe fix(core): update 2020-06-18 16:21:06 +00:00
9abbcac166 1.0.14 2020-06-18 16:19:23 +00:00
5db2cd1077 fix(core): update 2020-06-18 16:19:22 +00:00
3de9178cd0 1.0.13 2020-06-18 15:34:06 +00:00
2b1f8a2718 fix(core): update 2020-06-18 15:34:05 +00:00
2117c2f5d9 1.0.12 2020-01-21 10:05:36 +00:00
ca0daf2454 fix(core): update 2020-01-21 10:05:35 +00:00
ad6d18d306 1.0.11 2020-01-13 15:52:28 +00:00
7ddcd88862 fix(core): update 2020-01-13 15:52:27 +00:00
9d6b7f3b29 1.0.10 2020-01-13 15:52:03 +00:00
8be9fc38b6 fix(core): update 2020-01-13 15:52:02 +00:00
6bb3bb7d9a 1.0.9 2020-01-13 15:19:48 +00:00
7fcbd39166 fix(core): update 2020-01-13 15:19:47 +00:00
1e38d6564d 1.0.8 2020-01-12 18:57:15 +00:00
9b3c0e88da fix(core): update 2020-01-12 18:57:15 +00:00
9ba1d442b4 1.0.7 2020-01-12 18:32:44 +00:00
b130f09a80 fix(core): update 2020-01-12 18:32:43 +00:00
31ea0863e8 1.0.6 2019-10-28 15:38:44 +01:00
0210bdf4df fix(core): update 2019-10-28 15:38:44 +01:00
7b3734f79e 1.0.5 2019-10-26 23:45:36 +02:00
1244956187 fix(core): update 2019-10-26 23:45:35 +02:00
390e0f7b9e 1.0.4 2018-09-30 12:36:17 +02:00
a63c26c05d fix(npm): access level 2018-09-30 12:36:17 +02:00
3782b93837 1.0.3 2018-09-30 00:20:37 +02:00
8ccc5e93b1 fix(packagename): update 2018-09-30 00:20:37 +02:00
25 changed files with 22234 additions and 1041 deletions

View 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

View 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
View File

@ -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

View File

@ -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
View 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
View 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

File diff suppressed because it is too large Load Diff

48
changelog.md Normal file
View 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 versionmarking 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
View 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

View File

@ -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
View File

@ -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
}
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

1
readme.hints.md Normal file
View File

@ -0,0 +1 @@

302
readme.md Normal file
View 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
View 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

View File

@ -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
View 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.'
}

View File

@ -1 +1,2 @@
export * from './smartmail.classes.smartmail';
export * from './smartmail.classes.smartmail.js';
export * from './smartmail.classes.emailaddressvalidator.js';

View 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;
}
}
}
}

View File

@ -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
View 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');

View File

@ -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
View 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"
]
}

View File

@ -1,7 +0,0 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"]
}
}