Compare commits

...

14 Commits

Author SHA1 Message Date
f06f25b4db 3.0.37 2024-05-17 17:20:29 +02:00
316625c41b fix(core): update 2024-05-17 17:20:29 +02:00
ee67c68c17 3.0.36 2024-05-14 15:33:06 +02:00
8fb2d8b3e8 fix(core): update 2024-05-14 15:33:05 +02:00
75c89b040b 3.0.35 2024-05-14 15:28:10 +02:00
b6d0843e3e fix(core): update 2024-05-14 15:28:09 +02:00
1c5e2845d1 3.0.34 2024-05-14 03:24:03 +02:00
7798bf7e0a fix(core): update 2024-05-14 03:24:03 +02:00
76db7d1733 3.0.33 2024-05-14 02:18:42 +02:00
1db472ab01 fix(core): update 2024-05-14 02:18:42 +02:00
23e88030be 3.0.32 2024-05-13 23:24:09 +02:00
1644cbbfad fix(core): update 2024-05-13 23:24:08 +02:00
84e214d087 3.0.31 2024-05-11 12:52:43 +02:00
0bb7e438d5 fix(core): update 2024-05-11 12:52:42 +02:00
57 changed files with 5861 additions and 2435 deletions

View File

@ -1,18 +1,20 @@
{
"name": "@api.global/typedserver",
"version": "3.0.30",
"version": "3.0.37",
"description": "A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.",
"type": "module",
"exports": {
".": "./dist_ts/index.js",
"./ts": "./dist_ts/index.js",
"./ts_edgeworker": "./dist_ts_edgeworker",
"./ts_web_inject": "./dist_ts_web_inject/index.js",
"./ts_web_serviceworker": "./dist_web_serviceworker",
"./ts_web_serviceworker_client": "./dist_web_serviceworker_client"
"./ts_web_serviceworker": "./dist_ts_web_serviceworker",
"./ts_web_serviceworker_client": "./dist_ts_web_serviceworker_client"
},
"scripts": {
"test": "npm run build && tstest test/",
"build": "tsbuild tsfolders --web --allowimplicitany --skiplibcheck && tsbundle --from ./ts_web/index.ts --to ./dist_ts_web/bundle.js",
"build": "tsbuild tsfolders --web --allowimplicitany && npm run bundle",
"bundle": "tsbundle --from ./ts_web_inject/index.ts --to ./dist_ts_web_inject/bundle.js && tsbundle --from ./ts_web_serviceworker/index.ts --to ./dist_ts_web_serviceworker/serviceworker.bundle.js",
"interfaces": "tsbuild interfaces --web --allowimplicitany --skiplibcheck",
"docs": "tsdoc aidoc"
},
@ -58,6 +60,7 @@
"@api.global/typedrequest": "^3.0.23",
"@api.global/typedrequest-interfaces": "^3.0.19",
"@api.global/typedsocket": "^3.0.1",
"@cloudflare/workers-types": "^4.20240512.0",
"@design.estate/dees-comms": "^1.0.24",
"@push.rocks/lik": "^6.0.15",
"@push.rocks/smartchok": "^1.0.34",
@ -68,8 +71,11 @@
"@push.rocks/smartjson": "^5.0.19",
"@push.rocks/smartlog": "^3.0.3",
"@push.rocks/smartlog-destination-devtools": "^1.0.10",
"@push.rocks/smartlog-interfaces": "^3.0.0",
"@push.rocks/smartmanifest": "^2.0.2",
"@push.rocks/smartmatch": "^2.0.0",
"@push.rocks/smartmime": "^1.0.5",
"@push.rocks/smartntml": "^2.0.4",
"@push.rocks/smartopen": "^2.0.0",
"@push.rocks/smartpath": "^5.0.18",
"@push.rocks/smartpromise": "^4.0.2",
@ -90,7 +96,7 @@
"lit": "^3.1.3"
},
"devDependencies": {
"@git.zone/tsbuild": "^2.1.75",
"@git.zone/tsbuild": "^2.1.76",
"@git.zone/tsbundle": "^2.0.15",
"@git.zone/tsrun": "^1.2.44",
"@git.zone/tstest": "^1.0.90",

6921
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@api.global/typedserver',
version: '3.0.30',
version: '3.0.37',
description: 'A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.'
}

View File

@ -1,5 +1,5 @@
import * as plugins from './plugins.js';
import * as paths from './typedserver.paths.js';
import * as paths from './paths.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import * as servertools from './servertools/index.js';
import { type TCompressionMethod } from './servertools/classes.compressor.js';
@ -103,7 +103,7 @@ export class TypedServer {
case 'devtools':
res.setHeader('Content-Type', 'text/javascript');
res.status(200);
res.write(plugins.smartfile.fs.toStringSync(paths.bundlePath));
res.write(plugins.smartfile.fs.toStringSync(paths.injectBundlePath));
res.end();
break;
case 'reloadcheck':

View File

@ -4,7 +4,12 @@ import * as servertools from './servertools/index.js';
export { servertools };
export * from './typedserver.classes.typedserver.js';
export * from './classes.typedserver.js';
// Type helpers
export type Request = plugins.express.Request;
export type Response = plugins.express.Response;
// lets export utilityservers
import * as utilityservers from './utilityservers/index.js';
export { utilityservers };

View File

@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: '@losslessone_private/lole-infohtml',
version: '1.0.39',
description: 'html for displaying infos at lossless'
}

44
ts/infohtml/index.ts Normal file
View File

@ -0,0 +1,44 @@
import * as plugins from './infohtml.plugins.js';
import { simpleInfo } from './template.js';
export interface IHtmlInfoOptions {
text: string;
heading?: string;
title?: string;
sentryMessage?: string;
sentryDsn?: string;
redirectTo?: string;
}
export class InfoHtml {
// STATIC
public static async fromSimpleText(textArg: string) {
const infohtmlInstance = new InfoHtml({
text: textArg,
heading: null,
});
await infohtmlInstance.init();
return infohtmlInstance;
}
public static async fromOptions(optionsArg: IHtmlInfoOptions) {
const infohtmlInstance = new InfoHtml(optionsArg);
await infohtmlInstance.init();
return infohtmlInstance;
}
// INSTANCE
public options: IHtmlInfoOptions;
public smartntmlInstance: plugins.smartntml.Smartntml;
public htmlString: string;
constructor(optionsArg: IHtmlInfoOptions) {
this.options = optionsArg;
}
public async init() {
this.smartntmlInstance = new plugins.smartntml.Smartntml();
this.htmlString = await simpleInfo(this.smartntmlInstance, this.options);
return this.htmlString;
}
}

View File

@ -0,0 +1,3 @@
import * as smartntml from '@push.rocks/smartntml';
export { smartntml };

160
ts/infohtml/template.ts Normal file
View File

@ -0,0 +1,160 @@
import * as plugins from './infohtml.plugins.js';
import { type IHtmlInfoOptions } from './index.js';
export const simpleInfo = async (
smartntmlInstanceArg: plugins.smartntml.Smartntml,
optionsArg: IHtmlInfoOptions
) => {
const html = plugins.smartntml.deesElement.html;
const htmlTemplate = await plugins.smartntml.deesElement.html`
<html lang="en">
<head>
<title>${optionsArg.title}</title>
<script>
setTimeout(() => {
const redirectUrl = '${optionsArg.redirectTo}';
if (redirectUrl) {
window.location = redirectUrl;
}
}, 5000);
</script>
<style>
body {
margin: 0px;
background: #000000;
font-family: 'Roboto Mono', monospace;
min-height: 100vh;
min-width: 100vw;
border: 1px solid #e4002b;
}
* {
box-sizing: border-box;
}
.logo {
width: 150px;
padding-top: 70px;
margin: 0px auto 30px auto;
}
.content {
text-align: center;
max-width: 800px;
margin: auto;
}
.content .maintext {
margin: 10px;
color: #ffffff;
background: #333;
display: block;
border-radius: 3px;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
padding: 20px;
}
.content .maintext h1 {
margin: 0px;
}
.content .addontext {
margin: 10px;
color: #ffffff;
background: #222;
display: block;
padding: 10px 15px;
border-radius: 3px;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
padding: 10px;
}
.content .text h1 {
margin: 0px;
font-weight: 100;
font-size: 40px;
}
.content .text ul {
text-align: left;
}
a {
color: #ffffff;
}
.legal {
color: #fff;
position: fixed;
bottom: 0px;
width: 100vw;
text-align: center;
padding: 10px;
}
</style>
<meta
name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"
/>
<script
src="https://browser.sentry-cdn.com/5.4.0/bundle.min.js"
crossorigin="anonymous"
></script>
<script>
if (optionsArg.sentryDsn && optionsArg.sentryMessage) {
Sentry.init({
dsn: '${optionsArg.sentryDsn}',
// ...
});
Sentry.setExtra('location', window.location.href);
Sentry.captureMessage('${optionsArg.sentryMessage} @ ' + window.location.host);
}
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div class="logo">
<img src="https://assetbroker.lossless.one/brandfiles/lossless/svg-minimal-bright.svg" />
</div>
<div class="content">
${(() => {
const returnArray: plugins.smartntml.deesElement.TemplateResult[] = [];
if (optionsArg.heading) {
returnArray.push(html`
<div class="maintext">
<h1>${optionsArg.heading}</h1>
${optionsArg.text}
</div>
`);
} else {
returnArray.push(html` <div class="maintext">${optionsArg.text}</div> `);
}
if (optionsArg.sentryDsn && optionsArg.sentryMessage) {
returnArray.push(
html`<div class="addontext">
We recorded this event. Should you continue to see this page against your
expectations, feel free to mail us at
<a href="mailto:hello@lossless.com">hello@lossless.com</a>
</div>`
);
}
if (optionsArg.redirectTo) {
returnArray.push(
html`<div class="addontext">
We will redirect you to ${optionsArg.redirectTo} in a few seconds.
</div>`
);
}
return returnArray;
})()}
</div>
<div class="legal">
<a href="https://lossless.com">Lossless GmbH</a> / &copy 2014-${new Date().getFullYear()}
/ <a href="https://lossless.gmbh">Legal Info</a> /
<a href="https://lossless.gmbh">Privacy Policy</a>
</div>
</body>
</html>
`;
return smartntmlInstanceArg.renderTemplateResult(htmlTemplate);
};

View File

@ -4,4 +4,8 @@ export const packageDir = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
'../'
);
export const distBundleDir = plugins.path.join(packageDir, './dist_bundle');
export const injectBundleDir = plugins.path.join(packageDir, './dist_ts_web_inject');
export const injectBundlePath = plugins.path.join(injectBundleDir, './bundle.js');
export const serviceworkerBundleDir = plugins.path.join(packageDir, './dist_ts_web_serviceworker');

View File

@ -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 { type IServerOptions } from '../typedserver.classes.typedserver.js';
import { type IServerOptions } from '../classes.typedserver.js';
export type TServerStatus = 'initiated' | 'running' | 'stopped';
/**

View File

@ -4,3 +4,9 @@ export * from './classes.handler.js';
export * from './classes.handlerstatic.js';
export * from './classes.handlerproxy.js';
export * from './classes.handlertypedrouter.js';
export * from './classes.compressor.js';
import * as serviceworker from './tools.serviceworker.js';
export {
serviceworker,
}

View File

@ -3,28 +3,28 @@ import * as paths from '../paths.js';
import * as interfaces from '../../dist_ts_interfaces/index.js'
import { Handler } from './classes.handler.js';
import type { TypedServer } from '../typedserver.classes.typedserver.js';
import type { TypedServer } from '../classes.typedserver.js';
import { HandlerTypedRouter } from './classes.handlertypedrouter.js';
const lswJS: string = plugins.smartfile.fs.toStringSync(
plugins.path.join(paths.distBundleDir, './lsw.js')
const swBundleJs: string = plugins.smartfile.fs.toStringSync(
plugins.path.join(paths.serviceworkerBundleDir, './serviceworker.bundle.js')
);
const lswJSMeta: string = plugins.smartfile.fs.toStringSync(
plugins.path.join(paths.distBundleDir, './lsw.js.map')
const swBundleJsMap: string = plugins.smartfile.fs.toStringSync(
plugins.path.join(paths.serviceworkerBundleDir, './serviceworker.bundle.js.map')
);
let lswVersionInfo: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] =
let swVersionInfo: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] =
null;
const serviceworkerHandler = new Handler(
'GET',
async (req, res) => {
if (req.path === '/lsw.js') {
if (req.path === '/serviceworker.bundle.js') {
res.status(200);
res.set('Content-Type', 'text/javascript');
res.write(lswJS + '\n' + `/** appSemVer: ${lswVersionInfo?.appSemVer || 'not set'} */`);
} else if (req.path === '/lsw.js.map') {
res.write(swBundleJs + '\n' + `/** appSemVer: ${swVersionInfo?.appSemVer || 'not set'} */`);
} else if (req.path === '/serviceworker.bundle.js.map') {
res.status(200);
res.set('Content-Type', 'application/json');
res.write(lswJSMeta);
res.write(swBundleJsMap);
}
res.end();
}
@ -32,13 +32,13 @@ const serviceworkerHandler = new Handler(
export const addServiceWorkerRoute = (
typedserverInstance: TypedServer,
lswData: () => interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response']
swDataFunc: () => interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response']
) => {
// lets the version info as unique string;
lswVersionInfo = lswData();
swVersionInfo = swDataFunc();
// the basic stuff
typedserverInstance.server.addRoute('/lsw.js*', serviceworkerHandler);
typedserverInstance.server.addRoute('/serviceworker.js*', serviceworkerHandler);
// the typed stuff
const typedrouter = new plugins.typedrequest.TypedRouter();
@ -47,14 +47,14 @@ export const addServiceWorkerRoute = (
new plugins.typedrequest.TypedHandler<interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo>(
'serviceworker_versionInfo',
async (req) => {
const versionInfoResponse = lswData();
const versionInfoResponse = swDataFunc();
return versionInfoResponse;
}
)
);
typedserverInstance.server.addRoute(
'/lsw-typedrequest',
'/sw-typedrequest',
new HandlerTypedRouter(typedrouter)
);
};

View File

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

View File

@ -0,0 +1,51 @@
import { TypedServer } from '../classes.typedserver.js';
import * as servertools from '../servertools/index.js';
import * as plugins from '../plugins.js';
export interface ILoleServiceServerConstructorOptions {
addCustomRoutes?: (serverArg: servertools.Server) => Promise<any>;
serviceName: string;
serviceVersion: string;
serviceDomain: string;
port?: number;
}
// the main service server
export class ServiceServer {
public options: ILoleServiceServerConstructorOptions;
public typedServer: TypedServer;
constructor(optionsArg: ILoleServiceServerConstructorOptions) {
this.options = optionsArg;
}
public async start() {
console.log('starting lole-serviceserver...')
this.typedServer = new TypedServer({
cors: true,
domain: this.options.serviceDomain,
forceSsl: false,
port: this.options.port || 3000,
robots: true,
defaultAnswer: async () => {
const InfoHtml = (await import('../infohtml/index.js')).InfoHtml;
return (
await InfoHtml.fromSimpleText(
`${this.options.serviceName} (version ${this.options.serviceVersion})`
)
).htmlString;
},
});
// lets add any custom routes
if (this.options.addCustomRoutes) {
await this.options.addCustomRoutes(this.typedServer.server);
}
await this.typedServer.start();
}
public async stop() {
await this.typedServer.stop();
}
}

View File

@ -0,0 +1,137 @@
import * as interfaces from '../../dist_ts_interfaces/index.js';
import { type IServerOptions, TypedServer } from '../classes.typedserver.js';
import type { Request, Response } from '../index.js';
import * as plugins from '../plugins.js';
import * as servertools from '../servertools/index.js';
export interface IUtilityWebsiteServerConstructorOptions {
addCustomRoutes?: (serverArg: servertools.Server) => Promise<any>;
appSemVer?: string;
domain: string;
serveDir: string;
feedMetadata: IServerOptions['feedMetadata'];
}
/**
* the utility website server implements a best practice server for websites
* It supports:
* * live reload
* * compression
* * serviceworker
* * pwa manifest
*/
export class UtilityWebsiteServer {
public options: IUtilityWebsiteServerConstructorOptions;
public typedserver: TypedServer;
public typedrouter = new plugins.typedrequest.TypedRouter();
constructor(optionsArg: IUtilityWebsiteServerConstructorOptions) {
this.options = optionsArg;
}
/**
*
*/
public async start(portArg = 3000) {
this.typedserver = new TypedServer({
cors: true,
injectReload: true,
watch: true,
serveDir: this.options.serveDir,
enableCompression: true,
preferredCompressionMethod: 'gzip',
domain: this.options.domain,
forceSsl: false,
manifest: {
name: this.options.domain,
short_name: this.options.domain,
start_url: '/',
display_override: ['window-controls-overlay'],
lang: 'en',
background_color: '#000000',
scope: '/',
},
port: portArg,
// features
robots: true,
sitemap: true,
});
let lswData: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'] =
{
appHash: 'xxxxxx',
appSemVer: this.options.appSemVer || 'x.x.x',
};
// -> /lsw* - anything regarding serviceworker
servertools.serviceworker.addServiceWorkerRoute(this.typedserver, () => {
return lswData;
});
// lets add ads.txt
this.typedserver.server.addRoute(
'/ads.txt',
new servertools.Handler('GET', async (req, res) => {
res.type('txt/plain');
const adsTxt =
['google.com, pub-4104137977476459, DIRECT, f08c47fec0942fa0'].join('\n') + '\n';
res.write(adsTxt);
res.end();
})
);
this.typedserver.server.addRoute(
'/assetbroker/manifest/:manifestAsset',
new servertools.Handler('GET', async (req, res) => {
let manifestAssetName = req.params.manifestAsset;
if (manifestAssetName === 'favicon.png') {
manifestAssetName = `favicon_${this.options.domain
.replace('.', '')
.replace('losslesscom', 'lossless')}@2x_transparent.png`;
}
const fullOriginAssetUrl = `https://assetbroker.lossless.one/brandfiles/00general/${manifestAssetName}`;
console.log(`Getting ${manifestAssetName} from ${fullOriginAssetUrl}`);
const dataBuffer: Buffer = (await plugins.smartrequest.getBinary(fullOriginAssetUrl)).body;
res.type('.png');
res.write(dataBuffer);
res.end();
})
);
// lets add any custom routes
if (this.options.addCustomRoutes) {
await this.options.addCustomRoutes(this.typedserver.server);
}
// -> /* - serve the files
this.typedserver.serveDirHashSubject.subscribe((appHash: string) => {
lswData = {
appHash,
appSemVer: '1.0.0',
};
});
// lets setup the typedrouter chain
this.typedserver.typedrouter.addTypedRouter(this.typedrouter);
// lets start everything
console.log('routes are all set. Startin up now!');
await this.typedserver.start();
console.log('typedserver started!');
}
public async stop() {
await this.typedserver.stop();
}
/**
* allows you to hanlde requests from other server instances without the need to listen for yourself
* note smartexpress allows you start the instance wuith passing >>false<< as second parameter to .start();
* @param req
* @param res
*/
public async handleRequest(req: Request, res: Response) {
await this.typedserver.server.handleReqRes(req, res);
}
}

View File

@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
*/
export const commitinfo = {
name: 'cloudflare-workers',
version: '1.0.192',
description: 'cloudflare-workers'
}

View File

@ -0,0 +1,83 @@
import type { EdgeWorker } from '../classes.edgeworker.js';
import type { WorkerEvent } from '../classes.workerevent.js';
import * as plugins from '../plugins.js';
import { SmartlogDestination } from './smartlog.js';
export interface IAnalyticsData {
requestAgent: string;
requestUrl: string;
requestMethod: string;
requestStartTime: number;
responseStatus: number;
responseEndTime: number;
}
export class Analyzer {
cworkerEventRef: WorkerEvent;
public data: IAnalyticsData = {
requestAgent: 'unknown',
requestMethod: 'unknown',
requestUrl: 'unknown',
requestStartTime: 0,
responseStatus: 0,
responseEndTime: 0,
};
public finishedDeferred = plugins.smartpromise.defer();
constructor(cworkerEventRefArg: WorkerEvent) {
this.cworkerEventRef = cworkerEventRefArg;
this.smartlog.addLogDestination(new SmartlogDestination(this.cworkerEventRef.options.edgeWorkerRef));
}
public smartlog = new plugins.smartlog.Smartlog({
logContext: {
environment: 'production',
runtime: "cloudflare_workers",
zone: 'servezone',
company: 'Lossless GmbH',
companyunit: 'Lossless Cloud',
containerName: 'cloudflare_workers'
}
});
public setRequestData (optionsArg: {
requestAgent: string;
requestUrl: string;
requestMethod: string;
}) {
this.data = {
...this.data,
...{
requestAgent: optionsArg.requestAgent,
requestUrl: optionsArg.requestUrl,
requestMethod: optionsArg.requestMethod,
requestStartTime: Date.now()
}
};
}
public setResponseData(optionsArg: {
responseStatus: number,
responseEndTime: number,
}) {
this.data = {
...this.data,
...{
responseStatus: optionsArg.responseStatus,
responseEndTime: optionsArg.responseEndTime
}
};
this.sendLogs();
}
public async sendLogs() {
await this.smartlog.log('info', `
Got a ${this.data.requestMethod} request from ${this.data.requestAgent} to
${this.data.requestUrl}
that took ${this.data.responseEndTime - this.data.requestStartTime}ms to resolve with status ${this.data.responseStatus}.`, this.data);
this.finishedDeferred.resolve();
}
}

View File

@ -0,0 +1,26 @@
import * as smartlogInterfaces from '@push.rocks/smartlog-interfaces';
import type { EdgeWorker } from '../classes.edgeworker.js';
export class SmartlogDestination implements smartlogInterfaces.ILogDestination {
public edgeWorkerRef: EdgeWorker;
constructor(edgeworkerRefArg: EdgeWorker) {
this.edgeWorkerRef = edgeworkerRefArg;
}
public async handleLog(logPackageArg: smartlogInterfaces.ILogPackage) {
if (this.edgeWorkerRef.options.smartlogConfig) {
const requestBody: smartlogInterfaces.ILogPackageAuthenticated = {
auth: this.edgeWorkerRef.options.smartlogConfig.token,
logPackage: logPackageArg,
};
await fetch(this.edgeWorkerRef.options.smartlogConfig.endpoint, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(requestBody),
});
}
}
}

View File

@ -0,0 +1,67 @@
import * as interfaces from './interfaces/index.js';
import * as plugins from './plugins.js';
import { WorkerEvent } from './classes.workerevent.js';
import * as domainInstructions from './domaininstructions/index.js';
export class DomainRouter {
private smartmatches: plugins.smartmatch.SmartMatch[] = [];
constructor() {
for (const key of Object.keys(domainInstructions.instructionObject)) {
this.smartmatches.push(new plugins.smartmatch.SmartMatch(key));
}
}
/**
*
* @param cworkerevent
*/
public routeToResponder(cworkerevent: WorkerEvent) {
const match = this.smartmatches.find(smartmatchArg => {
return smartmatchArg.match(cworkerevent.request.url);
});
cworkerevent.responderInstruction = match
? domainInstructions.instructionObject[match.wildcard]
: {
type: 'cache'
};
}
/**
* rendertronRouter
*/
public checkWetherReRouteToRendertron(cworkerevent: WorkerEvent) {
let needsRendertron = false;
for (const botAgentIdentifier of domainInstructions.botUserAgents) {
if (needsRendertron) {
continue;
}
if (
cworkerevent.request.headers.get('user-agent') &&
cworkerevent.request.headers.get('user-agent').toLowerCase().includes(botAgentIdentifier.toLowerCase()) &&
!cworkerevent.request.url.includes('lossless.one')
) {
needsRendertron = true;
}
}
if (needsRendertron) {
cworkerevent.routedThroughRendertron = true;
}
}
/**
* check wether this is a preflight request that should be handled
*/
public checkWetherIsPreflight (cworkerevent: WorkerEvent) {
if (
cworkerevent.request.method === 'OPTIONS' &&
cworkerevent.request.headers.get('Origin') !== null &&
cworkerevent.request.headers.get('Access-Control-Request-Method') !== null &&
cworkerevent.request.headers.get('Access-Control-Request-Headers') !== null
) {
cworkerevent.isPreflight = true;
}
}
}

View File

@ -0,0 +1,73 @@
// imports
import { WorkerEvent } from './classes.workerevent.js';
import { DomainRouter } from './classes.domainrouter.js';
import * as plugins from './plugins.js';
import * as responders from './responders/index.js';
export interface IEdgeWorkerOptions {
smartlogConfig?: {
endpoint: string;
token: string;
}
}
export class EdgeWorker {
public options: IEdgeWorkerOptions;
domainRouter: DomainRouter;
constructor(optionsArg: IEdgeWorkerOptions = {}) {
this.options = optionsArg;
this.domainRouter = new DomainRouter();
addEventListener('fetch', this.fetchFunction as any);
}
public async fetchFunction (eventArg: plugins.cloudflareTypes.FetchEvent) {
if (new URL(eventArg.request.url).pathname.startsWith('/socket.io')) {
return;
}
const cworkerEvent = new WorkerEvent({
edgeWorkerRef: this,
event: eventArg,
passThroughOnException: true
});
// lets answer basic reuest things
responders.timeoutResponder(cworkerEvent);
cworkerEvent.hasResponse ? null : await responders.urlFormattingResponder(cworkerEvent);
// lets route the domain
this.domainRouter.routeToResponder(cworkerEvent);
this.domainRouter.checkWetherReRouteToRendertron(cworkerEvent);
this.domainRouter.checkWetherIsPreflight(cworkerEvent);
// guardresponder
cworkerEvent.hasResponse ? null : await responders.guardResponder(cworkerEvent);
// lets process all requests that need rendertron
cworkerEvent.hasResponse ? null : await responders.rendertronResponder(cworkerEvent);
// lets process all requests that are preflight requests
cworkerEvent.hasResponse ? null : await responders.preflightResponder(cworkerEvent);
switch (cworkerEvent.responderInstruction.type) {
case 'cache':
cworkerEvent.hasResponse ? null : await responders.cacheResponder(cworkerEvent);
break;
case 'origin':
cworkerEvent.hasResponse ? null : await responders.originResponder(cworkerEvent);
break;
case 'redirect':
cworkerEvent.hasResponse ? null : await responders.adsTxtResponder(cworkerEvent);
break;
case 'static':
cworkerEvent.hasResponse ? null : await responders.staticResponder(cworkerEvent);
break;
case 'ads.txt':
cworkerEvent.hasResponse ? null : await responders.adsTxtResponder(cworkerEvent);
break;
}
// cworkerEvent.hasResponse ? null : await responders.kvResponder(cworkerEvent);
cworkerEvent.hasResponse ? null : await responders.errorResponder(cworkerEvent);
};
}

View File

@ -0,0 +1,37 @@
import * as interfaces from './interfaces/index.js';
import * as plugins from './plugins.js';
declare var lokv: plugins.cloudflareTypes.KVNamespace;
/**
* an abstraction for the workerd KV store
*/
export class KVHandler {
private getSafeIdentifier(urlString: string) {
return encodeURI(urlString);
}
async getFromKv(keyIdentifier: string) {
const key = this.getSafeIdentifier(keyIdentifier);
const valueString = await lokv.get(key);
return valueString;
}
async putInKv(keyIdentifier: string, valueForStorage: string) {
const key = this.getSafeIdentifier(keyIdentifier);
const value = valueForStorage;
await lokv.put(key, value);
return null;
}
/**
* deletes a key/value from the cache
* @param keyIdentifier
*/
async deleteInKv(keyIdentifier: string) {
const cacheKey = this.getSafeIdentifier(keyIdentifier);
await lokv.delete(cacheKey);
}
}
export const kvHandlerInstance = new KVHandler();

View File

@ -0,0 +1,46 @@
import * as plugins from './plugins.js';
import { kvHandlerInstance } from './classes.kvhandler.js';
declare var lokv: plugins.cloudflareTypes.KVNamespace;
interface IKVResponseObject {
headers: { [key: string]: string };
version: string;
body: string;
}
export class ResponseKv {
public async storeResponse(urlIdentifier: string, responseArg: any) {
const headers: { [key: string]: string } = {};
for (const kv of responseArg.headers.entries()) {
headers[kv[0]] = kv[1];
}
const kvResponseForStorage: IKVResponseObject = {
headers,
version: '1.0.0',
body: await responseArg.text()
};
await kvHandlerInstance.putInKv(urlIdentifier, JSON.stringify(kvResponseForStorage));
}
public async getResponse(urlIdentifier: string): Promise<Response> {
const kvValue = await kvHandlerInstance.getFromKv(urlIdentifier);
if (kvValue) {
let kvResponse: IKVResponseObject;
try {
kvResponse = JSON.parse(kvValue);
} catch (e) {
console.log(e);
return null;
}
const headers = new Headers();
for (const key of Object.keys(kvResponse.headers)) {
headers.append(key, kvResponse.headers[key]);
}
headers.append('SERVEZONE_ROUTE', 'CLOUDFLARE_EDGE_LOKV');
return new Response(kvResponse.body, { headers: headers });
} else {
return null;
}
}
}

View File

@ -0,0 +1,102 @@
import * as interfaces from './interfaces/index.js';
import * as plugins from './plugins.js';
import * as helpers from './helpers/index.js';
import { DomainRouter } from './classes.domainrouter.js';
import { Analyzer } from './analytics/analyzer.js';
import type { EdgeWorker } from './classes.edgeworker.js';
export interface ICworkerEventOptions {
event: plugins.cloudflareTypes.FetchEvent
edgeWorkerRef: EdgeWorker;
passThroughOnException?: boolean;
}
export class WorkerEvent {
public options: ICworkerEventOptions;
public analyzer: Analyzer;
private responseDeferred: plugins.smartpromise.Deferred<any>;
private waitUntilDeferred: plugins.smartpromise.Deferred<any>;
private response: Response = null;
private waitList = [];
// routing settings
public responderInstruction: interfaces.IResponderInstruction;
public routedThroughRendertron: boolean = false;
public isPreflight: boolean = false;
public request: plugins.cloudflareTypes.Request;
public parsedUrl: URL;
constructor(optionsArg: ICworkerEventOptions) {
this.options = optionsArg;
// lets create an Analyzer for this request
this.analyzer = new Analyzer(this);
// lets make sure we always answer
this.options.passThroughOnException ? this.options.event.passThroughOnException() : null;
// lets set up some better asnyc behaviour
this.waitUntilDeferred = plugins.smartpromise.defer();
this.responseDeferred = plugins.smartpromise.defer();
this.addToWaitList(this.analyzer.finishedDeferred.promise);
// lets entangle the event with this class instance
this.request = this.options.event.request;
// lets start with analytics
this.analyzer.setRequestData({
requestAgent: this.request.headers.get('user-agent'),
requestMethod: this.request.method,
requestUrl: this.request.url
});
this.options.event.respondWith(this.responseDeferred.promise);
this.options.event.waitUntil(this.waitUntilDeferred.promise);
// lets parse the url
this.parsedUrl = new URL(this.request.url);
// lets check the waitlist
this.checkWaitList();
console.log(`Got request for ${this.request.url}`);
}
get hasResponse () {
let returnValue: boolean;
this.response ? returnValue = true : returnValue = false;
return returnValue;
}
public addToWaitList(promiseArg: Promise<any>) {
this.waitList.push(promiseArg);
}
private async checkWaitList() {
await this.responseDeferred.promise;
const currentWaitList = this.waitList;
this.waitList = [];
await Promise.all(currentWaitList);
if (this.waitList.length > 0) {
this.checkWaitList();
} else {
this.waitUntilDeferred.resolve();
}
}
public setResponse (responseArg: Response) {
this.response = responseArg;
this.responseDeferred.resolve(responseArg);
this.analyzer.setResponseData({
responseStatus: this.response.status,
responseEndTime: Date.now()
});
}
}

View File

@ -0,0 +1,36 @@
export const botUserAgents = [
// Baidu
'baiduspider',
'embedly',
// Facebook
'facebookexternalhit',
// Ghost
'Ghost',
// Microsoft
'bingbot',
'BingPreview',
'linkedinbot',
'MissinglettrBot',
'msnbot',
'outbrain',
'pinterest',
'quora link preview',
'rogerbot',
'showyoubot',
'slackbot',
'TelegramBot',
// Twitter
'twitterbot',
'vkShare',
'W3C_Validator',
// WhatsApp
'whatsapp',
// woorank
'woorank'
];

View File

@ -0,0 +1,7 @@
import * as interfaces from '../interfaces/index.js';
export const instructionObject: { [key: string]: interfaces.IResponderInstruction } = {
'*/ads.txt': {
type: 'ads.txt',
}
};

View File

@ -0,0 +1,2 @@
export * from './botuseragents.js';
export * from './domaininstructions.js';

View File

@ -0,0 +1,9 @@
import * as plugins from '../plugins.js';
declare var lokv: plugins.cloudflareTypes.KVNamespace;
export const checkLokv = () => {
if (!lokv) {
throw new Error('lokv not defined!');
} else {
console.log('lokv present!');
}
};

View File

@ -0,0 +1 @@
export * from './checks.js';

1
ts_edgeworker/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './classes.edgeworker.js';

View File

@ -0,0 +1,9 @@
import { WorkerEvent } from "../classes.workerevent.js";
export interface IResponderInstruction {
type: 'origin' | 'cache' | 'static' | 'redirect' | 'ads.txt';
cacheClientSideForMin?: number;
redirectUrl?: string;
}
export type TRequestResponser = (workerEventArg: WorkerEvent) => Promise<void>;

View File

@ -0,0 +1 @@
export * from './custom.js';

21
ts_edgeworker/plugins.ts Normal file
View File

@ -0,0 +1,21 @@
// @pushrocks scope
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartlog from '@push.rocks/smartlog';
import * as smartlogInterfaces from '@push.rocks/smartlog-interfaces';
import * as smartmatch from '@push.rocks/smartmatch';
import * as smartpromise from '@push.rocks/smartpromise';
export {
smartdelay,
smartlog,
smartlogInterfaces,
smartmatch,
smartpromise
};
// cloudflarea
import * as cloudflareTypes from '@cloudflare/workers-types';
export {
cloudflareTypes
}

View File

@ -0,0 +1,14 @@
import * as interfaces from '../interfaces/index.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const adsTxtResponder: interfaces.TRequestResponser = async (cWorkerEventArg: WorkerEvent) => {
if (cWorkerEventArg.responderInstruction.type === 'ads.txt') {
const response = new Response('google.com, pub-4104137977476459, DIRECT, f08c47fec0942fa0\n', {
headers: {
'Content-Type': 'text/plain; charset=utf-8'
}
})
cWorkerEventArg.setResponse(response);
}
};

View File

@ -0,0 +1,92 @@
import * as interfaces from '../interfaces/index.js';
import * as plugins from '../plugins.js';
import { WorkerEvent } from '../classes.workerevent.js';
import { kvHandlerInstance } from '../classes.kvhandler.js';
declare const fetch: plugins.cloudflareTypes.Fetcher['fetch'];
declare var caches: any;
export const cacheResponder: interfaces.TRequestResponser = async (cworkerEventArg: WorkerEvent) => {
const host = cworkerEventArg.request.headers.get('Host');
const appHashKey = `${host.toLowerCase()}_appHash`;
const appHash = await kvHandlerInstance.getFromKv(appHashKey);
const cache = caches.default;
let response: Response = await cache.match(cworkerEventArg.request);
if (
response &&
response.headers.get('appHash') &&
response.headers.get('appHash') !== appHash
) {
response = null;
}
if (response) {
cworkerEventArg.setResponse(response);
} else {
response = await handleNewRequest(cworkerEventArg.request);
if (response) {
cworkerEventArg.addToWaitList(new Promise<void>(async (resolve, reject) => {
const newAppHash = response.headers.get('appHash');
if (newAppHash) {
await kvHandlerInstance.putInKv(appHashKey, newAppHash);
}
resolve();
}));
cworkerEventArg.addToWaitList(buildCacheResponse(cache, cworkerEventArg.request, response));
cworkerEventArg.setResponse(response);
}
}
};
/**
* @param {Request} originalRequest
*/
const handleNewRequest = async (originalRequest: plugins.cloudflareTypes.Request): Promise<Response> => {
console.log('answering from origin');
const originResponse: any = await fetch(
originalRequest
);
// lets capture status
if (originResponse.status > 399) {
return null;
}
const responseClientPassThroughStream = new TransformStream();
originResponse.body.pipeTo(responseClientPassThroughStream.writable);
// build response for client
const clientHeaders = new Headers();
for (const kv of originResponse.headers.entries()) {
clientHeaders.append(kv[0], kv[1]);
}
clientHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_ORIGIN_INITIAL');
const responseForClient = new Response(responseClientPassThroughStream.readable, {
...originResponse,
headers: clientHeaders
});
// lets return the responses
return responseForClient;
};
const buildCacheResponse = async (cache, matchRequest: plugins.cloudflareTypes.Request, originResponse: any) => {
const cacheHeaders = new Headers();
for (const kv of originResponse.headers.entries()) {
cacheHeaders.append(kv[0], kv[1]);
}
cacheHeaders.delete('SERVEZONE_ROUTE');
cacheHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_CACHE');
cacheHeaders.delete('Cache-Control');
cacheHeaders.append('Cache-Control', 'public, max-age=60');
cacheHeaders.delete('Expires');
cacheHeaders.append('Expires', new Date(Date.now() + 60 * 1000).toUTCString());
const responseForCache = new Response(await originResponse.clone().body, {
...originResponse,
headers: cacheHeaders
});
await cache.put(matchRequest, responseForCache);
};

View File

@ -0,0 +1,6 @@
import * as interfaces from '../interfaces/index.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const errorResponder: interfaces.TRequestResponser = async (cWorkerEvent: WorkerEvent) => {
const errorResponse = await fetch('https://nullresolve.lossless.one/status/firewall');
cWorkerEvent.setResponse(errorResponse);
};

View File

@ -0,0 +1,8 @@
import * as interfaces from '../interfaces/index.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const guardResponder: interfaces.TRequestResponser = async (cWorkerEvent: WorkerEvent) => {
if (cWorkerEvent.parsedUrl.pathname.endsWith('.map')) {
const errorResponse = await fetch('https://nullresolve.lossless.one/status/firewall');
cWorkerEvent.setResponse(errorResponse);
}
};

View File

@ -0,0 +1,12 @@
export * from './adstxt.responder.js';
export * from './cache.responder.js';
export * from './urlformatting.responder.js';
export * from './error.responder.js';
export * from './guard.responder.js';
export * from './kv.responder.js';
export * from './origin.responder.js';
export * from './preflight.responder.js';
export * from './redirect.reponder.js';
export * from './rendertron.responder.js';
export * from './static.responder.js';
export * from './timeout.responder.js';

View File

@ -0,0 +1,34 @@
import * as interfaces from '../interfaces/index.js';
import * as plugins from '../plugins.js';
import { WorkerEvent } from '../classes.workerevent.js';
import { ResponseKv } from '../classes.responsekv.js';
declare const fetch: plugins.cloudflareTypes.Fetcher['fetch'];
export const kvResponder: interfaces.TRequestResponser = async (cworkerEventArg: WorkerEvent) => {
const responseKvInstance = new ResponseKv();
let response = await responseKvInstance.getResponse(cworkerEventArg.request.url);
if (response) {
console.log('Got response from KV');
} else {
response = await handleNewRequest(cworkerEventArg.request, responseKvInstance);
}
cworkerEventArg.setResponse(response);
};
const handleNewRequest = async (request: plugins.cloudflareTypes.Request, responseKvInstance: ResponseKv) => {
const originResponse: any = await fetch(request);
// build response for cache
const cacheHeaders = new Headers();
for (const kv of originResponse.headers.entries()) {
cacheHeaders.append(kv[0], kv[1]);
}
cacheHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_KVRESPONSE');
cacheHeaders.append('Cache-Control', 'max-age=600');
const responseForKV = new Response(await originResponse.body, {
...originResponse,
headers: cacheHeaders
});
await responseKvInstance.storeResponse(request.url, responseForKV.clone());
return responseForKV.clone();
};

View File

@ -0,0 +1,31 @@
import * as interfaces from '../interfaces/index.js';
import * as plugins from '../plugins.js';
import { WorkerEvent } from '../classes.workerevent.js';
declare const fetch: plugins.cloudflareTypes.Fetcher['fetch'];
export const originResponder: interfaces.TRequestResponser = async (eventArg: WorkerEvent) => {
const originResponse: any = await fetch(eventArg.request);
// lets capture status
if (originResponse.status > 399) {
return;
}
const headers = new Headers();
for (const kv of originResponse.headers.entries()) {
headers.append(kv[0], kv[1]);
}
headers.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_FASTORIGIN');
const responsePassThroughStream = new TransformStream();
originResponse.body.pipeTo(responsePassThroughStream.writable);
// response
const responseForClient = new Response(responsePassThroughStream.readable, {
...originResponse,
headers,
});
eventArg.setResponse(responseForClient);
};

View File

@ -0,0 +1,18 @@
import * as interfaces from '../interfaces/index.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const preflightResponder: interfaces.TRequestResponser = async (eventArg: WorkerEvent) => {
if (eventArg.isPreflight) {
const corsHeaders = new Headers();
corsHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_PREFLIGHT');
corsHeaders.append('Access-Control-Allow-Origin', '*');
corsHeaders.append('Access-Control-Allow-Methods', '*');
corsHeaders.append('Access-Control-Allow-Headers', '*');
eventArg.setResponse(
new Response(null, {
headers: corsHeaders,
})
);
}
};

View File

@ -0,0 +1,9 @@
import * as interfaces from '../interfaces/index.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const redirectResponder: interfaces.TRequestResponser = async (cWorkerEventArg: WorkerEvent) => {
if (cWorkerEventArg.responderInstruction.type === 'redirect') {
cWorkerEventArg.setResponse(Response.redirect(cWorkerEventArg.responderInstruction.redirectUrl, 302));
}
};

View File

@ -0,0 +1,22 @@
import * as plugins from '../plugins.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const rendertronResponder = async (cworkerevent: WorkerEvent) => {
if (cworkerevent.routedThroughRendertron) {
const oldHeaders: any = cworkerevent.request.headers;
const rendertronHeaders = new Headers();
for (const kv of oldHeaders.entries()) {
const headerName = kv[0];
const headerValue = headerName === 'user-agent' ? 'Lossless Rendertron' : kv[1];
rendertronHeaders.append(headerName, headerValue);
}
const rendertronRequest = new Request(
`https://rendertron.lossless.one/render/${cworkerevent.request.url}`,
{
method: cworkerevent.request.method,
headers: rendertronHeaders
}
);
cworkerevent.setResponse(await fetch(rendertronRequest));
}
};

View File

@ -0,0 +1,31 @@
import * as interfaces from '../interfaces/index.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const staticResponder: interfaces.TRequestResponser = async (cWorkerEventArg: WorkerEvent) => {
if (cWorkerEventArg.responderInstruction.type === 'static') {
const originResponse: any = await fetch(
`https://statichost.lossless.one/resolve?url=${encodeURI(cWorkerEventArg.request.url)}`
);
const cacheHeaders = new Headers();
for (const kv of originResponse.headers.entries()) {
cacheHeaders.append(kv[0], kv[1]);
}
cacheHeaders.delete('SERVEZONE_ROUTE');
cacheHeaders.append('SERVEZONE_ROUTE', 'LOSSLESS_EDGE_STATICHOST');
if (cWorkerEventArg.responderInstruction.cacheClientSideForMin) {
cacheHeaders.delete('Cache-Control');
cacheHeaders.append('Cache-Control', `public, max-age=${cWorkerEventArg.responderInstruction.cacheClientSideForMin * 60}`);
cacheHeaders.delete('Expires');
cacheHeaders.append('Expires', new Date(Date.now() + cWorkerEventArg.responderInstruction.cacheClientSideForMin * 1000).toUTCString());
}
const responseForClient = new Response(await originResponse.clone().body, {
...originResponse,
headers: cacheHeaders
});
cWorkerEventArg.setResponse(responseForClient);
}
};

View File

@ -0,0 +1,23 @@
import * as interfaces from '../interfaces/index.js';
import * as plugins from '../plugins.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const timeoutResponder: interfaces.TRequestResponser = async (cWorkerEvent: WorkerEvent) => {
await plugins.smartdelay.delayFor(10000);
if (cWorkerEvent.routedThroughRendertron) {
await plugins.smartdelay.delayFor(10000);
}
if (!cWorkerEvent.hasResponse) {
const errorResponse = await fetch(
`https://nullresolve.lossless.one/custom?title=${encodeURI(
`Lossless Network: Request Cancellation!`
)}&heading=${encodeURI(`Error: Request Cancellation`)}&text=${encodeURI(
`Lossless Network could not decide how to respond to this request within 5 seconds. Therefore it timed out and has been canceled.
<p>requestUrl: ${cWorkerEvent.request.url}<br>
requestTime: ${Date.now()}<br>
referenceNumber: xxxxxx</p>`
)}`
);
cWorkerEvent.setResponse(errorResponse);
}
};

View File

@ -0,0 +1,21 @@
import * as interfaces from '../interfaces/index.js';
import { WorkerEvent } from '../classes.workerevent.js';
export const urlFormattingResponder: interfaces.TRequestResponser = async (eventArg: WorkerEvent) => {
let shouldCorrect = false;
const correctedUrl = new URL(eventArg.request.url);
if (eventArg.parsedUrl.hostname.startsWith('www.')) {
shouldCorrect = true;
correctedUrl.hostname = eventArg.parsedUrl.hostname.substring(
4,
eventArg.parsedUrl.hostname.length
);
}
if (eventArg.parsedUrl.protocol.startsWith('http:')) {
shouldCorrect = true;
correctedUrl.protocol = 'https:';
}
if (shouldCorrect) {
eventArg.setResponse(Response.redirect(`${correctedUrl.protocol}//${correctedUrl.host}${correctedUrl.pathname}${correctedUrl.search}`, 301));
}
};

View File

@ -0,0 +1,7 @@
import * as interfaces from './interfaces/index.js';
export class VersionHandler {
}
export const versionHandlerInstance = new VersionHandler();

View File

@ -1,5 +1,5 @@
import * as plugins from './typedserver_web.plugins.js';
import * as interfaces from '../ts_interfaces/index.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import { logger } from './typedserver_web.logger.js';
logger.log('info', `TypedServer-Devtools initialized!`);

View File

@ -24,7 +24,7 @@ export class NetworkManager {
}
public getEffectiveType() {
return this.getConnection()?.effectiveType || '4g';
return this.getConnection()?.effectiveType || '4g';
}
public updateConnectionStatus() {

View File

@ -11,13 +11,14 @@ import { logger } from './serviceworker.logging.js';
import { UpdateManager } from './serviceworker.classes.updatemanager.js';
import { NetworkManager } from './serviceworker.classes.networkmanager.js';
import { TaskManager } from './serviceworker.classes.taskmanager.js';
import { ServiceworkerBackend } from './classes.backend.js';
export class LosslessServiceWorker {
// STATIC
// INSTANCE
public serviceWindowRef: interfaces.ServiceWindow;
public leleServiceWorkerBackend: plugins.leleServiceworker.LosslessServiceworkerBackend;
public leleServiceWorkerBackend: ServiceworkerBackend;
public cacheManager: CacheManager;
@ -29,7 +30,7 @@ export class LosslessServiceWorker {
constructor(selfArg: interfaces.ServiceWindow) {
logger.log('info', `Service worker instantiating at ${Date.now()}`);
this.serviceWindowRef = selfArg;
this.leleServiceWorkerBackend = plugins.leleServiceworker.getServiceWorkerBackend({
this.leleServiceWorkerBackend = new ServiceworkerBackend({
self: selfArg,
purgeCache: async (reqArg) => {
await this.cacheManager.cleanCaches(),

View File

@ -1,11 +1,12 @@
import * as plugins from './plugins.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import { LosslessServiceWorker } from './serviceworker.classes.serviceworker.js';
import { logger } from './serviceworker.logging.js';
import { CacheManager } from './serviceworker.classes.cachemanager.js';
export class UpdateManager {
public lastUpdateCheck: number = 0;
public lastVersionInfo: plugins.lointServiceworker.IRequest_Serviceworker_Backend_VersionInfo['response'];
public lastVersionInfo: interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo['response'];
public serviceworkerRef: LosslessServiceWorker;
@ -59,8 +60,8 @@ export class UpdateManager {
*/
public async getVersionInfoFromServer() {
const getAppHashRequest = new plugins.typedrequest.TypedRequest<
plugins.lointServiceworker.IRequest_Serviceworker_Backend_VersionInfo
>('/lsw-typedrequest', 'serviceworker_versionInfo');
interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo
>('/sw-typedrequest', 'serviceworker_versionInfo');
const result = await getAppHashRequest.fire({});
return result;
}

View File

@ -1,5 +1,5 @@
// types
import type * as interfaces from './interfaces/index.js';
import type * as interfaces from '../dist_ts_interfaces/index.js';
export type {
interfaces
}

View File

@ -1,2 +0,0 @@
import * as plugins from '../lele-serviceworker.plugins.js';

View File

@ -1,5 +1,5 @@
import * as plugins from './lele-serviceworker.plugins.js';
import * as interfaces from './interfaces/index.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import { logger } from './serviceworker.logging.js';
/**
@ -12,7 +12,7 @@ export class ActionManager {
constructor() {
// lets define handlers on the client/tab side
this.deesComms.createTypedHandler<interfaces.IMessage_Serviceworker_Client_UpdateInfo>('serviceworker_newVersion', async req => {
this.deesComms.createTypedHandler<interfaces.serviceworker.IMessage_Serviceworker_Client_UpdateInfo>('serviceworker_newVersion', async req => {
setTimeout(() => {
window.location.reload();
}, 200);
@ -22,7 +22,7 @@ export class ActionManager {
public async waitForServiceWorkerConnection () {
console.log('waiting for service worker connection...')
const tr = this.deesComms.createTypedRequest<interfaces.IRequest_Client_Serviceworker_ConnectionPolling>('broadcastConnectionPolling');
const tr = this.deesComms.createTypedRequest<interfaces.serviceworker.IRequest_Client_Serviceworker_ConnectionPolling>('broadcastConnectionPolling');
let connected = false;
while (!connected) {
tr.fire({
@ -45,13 +45,13 @@ export class ActionManager {
}
public async purgeServiceWorkerCache () {
const tr = this.deesComms.createTypedRequest<interfaces.IRequest_PurgeServiceWorkerCache>('purgeServiceWorkerCache');
const tr = this.deesComms.createTypedRequest<interfaces.serviceworker.IRequest_PurgeServiceWorkerCache>('purgeServiceWorkerCache');
const response = await tr.fire({});
return response;
}
public async getVersionInfo () {
const tr = this.deesComms.createTypedRequest<interfaces.IRequest_Serviceworker_Backend_VersionInfo>('serviceworker_versionInfo');
const tr = this.deesComms.createTypedRequest<interfaces.serviceworker.IRequest_Serviceworker_Backend_VersionInfo>('serviceworker_versionInfo');
const response = await tr.fire({});
return response;
}

View File

@ -1,5 +1,5 @@
import * as plugins from './lele-serviceworker.plugins.js';
import * as interfaces from './interfaces/index.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import { logger } from "./serviceworker.logging.js";
export class NotificationManager {

View File

@ -1,5 +1,5 @@
import * as plugins from './lele-serviceworker.plugins.js';
import * as interfaces from './interfaces/index.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import { logger } from "./serviceworker.logging.js";
import { NotificationManager } from './lele-serviceworker.classes.notificationmanager.js';
import { ActionManager } from './lele-serviceworker.classes.actionmanager.js';