Compare commits

...

76 Commits

Author SHA1 Message Date
14bfa3f62f 3.0.26 2024-03-01 00:14:34 +01:00
9e4413c276 fix(core): update 2024-03-01 00:14:34 +01:00
dd91691064 3.0.25 2024-02-29 18:58:10 +01:00
bf4794c06f fix(core): update 2024-02-29 18:58:09 +01:00
7e04474a66 3.0.24 2024-02-24 18:33:33 +01:00
907d51a842 fix(core): update 2024-02-24 18:33:33 +01:00
2114ff28c0 3.0.23 2024-02-21 01:16:39 +01:00
fd9431f82b fix(core): update 2024-02-21 01:16:39 +01:00
e8c1a66e15 3.0.22 2024-02-21 01:06:53 +01:00
1e98fd99f4 fix(core): update 2024-02-21 01:06:52 +01:00
da6aa9827c 3.0.21 2024-02-20 17:30:47 +01:00
ca3b8a4580 fix(core): update 2024-02-20 17:30:46 +01:00
a7ddb6b6a8 3.0.20 2024-01-19 20:52:00 +01:00
e1dfe30273 fix(core): update 2024-01-19 20:51:59 +01:00
754fa38fe8 3.0.19 2024-01-09 11:38:52 +01:00
7e59146e73 fix(core): update 2024-01-09 11:38:51 +01:00
3b550eacf7 3.0.18 2024-01-09 11:38:19 +01:00
6342895320 fix(core): update 2024-01-09 11:38:18 +01:00
b6a4095a53 3.0.17 2024-01-09 11:38:06 +01:00
622da78180 fix(core): update 2024-01-09 11:38:06 +01:00
21938e5f20 3.0.16 2024-01-09 10:25:04 +01:00
99427f5835 fix(core): update 2024-01-09 10:25:03 +01:00
552a15bb2f 3.0.15 2024-01-09 10:21:02 +01:00
b0efc48b96 fix(core): update 2024-01-09 10:21:01 +01:00
8c3aad69a0 3.0.14 2024-01-09 10:14:07 +01:00
fb2692b50e fix(core): update 2024-01-09 10:14:06 +01:00
65c868aefe 3.0.13 2024-01-08 15:34:54 +01:00
11df25f028 fix(core): update 2024-01-08 15:34:53 +01:00
efb4229f58 3.0.12 2024-01-08 14:43:12 +01:00
61dcc6badc fix(core): update 2024-01-08 14:43:11 +01:00
585da9bc79 3.0.11 2024-01-08 14:35:35 +01:00
60a2efaecb fix(core): update 2024-01-08 14:35:34 +01:00
2c8f550830 3.0.10 2024-01-07 14:50:14 +01:00
c9688159e5 fix(core): update 2024-01-07 14:50:14 +01:00
a710473d33 3.0.9 2023-11-06 13:56:03 +01:00
61c62672fc fix(core): update 2023-11-06 13:56:02 +01:00
1a7150e1f8 3.0.8 2023-11-06 11:29:53 +01:00
f35360adba fix(core): update 2023-11-06 11:29:52 +01:00
9774567dc0 3.0.7 2023-10-23 16:52:32 +02:00
529c5feeb1 fix(core): update 2023-10-23 16:52:31 +02:00
d2cac36a6e 3.0.6 2023-10-20 18:13:11 +02:00
2cdef55f13 fix(core): update 2023-10-20 18:13:10 +02:00
05444b757b 3.0.5 2023-09-21 00:48:05 +02:00
1ef5c0da06 fix(core): update 2023-09-21 00:48:04 +02:00
00b4108803 3.0.4 2023-08-06 18:15:02 +02:00
7e75cccbcb fix(core): update 2023-08-06 18:15:01 +02:00
f93d10d394 3.0.3 2023-08-06 17:45:31 +02:00
d949a05c79 fix(core): update 2023-08-06 17:45:30 +02:00
d281569bbb 3.0.2 2023-08-06 15:51:35 +02:00
ef06dd138e fix(core): update 2023-08-06 15:51:34 +02:00
60b610fc4a 3.0.1 2023-08-03 20:50:18 +02:00
b4e9bd5174 fix(core): update 2023-08-03 20:50:18 +02:00
1754524184 3.0.0 2023-08-03 20:45:10 +02:00
618f382ce9 BREAKING CHANGE(core): update 2023-08-03 20:45:09 +02:00
ef9883f100 2.0.65 2023-07-02 11:35:37 +02:00
99db788d11 fix(core): update 2023-07-02 11:35:36 +02:00
f7966e1f58 2.0.64 2023-07-02 02:09:04 +02:00
9828f7bc13 fix(core): update 2023-07-02 02:09:04 +02:00
dab87b274d 2.0.63 2023-07-02 01:53:11 +02:00
85171cb736 fix(core): update 2023-07-02 01:53:10 +02:00
0fd5e0a209 2.0.62 2023-07-02 01:46:53 +02:00
eadab07f17 fix(core): update 2023-07-02 01:46:53 +02:00
378592acc3 2.0.61 2023-07-02 01:38:50 +02:00
f885e49e34 fix(core): update 2023-07-02 01:38:49 +02:00
078730153d 2.0.60 2023-07-02 01:23:41 +02:00
4467ab76aa fix(core): update 2023-07-02 01:23:41 +02:00
a0bbf31f75 2.0.59 2023-07-01 18:25:27 +02:00
13e9ac7a98 fix(core): update 2023-07-01 18:25:27 +02:00
0ec00a5404 2.0.58 2023-07-01 17:23:50 +02:00
b0f48ba598 fix(core): update 2023-07-01 17:23:49 +02:00
ec4a51668c 2.0.57 2023-07-01 12:29:36 +02:00
07739bec27 fix(core): update 2023-07-01 12:29:35 +02:00
9aebd59c08 2.0.56 2023-07-01 11:55:35 +02:00
be7f4c503e fix(core): update 2023-07-01 11:55:35 +02:00
e1e1d4bf65 2.0.55 2023-07-01 11:49:40 +02:00
20ecb86a9e fix(core): update 2023-07-01 11:49:39 +02:00
31 changed files with 3034 additions and 1432 deletions

View File

@ -0,0 +1,67 @@
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,110 @@
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: 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
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: 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: Code quality
run: |
npmci command npm install -g typescript
npmci npm prepare
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

View File

@ -9,7 +9,7 @@
"gitscope": "pushrocks",
"gitrepo": "typedserver",
"description": "easy serving of static files",
"npmPackagename": "@apiglobal/typedserver",
"npmPackagename": "@api.global/typedserver",
"license": "MIT"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@apiglobal/typedserver",
"version": "2.0.54",
"name": "@api.global/typedserver",
"version": "3.0.26",
"description": "easy serving of static files",
"main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
@ -37,41 +37,44 @@
],
"homepage": "https://github.com/pushrocks/easyserve",
"dependencies": {
"@apiglobal/typedrequest": "^2.0.12",
"@apiglobal/typedrequest-interfaces": "^2.0.1",
"@apiglobal/typedsocket": "^2.0.24",
"@pushrocks/lik": "^6.0.2",
"@pushrocks/smartchok": "^1.0.23",
"@pushrocks/smartdelay": "^3.0.1",
"@pushrocks/smartenv": "^5.0.5",
"@pushrocks/smartfeed": "^1.0.11",
"@pushrocks/smartfile": "^10.0.7",
"@pushrocks/smartlog": "^3.0.1",
"@pushrocks/smartlog-destination-devtools": "^1.0.10",
"@pushrocks/smartmanifest": "^2.0.2",
"@pushrocks/smartmime": "^1.0.5",
"@pushrocks/smartopen": "^2.0.0",
"@pushrocks/smartpath": "^5.0.5",
"@pushrocks/smartpromise": "^4.0.2",
"@pushrocks/smartrequest": "^2.0.15",
"@pushrocks/smartrx": "^3.0.2",
"@pushrocks/smartsitemap": "^2.0.1",
"@pushrocks/smarttime": "^4.0.1",
"@pushrocks/webstore": "^2.0.8",
"@tsclass/tsclass": "^4.0.42",
"@api.global/typedrequest": "^3.0.17",
"@api.global/typedrequest-interfaces": "^3.0.18",
"@api.global/typedsocket": "^3.0.0",
"@push.rocks/lik": "^6.0.14",
"@push.rocks/smartchok": "^1.0.33",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartenv": "^5.0.12",
"@push.rocks/smartfeed": "^1.0.11",
"@push.rocks/smartfile": "^11.0.4",
"@push.rocks/smartjson": "^5.0.11",
"@push.rocks/smartlog": "^3.0.3",
"@push.rocks/smartlog-destination-devtools": "^1.0.10",
"@push.rocks/smartmanifest": "^2.0.2",
"@push.rocks/smartmime": "^1.0.5",
"@push.rocks/smartopen": "^2.0.0",
"@push.rocks/smartpath": "^5.0.11",
"@push.rocks/smartpromise": "^4.0.2",
"@push.rocks/smartrequest": "^2.0.21",
"@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartsitemap": "^2.0.3",
"@push.rocks/smartstream": "^3.0.33",
"@push.rocks/smarttime": "^4.0.6",
"@push.rocks/webstore": "^2.0.13",
"@tsclass/tsclass": "^4.0.52",
"@types/express": "^4.17.21",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"express": "^4.18.3",
"express-force-ssl": "^0.3.2",
"lit": "^2.7.5"
"lit": "^3.1.2"
},
"devDependencies": {
"@gitzone/tsbuild": "^2.1.66",
"@gitzone/tsbundle": "^2.0.8",
"@gitzone/tsrun": "^1.2.42",
"@gitzone/tstest": "^1.0.72",
"@pushrocks/tapbundle": "^5.0.4",
"@types/node": "^20.3.0"
"@git.zone/tsbuild": "^2.1.72",
"@git.zone/tsbundle": "^2.0.15",
"@git.zone/tsrun": "^1.2.44",
"@git.zone/tstest": "^1.0.86",
"@push.rocks/tapbundle": "^5.0.15",
"@types/node": "^20.11.24"
},
"private": false,
"browserslist": [

3658
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
# @apiglobal/typedserver
# @pushrocks/typedserver
easy serving of static files
## Availabililty and Links

View File

@ -1,5 +1,5 @@
import { tap, expect } from '@pushrocks/tapbundle';
import * as smartpath from '@pushrocks/smartpath';
import { tap, expect } from '@push.rocks/tapbundle';
import * as smartpath from '@push.rocks/smartpath';
import { TypedServer } from '../ts/index.js';

View File

@ -1,11 +1,11 @@
// tslint:disable-next-line:no-implicit-dependencies
import { expect, tap } from '@pushrocks/tapbundle';
import { expect, tap } from '@push.rocks/tapbundle';
// helper dependencies
// tslint:disable-next-line:no-implicit-dependencies
import * as smartpath from '@pushrocks/smartpath';
import * as smartrequest from '@pushrocks/smartrequest';
import * as smartpath from '@push.rocks/smartpath';
import * as smartrequest from '@push.rocks/smartrequest';
import * as typedserver from '../ts/index.js';
@ -27,6 +27,13 @@ tap.test('should create a valid Server', async () => {
manifest: {
name: 'Test App',
short_name: 'testapp',
start_url: '/',
display: 'standalone',
background_color: '#000',
theme_color: '#000',
scope: '/',
lang: 'en',
display_override: ['window-controls-overlay'],
},
feed: true,
sitemap: true,
@ -64,12 +71,14 @@ tap.test('should add handler to route', async () => {
tap.test('should create a valid StaticHandler', async () => {
testRoute2.addHandler(
new typedserver.servertools.HandlerStatic(smartpath.get.dirnameFromImportMetaUrl(import.meta.url))
new typedserver.servertools.HandlerStatic(
smartpath.get.dirnameFromImportMetaUrl(import.meta.url)
)
);
});
tap.test('should add typedrequest and typedsocket', async () => {
const typedrequest = await import('@apiglobal/typedrequest');
const typedrequest = await import('@api.global/typedrequest');
const typedrouter = new typedrequest.TypedRouter();
testServer.addTypedRequest(typedrouter);

View File

@ -2,7 +2,7 @@
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@apiglobal/typedserver',
version: '2.0.54',
name: '@api.global/typedserver',
version: '3.0.26',
description: 'easy serving of static files'
}

View File

@ -1,10 +1,8 @@
import * as plugins from './typedserver.plugins.js';
import * as plugins from './plugins.js';
import * as servertools from './servertools/index.js';
export {
servertools
}
export { servertools };
export * from './typedserver.classes.typedserver.js';
// Type helpers

View File

@ -1,11 +1,11 @@
export type TResponseModifier = <T>(responseArg: {
headers: { [header: string]: number | string | string[] | undefined };
path: string;
responseContent: string;
responseContent: Buffer;
travelData?: T;
}) => Promise<{
headers: { [header: string]: number | string | string[] | undefined };
path: string;
responseContent: string;
responseContent: Buffer;
travelData?: T;
}>;

View File

@ -1,23 +1,26 @@
import * as typedrequestInterfaces from '@apiglobal/typedrequest-interfaces';
// not using the global plugins here to support better bundling...
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
export interface IReq_PushLatestServerChangeTime extends typedrequestInterfaces.implementsTR<
typedrequestInterfaces.ITypedRequest,
IReq_PushLatestServerChangeTime
> {
method: 'pushLatestServerChangeTime',
export interface IReq_PushLatestServerChangeTime
extends typedrequestInterfaces.implementsTR<
typedrequestInterfaces.ITypedRequest,
IReq_PushLatestServerChangeTime
> {
method: 'pushLatestServerChangeTime';
request: {
time: number;
};
response: {}
response: {};
}
export interface IReq_GetLatestServerChangeTime extends typedrequestInterfaces.implementsTR<
typedrequestInterfaces.ITypedRequest,
IReq_GetLatestServerChangeTime
> {
method: 'getLatestServerChangeTime',
export interface IReq_GetLatestServerChangeTime
extends typedrequestInterfaces.implementsTR<
typedrequestInterfaces.ITypedRequest,
IReq_GetLatestServerChangeTime
> {
method: 'getLatestServerChangeTime';
request: {};
response: {
time: number;
}
};
}

67
ts/plugins.ts Normal file
View File

@ -0,0 +1,67 @@
// node native
import * as http from 'http';
import * as https from 'https';
import * as net from 'net';
import * as path from 'path';
import * as stream from 'stream';
import * as zlib from 'zlib';
export { http, https, net, path, zlib };
// @tsclass scope
import * as tsclass from '@tsclass/tsclass';
export { tsclass };
// @apiglobal scope
import * as typedrequest from '@api.global/typedrequest';
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
import * as typedsocket from '@api.global/typedsocket';
export { typedrequest, typedrequestInterfaces, typedsocket };
// @pushrocks scope
import * as lik from '@push.rocks/lik';
import * as smartchok from '@push.rocks/smartchok';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartfeed from '@push.rocks/smartfeed';
import * as smartfile from '@push.rocks/smartfile';
import * as smartjson from '@push.rocks/smartjson';
import * as smartmanifest from '@push.rocks/smartmanifest';
import * as smartmime from '@push.rocks/smartmime';
import * as smartopen from '@push.rocks/smartopen';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrequest from '@push.rocks/smartrequest';
import * as smartrx from '@push.rocks/smartrx';
import * as smartsitemap from '@push.rocks/smartsitemap';
import * as smartstream from '@push.rocks/smartstream';
import * as smarttime from '@push.rocks/smarttime';
export {
lik,
smartchok,
smartdelay,
smartfeed,
smartfile,
smartjson,
smartmanifest,
smartmime,
smartopen,
smartpath,
smartpromise,
smartrequest,
smartsitemap,
smartstream,
smarttime,
smartrx,
};
// express
import bodyParser from 'body-parser';
import cors from 'cors';
import express from 'express';
// @ts-ignore
import expressForceSsl from 'express-force-ssl';
export { bodyParser, cors, express, expressForceSsl };

View File

@ -0,0 +1,127 @@
import * as plugins from '../plugins.js';
export type TCompressionMethod = 'gzip' | 'deflate' | 'br' | 'none';
export interface ICompressionResult {
result: Buffer;
compressionMethod: TCompressionMethod;
}
export class Compressor {
private _cache: Map<string, Buffer>;
private MAX_CACHE_SIZE: number = 100 * 1024 * 1024; // 100 MB
constructor() {
this._cache = new Map<string, Buffer>();
}
private _addToCache(key: string, value: Buffer) {
this._cache.set(key, value);
this._manageCacheSize();
}
private _manageCacheSize() {
let currentSize = Array.from(this._cache.values()).reduce((acc, buffer) => acc + buffer.length, 0);
while (currentSize > this.MAX_CACHE_SIZE) {
const firstKey = this._cache.keys().next().value;
const firstValue = this._cache.get(firstKey)!;
currentSize -= firstValue.length;
this._cache.delete(firstKey);
}
}
public async compressContent(
content: Buffer,
method: 'gzip' | 'deflate' | 'br' | 'none'
): Promise<Buffer> {
const cacheKey = content.toString('base64') + method;
const cachedResult = this._cache.get(cacheKey);
if (cachedResult) {
return cachedResult;
}
return new Promise((resolve, reject) => {
const callback = (err: Error | null, result: Buffer) => {
if (err) reject(err);
else {
this._addToCache(cacheKey, result);
resolve(result);
}
};
switch (method) {
case 'gzip':
plugins.zlib.gzip(content, {
level: 1,
},callback,);
break;
case 'br':
plugins.zlib.brotliCompress(content, {}, callback);
break;
case 'deflate':
plugins.zlib.deflate(content, callback);
break;
default:
this._addToCache(cacheKey, content);
resolve(content);
}
});
}
public determineCompression(acceptEncoding: string | string[], preferredCompressionMethodsArg: TCompressionMethod[] = []) {
// Ensure acceptEncoding is a single string
const encodingString = Array.isArray(acceptEncoding)
? acceptEncoding.join(', ')
: acceptEncoding;
let compressionMethod: TCompressionMethod = 'none';
// Prioritize preferred compression methods if provided
for (const preferredMethod of preferredCompressionMethodsArg) {
if (new RegExp(`\\b${preferredMethod}\\b`).test(encodingString)) {
return preferredMethod;
}
}
// Fallback to default prioritization if no preferred method matches
if (/\bbr\b/.test(encodingString)) {
compressionMethod = 'br';
} else if (/\bgzip\b/.test(encodingString)) {
compressionMethod = 'gzip';
} else if (/\bdeflate\b/.test(encodingString)) {
compressionMethod = 'deflate';
}
return compressionMethod;
}
public async maybeCompress(requestHeaders: plugins.http.IncomingHttpHeaders, content: Buffer, preferredCompressionMethodsArg?: TCompressionMethod[]): Promise<ICompressionResult> {
const acceptEncoding = requestHeaders['accept-encoding'];
const compressionMethod = this.determineCompression(acceptEncoding, preferredCompressionMethodsArg);
const result = await this.compressContent(content, compressionMethod);
return {
result,
compressionMethod,
};
}
public createCompressionStream(method: 'gzip' | 'deflate' | 'br' | 'none') {
let compressionStream: any;
switch (method) {
case 'gzip':
compressionStream = plugins.zlib.createGzip();
case 'br':
compressionStream = plugins.zlib.createBrotliCompress({
chunkSize: 16 * 1024,
params: {
},
});
case 'deflate':
compressionStream = plugins.zlib.createDeflate();
default:
compressionStream = plugins.smartstream.createPassThrough();
}
}
}

View File

@ -1,6 +1,6 @@
import { Handler } from './classes.handler.js';
import { Server } from './classes.server.js';
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
export class Feed {
public smartexpressRef: Server;

View File

@ -1,5 +1,5 @@
import * as plugins from '../typedserver.plugins.js';
import { Request, Response } from 'express';
import * as plugins from '../plugins.js';
import { type Request, type Response } from 'express';
export interface IHandlerFunction {
(requestArg: Request, responseArg: Response): void;

View File

@ -1,4 +1,4 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
import { Handler } from './classes.handler.js';
import * as interfaces from '../interfaces/index.js';
@ -40,7 +40,7 @@ export class HandlerProxy extends Handler {
}
}
let responseToSend: string = proxiedResponse.body;
let responseToSend: Buffer = proxiedResponse.body;
if (typeof responseToSend !== 'string') {
console.log(proxyRequestUrl);
console.log(responseToSend);

View File

@ -1,9 +1,11 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
import * as interfaces from '../interfaces/index.js';
import { Handler } from './classes.handler.js';
import { Compressor, type TCompressionMethod, type ICompressionResult } from './classes.compressor.js';
export class HandlerStatic extends Handler {
public compressor = new Compressor();
constructor(
pathArg: string,
optionsArg?: {
@ -11,6 +13,8 @@ export class HandlerStatic extends Handler {
responseModifier?: interfaces.TResponseModifier;
headers?: { [key: string]: string };
serveIndexHtmlDefault?: boolean;
enableCompression?: boolean;
preferredCompressionMethod?: TCompressionMethod;
}
) {
super('GET', async (req, res) => {
@ -62,11 +66,9 @@ export class HandlerStatic extends Handler {
}
// lets actually care about serving, if security checks pass
let fileString: string;
let fileEncoding: 'binary' | 'utf8';
let fileBuffer: Buffer;
try {
fileString = plugins.smartfile.fs.toStringSync(joinedPath);
fileEncoding = plugins.smartmime.getEncoding(joinedPath);
fileBuffer = plugins.smartfile.fs.toBufferSync(joinedPath);
usedPath = joinedPath;
} catch (err) {
// try serving index.html instead
@ -75,8 +77,7 @@ export class HandlerStatic extends Handler {
console.log(`serving default path ${defaultPath} instead of ${joinedPath}`);
try {
parsedPath = plugins.path.parse(defaultPath);
fileString = plugins.smartfile.fs.toStringSync(defaultPath);
fileEncoding = plugins.smartmime.getEncoding(defaultPath);
fileBuffer = plugins.smartfile.fs.toBufferSync(defaultPath);
usedPath = defaultPath;
} catch (err) {
res.writeHead(500);
@ -99,7 +100,7 @@ export class HandlerStatic extends Handler {
const modifiedResponse = await optionsArg.responseModifier({
headers: res.getHeaders(),
path: usedPath,
responseContent: fileString,
responseContent: fileBuffer,
travelData,
});
@ -115,11 +116,26 @@ export class HandlerStatic extends Handler {
}
// responseContent
fileString = modifiedResponse.responseContent;
fileBuffer = modifiedResponse.responseContent;
}
// lets finally deal with compression
let compressionResult: ICompressionResult;
if (optionsArg && optionsArg.enableCompression) {
compressionResult = await this.compressor.maybeCompress(requestHeaders, fileBuffer, [optionsArg.preferredCompressionMethod]);
} else {
compressionResult = {
compressionMethod: 'none',
result: fileBuffer,
};
}
res.status(200);
res.write(Buffer.from(fileString, fileEncoding));
if (compressionResult?.compressionMethod) {
res.header('Content-Encoding', compressionResult.compressionMethod);
}
res.write(compressionResult.result);
res.end();
});
}

View File

@ -1,4 +1,4 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
import { Handler } from './classes.handler.js';
import * as interfaces from '../interfaces/index.js';
@ -11,7 +11,9 @@ export class HandlerTypedRouter extends Handler {
constructor(typedrouter: plugins.typedrequest.TypedRouter) {
super('POST', async (req, res) => {
const response = await typedrouter.routeAndAddResponse(req.body);
res.json(response);
res.type('json');
res.write(plugins.smartjson.stringify(response));
res.end();
});
}
}

View File

@ -1,14 +1,13 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
import { Handler } from './classes.handler.js';
import { Server } from './classes.server.js';
import { ObjectMap } from '@pushrocks/lik';
import { IRoute as IExpressRoute } from 'express';
import { type IRoute as IExpressRoute } from 'express';
export class Route {
public routeString: string;
public handlerObjectMap = new ObjectMap<Handler>();
public expressMiddlewareObjectMap = new ObjectMap<any>();
public handlerObjectMap = new plugins.lik.ObjectMap<Handler>();
public expressMiddlewareObjectMap = new plugins.lik.ObjectMap<any>();
public expressRoute: IExpressRoute; // will be set to server route on server start
constructor(ServerArg: Server, routeStringArg: string) {
this.routeString = routeStringArg;

View File

@ -1,4 +1,4 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
import { Route } from './classes.route.js';
import { Handler } from './classes.handler.js';
@ -9,7 +9,7 @@ import { setupRobots } from './tools.robots.js';
import { setupManifest } from './tools.manifest.js';
import { Sitemap } from './classes.sitemap.js';
import { Feed } from './classes.feed.js';
import { IServerOptions } from '../typedserver.classes.typedserver.js'
import { type IServerOptions } from '../typedserver.classes.typedserver.js';
export type TServerStatus = 'initiated' | 'running' | 'stopped';
/**
@ -101,16 +101,10 @@ export class Server {
console.log('Using externally supplied http server');
}
this.httpServer.keepAliveTimeout = 600 * 1000;
this.httpServer.headersTimeout = 600 * 1000;
this.httpServer.headersTimeout = 20 * 1000;
// general request handlling
this.expressAppInstance.use((req, res, next) => {
req.on('error', () => {
req.destroy();
});
req.on('timeout', () => {
req.destroy();
});
next();
});
@ -138,7 +132,8 @@ export class Server {
this.expressAppInstance.use((req, res, next) => {
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
res.setHeader('Cross-Origin-Embedder-Policy', 'unsafe-none');
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('SERVEZONE_ROUTE', 'LOSSLESS_ORIGIN_CONTAINER');
res.setHeader('Cache-Control', 'no-cache');
@ -147,7 +142,24 @@ export class Server {
});
// body parsing
this.expressAppInstance.use(plugins.bodyParser.json({ limit: 100000000 })); // for parsing application/json
this.expressAppInstance.use(async (req, res, next) => {
if (req.headers['content-type'] === 'application/json') {
let data = '';
req.on('data', chunk => {
data += chunk;
});
req.on('end', () => {
try {
req.body = plugins.smartjson.parse(data);
next();
} catch (error) {
res.status(400).send('Invalid JSON');
}
});
} else {
next();
}
});
this.expressAppInstance.use(plugins.bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
// robots
@ -224,25 +236,47 @@ export class Server {
this.httpServer.on('connection', (connection: plugins.net.Socket) => {
this.socketMap.add(connection);
console.log(`added connection. now ${this.socketMap.getArray().length} sockets connected.`);
const cleanupConnection = () => {
const closeListener = () => {
console.log('connection closed');
cleanupConnection();
};
const errorListener = () => {
console.log('connection errored');
cleanupConnection();
};
const endListener = () => {
console.log('connection ended');
cleanupConnection();
};
const timeoutListener = () => {
console.log('connection timed out');
cleanupConnection();
};
connection.addListener('close', closeListener);
connection.addListener('error', errorListener);
connection.addListener('end', endListener);
connection.addListener('timeout', timeoutListener);
const cleanupConnection = async () => {
connection.removeListener('close', closeListener);
connection.removeListener('error', errorListener);
connection.removeListener('end', endListener);
connection.removeListener('timeout', timeoutListener);
if (this.socketMap.checkForObject(connection)) {
this.socketMap.remove(connection);
console.log(`removed connection. ${this.socketMap.getArray().length} sockets remaining.`);
connection.destroy();
await plugins.smartdelay.delayFor(0);
if (connection.destroyed === false) {
connection.destroy();
}
}
};
connection.on('close', () => {
cleanupConnection();
});
connection.on('error', () => {
cleanupConnection();
});
connection.on('end', () => {
cleanupConnection();
});
connection.on('timeout', () => {
cleanupConnection();
});
});
// finally listen on a port

View File

@ -1,7 +1,7 @@
import { Server } from './classes.server.js';
import { Handler } from './classes.handler.js';
import * as plugins from '../typedserver.plugins.js';
import { IUrlInfo } from '@pushrocks/smartsitemap';
import * as plugins from '../plugins.js';
import { type IUrlInfo } from '@push.rocks/smartsitemap';
export class Sitemap {
public smartexpressRef: Server;

View File

@ -1,4 +1,4 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
export const setupManifest = async (
expressInstanceArg: plugins.express.Application,

View File

@ -1,4 +1,4 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
import { Server } from './classes.server.js';
import { Handler } from './classes.handler.js';

View File

@ -1,4 +1,4 @@
import * as plugins from '../typedserver.plugins.js';
import * as plugins from '../plugins.js';
import { Server } from './classes.server.js';
import { Handler } from './classes.handler.js';

View File

@ -1,7 +1,8 @@
import * as plugins from './typedserver.plugins.js';
import * as plugins from './plugins.js';
import * as paths from './typedserver.paths.js';
import * as interfaces from './interfaces/index.js';
import * as servertools from './servertools/index.js';
import { type TCompressionMethod } from './servertools/classes.compressor.js';
export interface IServerOptions {
/**
@ -14,6 +15,16 @@ export interface IServerOptions {
*/
injectReload?: boolean;
/**
* enable compression
*/
enableCompression?: boolean;
/**
* choose a preferred compression method
*/
preferredCompressionMethod?: TCompressionMethod;
/**
* watch the serve directory?
*/
@ -109,18 +120,22 @@ export class TypedServer {
}
})
);
this.server.addRoute(
'/typedrequest',
new servertools.HandlerTypedRouter(this.typedrouter)
)
}
/**
* inits and starts the server
*/
public async start() {
if(this.options.serveDir) {
if (this.options.serveDir) {
this.server.addRoute(
'/*',
new servertools.HandlerStatic(this.options.serveDir, {
responseModifier: async (responseArg) => {
let fileString = responseArg.responseContent;
let fileString = responseArg.responseContent.toString();
if (plugins.path.parse(responseArg.path).ext === '.html') {
const fileStringArray = fileString.split('<head>');
if (this.options.injectReload && fileStringArray.length === 2) {
@ -149,17 +164,21 @@ export class TypedServer {
return {
headers,
path: responseArg.path,
responseContent: fileString,
responseContent: Buffer.from(fileString),
};
},
serveIndexHtmlDefault: true,
enableCompression: this.options.enableCompression,
preferredCompressionMethod: this.options.preferredCompressionMethod,
})
);
} else if (this.options.injectReload) {
throw new Error('You set to inject the reload script without a serve dir. This is not supported at the moment.')
throw new Error(
'You set to inject the reload script without a serve dir. This is not supported at the moment.'
);
}
if (this.options.watch && this.options.serveDir) {
this.smartchokInstance = new plugins.smartchok.Smartchok([this.options.serveDir], {});
this.smartchokInstance = new plugins.smartchok.Smartchok([this.options.serveDir]);
await this.smartchokInstance.start();
(await this.smartchokInstance.getObservableFor('change')).subscribe(async () => {
await this.createServeDirHash();
@ -177,11 +196,13 @@ export class TypedServer {
);
// lets setup typedrouter
this.typedrouter.addTypedHandler<interfaces.IReq_GetLatestServerChangeTime>(new plugins.typedrequest.TypedHandler('getLatestServerChangeTime', async reqDataArg => {
return {
time: this.lastReload,
}
}))
this.typedrouter.addTypedHandler<interfaces.IReq_GetLatestServerChangeTime>(
new plugins.typedrequest.TypedHandler('getLatestServerChangeTime', async (reqDataArg) => {
return {
time: this.lastReload,
};
})
);
// console.log('open url in browser');
// await plugins.smartopen.openUrl(`http://testing.git.zone:${this.options.port}`);
@ -192,7 +213,9 @@ export class TypedServer {
*/
public async reload() {
this.lastReload = Date.now();
for (const connectionArg of await this.typedsocket.findAllTargetConnectionsByTag('typedserver_frontend')) {
for (const connectionArg of await this.typedsocket.findAllTargetConnectionsByTag(
'typedserver_frontend'
)) {
const pushTime =
this.typedsocket.createTypedRequest<interfaces.IReq_PushLatestServerChangeTime>(
'pushLatestServerChangeTime',

View File

@ -1,4 +1,4 @@
import * as plugins from './typedserver.plugins.js';
import * as plugins from './plugins.js';
export const packageDir = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),

View File

@ -1,67 +0,0 @@
// node native
import * as http from 'http';
import * as https from 'https';
import * as net from 'net';
import * as path from 'path';
export { http, https, net, path };
// @tsclass scope
import * as tsclass from '@tsclass/tsclass';
export {
tsclass
}
// @apiglobal scope
import * as typedrequest from '@apiglobal/typedrequest';
import * as typedrequestInterfaces from '@apiglobal/typedrequest-interfaces';
import * as typedsocket from '@apiglobal/typedsocket';
export {
typedrequest,
typedrequestInterfaces,
typedsocket,
}
// @pushrocks scope
import * as lik from '@pushrocks/lik';
import * as smartchok from '@pushrocks/smartchok';
import * as smartdelay from '@pushrocks/smartdelay';
import * as smartfeed from '@pushrocks/smartfeed';
import * as smartfile from '@pushrocks/smartfile';
import * as smartmanifest from '@pushrocks/smartmanifest';
import * as smartmime from '@pushrocks/smartmime';
import * as smartopen from '@pushrocks/smartopen';
import * as smartpath from '@pushrocks/smartpath';
import * as smartpromise from '@pushrocks/smartpromise';
import * as smartrequest from '@pushrocks/smartrequest';
import * as smartrx from '@pushrocks/smartrx';
import * as smartsitemap from '@pushrocks/smartsitemap';
import * as smarttime from '@pushrocks/smarttime';
export {
lik,
smartchok,
smartdelay,
smartfeed,
smartfile,
smartmanifest,
smartmime,
smartopen,
smartpath,
smartpromise,
smartrequest,
smartsitemap,
smarttime,
smartrx,
};
// express
import bodyParser from 'body-parser';
import cors from 'cors';
import express from 'express';
// @ts-ignore
import expressForceSsl from 'express-force-ssl';
export { bodyParser, cors, express, expressForceSsl };

View File

@ -2,7 +2,7 @@
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@apiglobal/typedserver',
version: '2.0.54',
name: '@api.global/typedserver',
version: '3.0.26',
description: 'easy serving of static files'
}

View File

@ -1,6 +1,6 @@
// @apiglobal scope
import * as typedrequest from '@apiglobal/typedrequest';
import * as typedsocket from '@apiglobal/typedsocket';
import * as typedrequest from '@api.global/typedrequest';
import * as typedsocket from '@api.global/typedsocket';
export {
typedrequest,
@ -8,10 +8,10 @@ export {
}
// pushrocks scope
import * as smartdelay from '@pushrocks/smartdelay';
import * as smartlog from '@pushrocks/smartlog';
import * as smartlogDestinationDevtools from '@pushrocks/smartlog-destination-devtools';
import * as webstore from '@pushrocks/webstore';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartlog from '@push.rocks/smartlog';
import * as smartlogDestinationDevtools from '@push.rocks/smartlog-destination-devtools';
import * as webstore from '@push.rocks/webstore';
export {
smartdelay,

View File

@ -1,9 +1,14 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false,
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "nodenext",
"esModuleInterop": true
}
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true
},
"exclude": [
"dist_*/**/*.d.ts"
]
}