fix(core): update
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
.nogit/
|
||||
|
||||
# artifacts
|
||||
coverage/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
|
||||
# caches
|
||||
.yarn/
|
||||
.cache/
|
||||
.rpt2_cache
|
||||
|
||||
# builds
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
||||
111
.gitlab-ci.yml
Normal file
111
.gitlab-ci.yml
Normal file
@@ -0,0 +1,111 @@
|
||||
# gitzone ci_docker
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .npmci-cache/
|
||||
key: '$CI_BUILD_STAGE'
|
||||
|
||||
before_script:
|
||||
- npm install -g @shipzone/npmci
|
||||
- npmci npm prepare
|
||||
|
||||
stages:
|
||||
- security
|
||||
- test
|
||||
- release
|
||||
- metadata
|
||||
- pages
|
||||
|
||||
# ====================
|
||||
# security stage
|
||||
# ====================
|
||||
|
||||
auditProductionDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --production --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=prod --production
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
|
||||
auditDevDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=dev
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
allow_failure: true
|
||||
|
||||
# ====================
|
||||
# test stage
|
||||
# ====================
|
||||
|
||||
testStable:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
|
||||
testBuild:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci command npm run build
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
# ====================
|
||||
# release stage
|
||||
# ====================
|
||||
|
||||
release:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-dbase:npmci
|
||||
services:
|
||||
- docker:stable-dind
|
||||
stage: release
|
||||
script:
|
||||
- npmci docker login
|
||||
- npmci docker build
|
||||
- npmci docker test
|
||||
- npmci docker push registry.gitlab.com
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- priv
|
||||
|
||||
# ====================
|
||||
# metadata stage
|
||||
# ====================
|
||||
|
||||
trigger:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci trigger
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
38
Dockerfile
Normal file
38
Dockerfile
Normal file
@@ -0,0 +1,38 @@
|
||||
# gitzone dockerfile_service
|
||||
## STAGE 1 // BUILD
|
||||
FROM registry.gitlab.com/hosttoday/ht-docker-node:npmci as node1
|
||||
COPY ./ /app
|
||||
WORKDIR /app
|
||||
ARG NPMCI_TOKEN_NPM2
|
||||
ENV NPMCI_TOKEN_NPM2 $NPMCI_TOKEN_NPM2
|
||||
RUN npmci npm prepare
|
||||
RUN rm -rf node_modules && npm install
|
||||
RUN npm run build
|
||||
|
||||
# gitzone dockerfile_service
|
||||
## STAGE 2 // install production
|
||||
FROM registry.gitlab.com/hosttoday/ht-docker-node:npmci as node2
|
||||
WORKDIR /app
|
||||
COPY --from=node1 /app /app
|
||||
ARG NPMCI_TOKEN_NPM2
|
||||
ENV NPMCI_TOKEN_NPM2 $NPMCI_TOKEN_NPM2
|
||||
RUN npmci npm prepare
|
||||
RUN rm -r node_modules/ && npm install --production
|
||||
|
||||
## STAGE 3 // rebuild dependencies for alpine
|
||||
FROM registry.gitlab.com/hosttoday/ht-docker-node:alpine as node3
|
||||
WORKDIR /app
|
||||
COPY --from=node2 /app /app
|
||||
RUN npm rebuild --production
|
||||
|
||||
## STAGE 4 // the final production image with all dependencies in place
|
||||
FROM registry.gitlab.com/hosttoday/ht-docker-node:alpine as node4
|
||||
WORKDIR /app
|
||||
COPY --from=node3 /app /app
|
||||
|
||||
### Healthchecks
|
||||
RUN npm install -g @servezone/healthy
|
||||
HEALTHCHECK --interval=30s --timeout=30s --start-period=30s --retries=3 CMD [ "healthy" ]
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["npm", "start"]
|
||||
93
README.md
93
README.md
@@ -1,93 +0,0 @@
|
||||
# npmcdn
|
||||
|
||||
a cdn mapping to packages on npm
|
||||
|
||||
## Getting started
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
- [ ] [Create](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin https://gitlab.com/losslessone/services/servezone/npmcdn.git
|
||||
git branch -M master
|
||||
git push -uf origin master
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
|
||||
- [ ] [Set up project integrations](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://gitlab.com/losslessone/services/servezone/npmcdn/-/settings/integrations)
|
||||
|
||||
## Collaborate with your team
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Automatically merge when pipeline succeeds](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||
|
||||
## Test and Deploy
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
|
||||
***
|
||||
|
||||
# Editing this README
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://gitlab.com/-/experiment/new_project_readme_content:0ba4da2bb83083f24ae1c5b3d0992b34?https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
|
||||
4
cli.js
Normal file
4
cli.js
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
process.env.CLI_CALL = 'true';
|
||||
const cliTool = require('./dist_ts/index');
|
||||
cliTool.runCli();
|
||||
5
cli.ts.js
Normal file
5
cli.ts.js
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
process.env.CLI_CALL = 'true';
|
||||
require('@gitzone/tsrun');
|
||||
const cliTool = require('./ts/index');
|
||||
cliTool.runCli();
|
||||
25
npmextra.json
Normal file
25
npmextra.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"gitzone": {
|
||||
"projectType": "service",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
"gitscope": "losslessone/services/servezone",
|
||||
"gitrepo": "npmcdn",
|
||||
"description": "a cdn using npm as source",
|
||||
"npmPackagename": "npmcdn",
|
||||
"license": "UNLICENSED",
|
||||
"projectDomain": "npmcdn.lossless.one",
|
||||
"shortDescription": "undefined variable"
|
||||
}
|
||||
},
|
||||
"npmci": {
|
||||
"npmGlobalTools": [],
|
||||
"dockerRegistryRepoMap": {
|
||||
"registry.gitlab.com": "losslessone/services/servezone/npmcdn"
|
||||
},
|
||||
"dockerBuildargEnvMap": {
|
||||
"NPMCI_TOKEN_NPM2": "NPMCI_TOKEN_NPM2"
|
||||
},
|
||||
"npmRegistryUrl": "verdaccio.lossless.one"
|
||||
}
|
||||
}
|
||||
30969
package-lock.json
generated
Normal file
30969
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
65
package.json
Normal file
65
package.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "npmcdn",
|
||||
"version": "1.0.1",
|
||||
"description": "a cdn using npm as source",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"author": "Lossless GmbH",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"test": "(tstest test/)",
|
||||
"start": "(node --max_old_space_size=100 ./cli.js)",
|
||||
"startTs": "(node cli.ts.js)",
|
||||
"build": "(tsbuild --web)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gitzone/tsbuild": "^2.1.17",
|
||||
"@gitzone/tsrun": "^1.2.8",
|
||||
"@gitzone/tstest": "^1.0.28",
|
||||
"@gitzone/tswatch": "^1.0.30",
|
||||
"@pushrocks/tapbundle": "^3.0.13",
|
||||
"tslint": "^5.20.0",
|
||||
"tslint-config-prettier": "^1.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@losslessone_private/lole-serviceserver": "^1.0.24",
|
||||
"@pushrocks/smartstate": "^1.0.14",
|
||||
"@pushrocks/qenv": "^4.0.10",
|
||||
"@pushrocks/smartdata": "^4.0.12",
|
||||
"@pushrocks/projectinfo": "^4.0.5",
|
||||
"@pushrocks/smartdelay": "^2.0.13",
|
||||
"@pushrocks/smartfile": "^8.0.10",
|
||||
"@pushrocks/smartmarkdown": "^2.0.6",
|
||||
"@pushrocks/smartmime": "^1.0.5",
|
||||
"@pushrocks/smartnpm": "^1.0.39",
|
||||
"@pushrocks/smartpromise": "^3.1.6",
|
||||
"@pushrocks/smarttime": "^3.0.38",
|
||||
"@pushrocks/tapbundle": "^3.2.14",
|
||||
"@types/autocannon": "^4.1.1",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/splunk-logging": "^0.9.2",
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.17.1",
|
||||
"lit-ntml": "^2.20.0",
|
||||
"prom-client": "^13.2.0",
|
||||
"splunk-logging": "^0.11.1",
|
||||
"tty-table": "^4.1.3"
|
||||
},
|
||||
"private": true,
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
],
|
||||
"files": [
|
||||
"ts/**/*",
|
||||
"ts_web/**/*",
|
||||
"dist/**/*",
|
||||
"dist_*/**/*",
|
||||
"dist_ts/**/*",
|
||||
"dist_ts_web/**/*",
|
||||
"assets/**/*",
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
"readme.md"
|
||||
]
|
||||
}
|
||||
34
readme.md
Normal file
34
readme.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# @losslessone/services/servezone/npmcdn
|
||||
undefined variable
|
||||
|
||||
## Availabililty and Links
|
||||
* [npmjs.org (npm package)](https://www.npmjs.com/package/npmcdn)
|
||||
* [gitlab.com (source)](https://gitlab.com/losslessone/services/servezone/npmcdn)
|
||||
* [github.com (source mirror)](https://github.com/losslessone/services/servezone/npmcdn)
|
||||
* [docs (typedoc)](https://losslessone/services/servezone.gitlab.io/npmcdn/)
|
||||
|
||||
## Status for master
|
||||
|
||||
Status Category | Status Badge
|
||||
-- | --
|
||||
GitLab Pipelines | [](https://lossless.cloud)
|
||||
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
||||
npm | [](https://lossless.cloud)
|
||||
Snyk | [](https://lossless.cloud)
|
||||
TypeScript Support | [](https://lossless.cloud)
|
||||
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
||||
Code Style | [](https://lossless.cloud)
|
||||
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
||||
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
||||
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
||||
Platform support | [](https://lossless.cloud) [](https://lossless.cloud)
|
||||
|
||||
## Usage
|
||||
|
||||
Use TypeScript for best in class intellisense
|
||||
For further information read the linked docs at the top of this readme.
|
||||
|
||||
> UNLICENSED licensed | **©** [Lossless GmbH](https://lossless.gmbh)
|
||||
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
|
||||
|
||||
[](https://maintainedby.lossless.com)
|
||||
9
test/devserver.ts
Normal file
9
test/devserver.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { UiPublicServer } from '../ts';
|
||||
|
||||
export const defaultPublicServer = new UiPublicServer({
|
||||
port: 3000,
|
||||
packageBaseDirectory: './ts',
|
||||
npmRegistryUrl: 'https://registry.npmjs.org',
|
||||
allowedPackages: ['@pushrocks/smartfile', '@pushrocks/smarturl', '@pushrocks/notthere'],
|
||||
mode: 'dev',
|
||||
});
|
||||
99
test/test.node.ts
Normal file
99
test/test.node.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { tap, expect } from '@pushrocks/tapbundle';
|
||||
|
||||
import * as uiPublicServer from '../ts/npm-publicserver.classes.uipublicserver';
|
||||
import * as smartnetwork from '@pushrocks/smartnetwork';
|
||||
import * as smartrequest from '@pushrocks/smartrequest';
|
||||
|
||||
const plugins = {
|
||||
smartnetwork,
|
||||
smartrequest,
|
||||
};
|
||||
|
||||
let testserverInstance: uiPublicServer.UiPublicServer;
|
||||
|
||||
tap.test('should create an instance of PublicServer', async () => {
|
||||
testserverInstance = new uiPublicServer.UiPublicServer({
|
||||
npmRegistryUrl: 'https://registry.npmjs.org',
|
||||
port: 3000,
|
||||
allowedPackages: ['@pushrocks/smartfile'],
|
||||
mode: 'prod',
|
||||
});
|
||||
expect(testserverInstance).to.be.instanceOf(uiPublicServer.UiPublicServer);
|
||||
});
|
||||
|
||||
tap.test('should start the server', async () => {
|
||||
const smartnetworkInstance = new plugins.smartnetwork.SmartNetwork();
|
||||
const result1 = await smartnetworkInstance.isLocalPortUnused(3000);
|
||||
await testserverInstance.startServer();
|
||||
const result2 = await smartnetworkInstance.isLocalPortUnused(3000);
|
||||
expect(result1).to.be.true;
|
||||
expect(result2).to.be.false;
|
||||
});
|
||||
|
||||
tap.skip.test('optional manual testing', async (toolsArg) => {
|
||||
await toolsArg.delayFor(30000);
|
||||
});
|
||||
|
||||
tap.test('should NOT deliver a file for a malformed org', async () => {
|
||||
const response = await plugins.smartrequest.request('http://localhost:3000/someorg/somemodule', {
|
||||
method: 'GET',
|
||||
});
|
||||
console.log(response.body);
|
||||
expect(response.body).to.equal('npmorg "someorg" must start with @');
|
||||
});
|
||||
|
||||
tap.test('should NOT deliver a file for a nonexisting file', async () => {
|
||||
const response = await plugins.smartrequest.request(
|
||||
'http://localhost:3000/@pushrocks/smartfile/readme2.md',
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
);
|
||||
console.log(response.body);
|
||||
expect(response.body.includes('@pushrocks/smartfile@ does not have a file at')).to.be.true;
|
||||
});
|
||||
|
||||
tap.test('should deliver a file for an existing file', async () => {
|
||||
const response = await plugins.smartrequest.request(
|
||||
'http://localhost:3000/@pushrocks/smartfile/readme.md',
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
);
|
||||
expect(response.body.startsWith('# @pushrocks/smartfile')).to.be.true;
|
||||
});
|
||||
|
||||
tap.test('should deliver different versions', async () => {
|
||||
const response = await plugins.smartrequest.request(
|
||||
'http://localhost:3000/@pushrocks/smartfile/package.json?version=7',
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
);
|
||||
const packageJson = response.body;
|
||||
expect(packageJson.version.startsWith('7')).to.be.true;
|
||||
|
||||
const response2 = await plugins.smartrequest.request(
|
||||
'http://localhost:3000/@pushrocks/smartfile/package.json?version=8.x.x',
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
);
|
||||
const packageJson2 = response2.body;
|
||||
expect(packageJson2.version.startsWith('8')).to.be.true;
|
||||
|
||||
const response3 = await plugins.smartrequest.request(
|
||||
'http://localhost:3000/@pushrocks/smartfile/package.json?version=6.0.6',
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
);
|
||||
const packageJson3 = response3.body;
|
||||
expect(packageJson3.version).to.equal('6.0.6');
|
||||
});
|
||||
|
||||
tap.test('should stop the server', async () => {
|
||||
await testserverInstance.stopServer();
|
||||
});
|
||||
|
||||
tap.start();
|
||||
22
ts/index.ts
Normal file
22
ts/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export * from './npm-publicserver.classes.uipublicserver';
|
||||
|
||||
import { UiPublicServer } from '.';
|
||||
|
||||
process.env.UIP_ENV = process.env.BACKEND_URL.includes('develop-backend') ? 'dev' : 'prod';
|
||||
|
||||
export const defaultPublicServer = new UiPublicServer({
|
||||
port: 3000,
|
||||
packageBaseDirectory: './public/',
|
||||
npmRegistryUrl: 'https://registry.npmjs.org/',
|
||||
allowedPackages: [
|
||||
'@pushrocks/smartfile'
|
||||
],
|
||||
mode: process.env.UIP_ENV === 'dev' ? 'dev' : 'prod',
|
||||
log: false,
|
||||
});
|
||||
|
||||
export const runCli = async () => {
|
||||
|
||||
}
|
||||
|
||||
export const stop = async () => {}
|
||||
12
ts/interfaces.ts
Normal file
12
ts/interfaces.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as url from 'url';
|
||||
export interface ISimpleRequest {
|
||||
headers: {};
|
||||
parsedUrl: url.URL;
|
||||
}
|
||||
|
||||
export interface ISimpleResponse {
|
||||
headers: { [key: string]: string };
|
||||
status: number;
|
||||
body: string | Buffer;
|
||||
}
|
||||
export type IRenderFunction = (req: ISimpleRequest) => Promise<ISimpleResponse>;
|
||||
20
ts/logging.ts
Normal file
20
ts/logging.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import * as plugins from './plugins';
|
||||
|
||||
export const logger = new plugins.splunkLogging.Logger({
|
||||
token: '',
|
||||
});
|
||||
|
||||
logger.send({
|
||||
message: {
|
||||
package: '',
|
||||
subFolder: '',
|
||||
filePath: '',
|
||||
status: 200,
|
||||
},
|
||||
severity: 'info',
|
||||
metadata: {
|
||||
host: 'ui-publicserver',
|
||||
source: 'nodejs',
|
||||
sourcetype: 'process',
|
||||
},
|
||||
});
|
||||
403
ts/npm-publicserver.classes.uipublicserver.ts
Normal file
403
ts/npm-publicserver.classes.uipublicserver.ts
Normal file
@@ -0,0 +1,403 @@
|
||||
import * as interfaces from './interfaces';
|
||||
import * as plugins from './plugins';
|
||||
import * as paths from './paths';
|
||||
import * as ntml from './ntml';
|
||||
|
||||
export interface IPublicServerOptions {
|
||||
packageBaseDirectory?: string;
|
||||
npmRegistryUrl?: string;
|
||||
allowedPackages?: string[];
|
||||
port?: number;
|
||||
mode: 'dev' | 'prod';
|
||||
log?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* the main public server instance
|
||||
*/
|
||||
export class UiPublicServer {
|
||||
public projectinfo = new plugins.projectinfo.ProjectinfoNpm(paths.packageDir);
|
||||
public readme = new plugins.smartmarkdown.SmartMarkdown().markdownToHtml(
|
||||
plugins.smartfile.fs.toStringSync(plugins.path.join(paths.packageDir, 'readme.md'))
|
||||
);
|
||||
public startedAt: string;
|
||||
private server: plugins.http.Server;
|
||||
private npmRegistry: plugins.smartnpm.NpmRegistry;
|
||||
|
||||
private corsDev = '*';
|
||||
private corsProd = ''; // TODO: Define allowed URLs
|
||||
|
||||
public defaultOptions: IPublicServerOptions = {
|
||||
packageBaseDirectory: './',
|
||||
npmRegistryUrl: 'https://registry.npmjs.org',
|
||||
allowedPackages: [],
|
||||
port: 8080,
|
||||
mode: 'dev',
|
||||
log: true,
|
||||
};
|
||||
public options: IPublicServerOptions;
|
||||
|
||||
constructor(optionsArg?: IPublicServerOptions) {
|
||||
// lets create an npm instance for the registry that we are talking to
|
||||
this.options = {
|
||||
...this.defaultOptions,
|
||||
...optionsArg,
|
||||
};
|
||||
|
||||
if (!this.options.packageBaseDirectory.endsWith('/')) {
|
||||
this.options.packageBaseDirectory += '/';
|
||||
}
|
||||
|
||||
this.npmRegistry = new plugins.smartnpm.NpmRegistry({
|
||||
npmRegistryUrl: this.options.npmRegistryUrl,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* starts the server
|
||||
*/
|
||||
public async startServer() {
|
||||
console.log('starting the uipublicserver');
|
||||
const done = plugins.smartpromise.defer();
|
||||
const expressApplication = plugins.express();
|
||||
|
||||
const shouldCompress = (req: plugins.express.Request, res: plugins.express.Response) => {
|
||||
if (req.headers['x-no-compression']) {
|
||||
// don't compress responses with this request header
|
||||
return false;
|
||||
}
|
||||
|
||||
// fallback to standard filter function
|
||||
return plugins.compression.filter(req, res);
|
||||
};
|
||||
|
||||
expressApplication.use(plugins.compression({ filter: shouldCompress }));
|
||||
|
||||
if (this.options.mode === 'dev') {
|
||||
expressApplication.use('/peek/', async (req, res) => {
|
||||
const host = req.headers.host || 'localhost';
|
||||
const parsedUrl = new plugins.url.URL(`http://${host}${req.url}`);
|
||||
const simpleResponse: interfaces.ISimpleResponse = await this.renderPeek({
|
||||
headers: req.headers,
|
||||
parsedUrl,
|
||||
});
|
||||
for (const header of Object.keys(simpleResponse.headers)) {
|
||||
res.setHeader(header, simpleResponse.headers[header]);
|
||||
}
|
||||
res.status(simpleResponse.status);
|
||||
res.write(simpleResponse.body);
|
||||
res.end();
|
||||
});
|
||||
|
||||
expressApplication.use('/readme/', async (req, res) => {
|
||||
const host = req.headers.host || 'localhost';
|
||||
const parsedUrl = new plugins.url.URL(`http://${host}${req.url}`);
|
||||
const simpleResponse: interfaces.ISimpleResponse = await this.renderReadme({
|
||||
headers: req.headers,
|
||||
parsedUrl,
|
||||
});
|
||||
for (const header of Object.keys(simpleResponse.headers)) {
|
||||
res.setHeader(header, simpleResponse.headers[header]);
|
||||
}
|
||||
res.status(simpleResponse.status);
|
||||
res.write(simpleResponse.body);
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
|
||||
expressApplication.use('/', async (req, res) => {
|
||||
const host = req.headers.host || 'localhost';
|
||||
const parsedUrl = new plugins.url.URL(`http://${host}${req.url}`);
|
||||
const simpleResponse: interfaces.ISimpleResponse = await this.render({
|
||||
headers: req.headers,
|
||||
parsedUrl,
|
||||
});
|
||||
for (const header of Object.keys(simpleResponse.headers)) {
|
||||
res.setHeader(header, simpleResponse.headers[header]);
|
||||
}
|
||||
// CORS
|
||||
res.setHeader('ui-public-server-version', this.projectinfo.version);
|
||||
res.setHeader('access-control-allow-origin', true ? this.corsDev : this.corsProd); // TODO: replace true with check for env
|
||||
res.status(simpleResponse.status);
|
||||
res.write(simpleResponse.body);
|
||||
res.end();
|
||||
});
|
||||
|
||||
// note: We assume that ssl termination is done in reverse proxy in kubernetes. so simple http should suffice here.
|
||||
this.server = expressApplication.listen(this.options.port, () => {
|
||||
console.log(`listening on port ${this.options.port}`);
|
||||
this.startedAt = new Date().toISOString();
|
||||
done.resolve();
|
||||
});
|
||||
await done.promise;
|
||||
}
|
||||
|
||||
public disableLogging() {
|
||||
this.options.log = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* stops the server
|
||||
*/
|
||||
public async stopServer() {
|
||||
const done = plugins.smartpromise.defer();
|
||||
if (!this.server) {
|
||||
console.log('server does not seem to be started, so no need to stop it');
|
||||
return;
|
||||
}
|
||||
this.server.close(() => {
|
||||
done.resolve();
|
||||
});
|
||||
await done.promise;
|
||||
}
|
||||
|
||||
public requestMap: { [key: string]: plugins.smartpromise.Deferred<any> } = {};
|
||||
|
||||
/**
|
||||
* renders a response using interfaces.ISimpleRequest and interfaces.ISimpleResponse
|
||||
* @param req
|
||||
*/
|
||||
private render: interfaces.IRenderFunction = async (req) => {
|
||||
const pathArray = req.parsedUrl.pathname.split('/');
|
||||
|
||||
if (pathArray.length < 3) {
|
||||
const textArray = [
|
||||
`serving javascript to the web for Lossless GmbH.<br/>`,
|
||||
`Running in mode: <b>${this.options.mode}</b><br/><br/>`,
|
||||
];
|
||||
if (this.options.mode === 'dev') {
|
||||
textArray.push(
|
||||
`<b>Wondering what we serve?</b> <a href="/peek/">Lets take a peek!</a><br/>`
|
||||
);
|
||||
textArray.push(`<b>Documentation:</b> <a href="/readme/">Read the docs</a>`);
|
||||
}
|
||||
return {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
status: 201,
|
||||
body: await plugins.litNtml.html`
|
||||
${ntml.getBody(this, textArray)}
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
// lets care about the npm organization
|
||||
const npmOrg = pathArray[1];
|
||||
if (!npmOrg.startsWith('@')) {
|
||||
console.log('malformed npmorg');
|
||||
return {
|
||||
status: 500,
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
body: `npmorg "${npmOrg}" must start with @`,
|
||||
};
|
||||
}
|
||||
|
||||
// lets care about the packageName
|
||||
const npmPackage = pathArray[2];
|
||||
const packageName = `${npmOrg}/${npmPackage}`;
|
||||
if (this.options.log) {
|
||||
console.log(
|
||||
`got a request for package ${packageName} on registry ${this.npmRegistry.options.npmRegistryUrl}`
|
||||
);
|
||||
}
|
||||
if (!this.options.allowedPackages.includes(packageName)) {
|
||||
return {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
status: 503,
|
||||
body: await plugins.litNtml.html`
|
||||
the requested package is not allowlisted for public access
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
// lets care about the inner package path
|
||||
let filePath = this.options.packageBaseDirectory;
|
||||
let first = true;
|
||||
for (let i = 3; i < pathArray.length; i++) {
|
||||
if (first) {
|
||||
filePath += './';
|
||||
first = false;
|
||||
} else {
|
||||
filePath += '/';
|
||||
}
|
||||
filePath += pathArray[i];
|
||||
}
|
||||
|
||||
// lets care about version and disttag
|
||||
|
||||
const version = req.parsedUrl.searchParams.get('version') || '';
|
||||
const distTag = req.parsedUrl.searchParams.get('disttag') || '';
|
||||
|
||||
const requestDescriptor = `${packageName}/${filePath}/${distTag}/${version}`;
|
||||
|
||||
let smartfile: plugins.smartfile.Smartfile;
|
||||
|
||||
// protect against parallel requests
|
||||
if (this.requestMap[requestDescriptor]) {
|
||||
smartfile = await this.requestMap[requestDescriptor].promise;
|
||||
} else {
|
||||
this.requestMap[requestDescriptor] = plugins.smartpromise.defer();
|
||||
smartfile = await this.npmRegistry
|
||||
.getFileFromPackage(packageName, filePath, {
|
||||
version,
|
||||
distTag,
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
return null;
|
||||
});
|
||||
this.requestMap[requestDescriptor].resolve(smartfile);
|
||||
delete this.requestMap[requestDescriptor];
|
||||
}
|
||||
|
||||
if (!smartfile) {
|
||||
return {
|
||||
status: 404,
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
body: await ntml.getBody(
|
||||
this,
|
||||
`${packageName}@${version} does not have a file at "${filePath}"`
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
headers: {
|
||||
'cache-control': `max-age=${
|
||||
(version
|
||||
? plugins.smarttime.getMilliSecondsFromUnits({ months: 1 })
|
||||
: plugins.smarttime.getMilliSecondsFromUnits({ days: 1 })) / 1000
|
||||
}`,
|
||||
'content-length': smartfile.contentBuffer.length.toString(),
|
||||
'content-type': plugins.smartmime.detectMimeType(smartfile.path),
|
||||
},
|
||||
status: 200,
|
||||
body: smartfile.contentBuffer,
|
||||
};
|
||||
};
|
||||
|
||||
private renderPeek: interfaces.IRenderFunction = async (req) => {
|
||||
const pathArray = req.parsedUrl.pathname.split('/');
|
||||
const npmOrg = pathArray[1];
|
||||
const npmPackage = pathArray[2];
|
||||
const npmPackageName = `${npmOrg}/${npmPackage}`;
|
||||
const distTag = req.parsedUrl.searchParams.get('disttag');
|
||||
const version = req.parsedUrl.searchParams.get('version');
|
||||
|
||||
// lets care about cors
|
||||
|
||||
if (!npmOrg || !npmPackage || !this.options.allowedPackages.includes(npmPackageName)) {
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
body: `
|
||||
${await ntml.getBody(this, [
|
||||
`<a href="../">../ -> Go back</a><br/>`,
|
||||
`<b>allowlisted packages:</b><br/>`,
|
||||
...this.options.allowedPackages.map((packageArg) => {
|
||||
return `
|
||||
<div>"${packageArg}" -> mapping to "${this.options.packageBaseDirectory}" inside the package <a href="/peek/${packageArg}">Peek...</a></div>
|
||||
`;
|
||||
}),
|
||||
])}
|
||||
`,
|
||||
};
|
||||
}
|
||||
const result = await this.npmRegistry
|
||||
.getFilesFromPackage(npmPackageName, plugins.path.join(this.options.packageBaseDirectory), {
|
||||
version: version,
|
||||
distTag: distTag,
|
||||
})
|
||||
.catch((err) => {
|
||||
return;
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
status: 404,
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
body: `
|
||||
${await ntml.getBody(this, [
|
||||
`<a href="/peek/">/peek/ -> Go to peek overview</a><br/>`,
|
||||
`<b>Not found: package ${npmPackageName}@${
|
||||
version || 'latest'
|
||||
} is not available on the supplied registry</b><br/>`,
|
||||
])}
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
const packageInfo = await this.npmRegistry.getPackageInfo(npmPackageName);
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
body: `
|
||||
${await ntml.getBody(this, [
|
||||
`<a href="/peek/">/peek/ -> Go to peek overview</a><br/><br/>`,
|
||||
`<h1>${npmPackageName}@${version || distTag || '<i>inferred</i> latest'}</h1>`,
|
||||
`<b>Versions:</b> ${packageInfo.allVersions
|
||||
.map(
|
||||
(versionArg) =>
|
||||
`<a href="./${npmPackage}?version=${versionArg.version}">${versionArg.version}</a> | `
|
||||
)
|
||||
.join('')}<br/><br/>`,
|
||||
`<b>DistTags:</b> ${
|
||||
packageInfo.allDistTags
|
||||
.map(
|
||||
(distTagArg) =>
|
||||
`<a href="./${npmPackage}?disttag=${distTagArg.name}">${distTagArg.name} (${distTagArg.targetVersion})</a> | `
|
||||
)
|
||||
.join('') || 'no dist tags found!'
|
||||
}<br/><br/>`,
|
||||
`<b>File Overview at version and path:</b><br/>`,
|
||||
`"<b>${npmPackageName}@${
|
||||
version || distTag || '<i>inferred</i> latest'
|
||||
}</b>" under internal path "<b>${this.options.packageBaseDirectory}</b>"<br/></br>`,
|
||||
...result.map((smartfile) => {
|
||||
const displayPath = smartfile.path.replace(
|
||||
plugins.path.join('package', this.options.packageBaseDirectory),
|
||||
''
|
||||
);
|
||||
return `<a href="/${npmPackageName}/${displayPath}?version=${version || ''}&disttag=${
|
||||
distTag || ''
|
||||
}">${displayPath}</a><br/>`;
|
||||
}),
|
||||
])}
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
private renderReadme: interfaces.IRenderFunction = async (req) => {
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
body: await ntml.getBody(this, [
|
||||
`<style>
|
||||
pre {
|
||||
display: block;
|
||||
background: #fafafa;
|
||||
border: 1px dotted #CCC;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>`,
|
||||
this.readme,
|
||||
]),
|
||||
};
|
||||
};
|
||||
}
|
||||
59
ts/ntml/body.ts
Normal file
59
ts/ntml/body.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { UiPublicServer } from '../npm-publicserver.classes.uipublicserver';
|
||||
import * as plugins from '../plugins';
|
||||
export const getBody = async (uipublicServerArg: UiPublicServer, contentArg: string | string[]) => {
|
||||
return await plugins.litNtml.html`
|
||||
<head></head>
|
||||
<body>
|
||||
<style>
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 20px;
|
||||
background: #eeeeeb;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.main {
|
||||
background: #ffffff;
|
||||
max-width: 1000px;
|
||||
margin: auto;
|
||||
border: 1px dashed #CCCCCC;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
padding-bottom: 60px;
|
||||
position:relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.logo {
|
||||
text-align: center;
|
||||
background: #fafafa;
|
||||
border-bottom: 1px dotted #CCCCCC;
|
||||
margin-left: -20px;
|
||||
margin-top: -20px;
|
||||
margin-right: -20px;
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
<div class="main">
|
||||
<div class="logo">
|
||||
<img src="https://assetbroker.lossless.one/brandfiles/lossless/svg-minimal-bright.svg" onclick="window.location.href = 'https://lossless.com'" />
|
||||
</div>
|
||||
${contentArg}
|
||||
<div class="footer">
|
||||
UiPublicServer v${uipublicServerArg.projectinfo.version} |
|
||||
running since ${uipublicServerArg.startedAt} |
|
||||
<a href="https://lossless.gmbh" target="_blank">Legal Info</a></div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
};
|
||||
1
ts/ntml/index.ts
Normal file
1
ts/ntml/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './body';
|
||||
3
ts/paths.ts
Normal file
3
ts/paths.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import * as plugins from './plugins';
|
||||
|
||||
export const packageDir = plugins.path.join(__dirname, '../');
|
||||
36
ts/plugins.ts
Normal file
36
ts/plugins.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// node native
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import * as path from 'path';
|
||||
|
||||
export { http, url, path };
|
||||
|
||||
// @pushrocks scope (maintained by Lossless GmbH)
|
||||
import * as projectinfo from '@pushrocks/projectinfo';
|
||||
import * as smartdelay from '@pushrocks/smartdelay';
|
||||
import * as smartfile from '@pushrocks/smartfile';
|
||||
import * as smartmime from '@pushrocks/smartmime';
|
||||
import * as smartmarkdown from '@pushrocks/smartmarkdown';
|
||||
import * as smartnpm from '@pushrocks/smartnpm';
|
||||
import * as smartpromise from '@pushrocks/smartpromise';
|
||||
import * as smarttime from '@pushrocks/smarttime';
|
||||
|
||||
export {
|
||||
projectinfo,
|
||||
smartdelay,
|
||||
smartfile,
|
||||
smartmime,
|
||||
smartmarkdown,
|
||||
smartnpm,
|
||||
smartpromise,
|
||||
smarttime,
|
||||
};
|
||||
|
||||
// unscoped packages
|
||||
import compression from 'compression';
|
||||
import express from 'express';
|
||||
import * as litNtml from 'lit-ntml';
|
||||
import * as promClient from 'prom-client';
|
||||
import splunkLogging from 'splunk-logging';
|
||||
|
||||
export { compression, express, litNtml, promClient, splunkLogging };
|
||||
17
tslint.json
Normal file
17
tslint.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": ["tslint:latest", "tslint-config-prettier"],
|
||||
"rules": {
|
||||
"semicolon": [true, "always"],
|
||||
"no-console": false,
|
||||
"ordered-imports": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"member-ordering": {
|
||||
"options":{
|
||||
"order": [
|
||||
"static-method"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
||||
Reference in New Issue
Block a user