@api.global/typedserver
Version:
A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.
656 lines • 53.4 kB
JavaScript
import * as plugins from './plugins.js';
import * as interfaces from '../dist_ts_interfaces/index.js';
import { DevToolsController } from './controllers/controller.devtools.js';
import { TypedRequestController } from './controllers/controller.typedrequest.js';
import { BuiltInRoutesController } from './controllers/controller.builtin.js';
export class TypedServer {
// instance
options;
smartServe;
smartwatchInstance;
serveDirHashSubject = new plugins.smartrx.rxjs.ReplaySubject(1);
serveHash = '000000';
typedsocket;
typedrouter = new plugins.typedrequest.TypedRouter();
// Sitemap helper
sitemapHelper;
smartmanifestInstance;
// Decorated controllers
devToolsController;
typedRequestController;
builtInRoutesController;
// File server for static files
fileServer;
lastReload = Date.now();
ended = false;
constructor(optionsArg) {
const standardOptions = {
port: 3000,
injectReload: false,
serveDir: null,
watch: false,
cors: true,
};
this.options = {
...standardOptions,
...optionsArg,
};
}
/**
* Access sitemap URLs (for adding/replacing)
*/
get sitemap() {
return this.sitemapHelper;
}
/**
* Add a custom route handler
* Supports Express-style path patterns like '/path/:param' and '/path/*splat'
* @param path - The route path pattern
* @param method - HTTP method (GET, POST, PUT, DELETE, PATCH, ALL)
* @param handler - Async function that receives IRequestContext and returns Response or null
*/
addRoute(path, method, handler) {
// Delegate to smartserve's ControllerRegistry
plugins.smartserve.ControllerRegistry.addRoute(path, method, handler);
}
/**
* inits and starts the server
*/
async start() {
// Validate essential configuration before starting
if (this.options.injectReload && !this.options.serveDir) {
throw new Error('You set to inject the reload script without a serve dir. This is not supported at the moment.');
}
const port = typeof this.options.port === 'string'
? parseInt(this.options.port, 10)
: this.options.port || 3000;
// Initialize optional helpers
if (this.options.sitemap) {
this.sitemapHelper = new SitemapHelper(this.options.domain);
}
if (this.options.manifest) {
this.smartmanifestInstance = new plugins.smartmanifest.SmartManifest(this.options.manifest);
}
// Initialize file server for static files
if (this.options.serveDir) {
this.fileServer = new plugins.smartserve.FileServer({
root: this.options.serveDir,
index: ['index.html'],
etag: true,
});
}
// Initialize decorated controllers
if (this.options.injectReload) {
this.devToolsController = new DevToolsController({
getLastReload: () => this.lastReload,
getEnded: () => this.ended,
});
}
this.typedRequestController = new TypedRequestController(this.typedrouter);
this.builtInRoutesController = new BuiltInRoutesController({
domain: this.options.domain,
robots: this.options.robots,
manifest: this.smartmanifestInstance,
sitemap: this.options.sitemap,
feed: this.options.feed,
appVersion: this.options.appVersion,
feedMetadata: this.options.feedMetadata,
articleGetterFunction: this.options.articleGetterFunction,
blockWaybackMachine: this.options.blockWaybackMachine,
getSitemapUrls: () => this.sitemapHelper?.urls || [],
});
// Register controllers with SmartServe's ControllerRegistry
// Note: @Route decorators auto-register classes at import time.
// Controllers with constructor args (like DevToolsController) use default no-op
// constructors to handle auto-instantiation gracefully.
if (this.options.injectReload) {
plugins.smartserve.ControllerRegistry.registerInstance(this.devToolsController);
}
plugins.smartserve.ControllerRegistry.registerInstance(this.typedRequestController);
plugins.smartserve.ControllerRegistry.registerInstance(this.builtInRoutesController);
// Compile routes for fast matching
plugins.smartserve.ControllerRegistry.compileRoutes();
// Build SmartServe options
const smartServeOptions = {
port,
hostname: '0.0.0.0',
compression: this.options.compression,
tls: this.options.privateKey && this.options.publicKey
? {
key: this.options.privateKey,
cert: this.options.publicKey,
}
: undefined,
websocket: {
typedRouter: this.typedrouter,
onConnectionOpen: (peer) => {
peer.tags.add('allClients');
console.log(`WebSocket connected: ${peer.id}`);
},
onConnectionClose: (peer) => {
console.log(`WebSocket disconnected: ${peer.id}`);
},
},
};
this.smartServe = new plugins.smartserve.SmartServe(smartServeOptions);
// Set up custom request handler that integrates with ControllerRegistry
this.smartServe.setHandler(async (request) => {
return this.handleRequest(request);
});
// Setup file watching
if (this.options.watch && this.options.serveDir) {
try {
// Use glob pattern to match all files recursively in serveDir
const watchGlob = this.options.serveDir.endsWith('/')
? `${this.options.serveDir}**/*`
: `${this.options.serveDir}/**/*`;
this.smartwatchInstance = new plugins.smartwatch.Smartwatch([watchGlob]);
await this.smartwatchInstance.start();
(await this.smartwatchInstance.getObservableFor('change')).subscribe(async () => {
await this.createServeDirHash();
this.reload();
});
await this.createServeDirHash();
}
catch (error) {
console.error('Failed to initialize file watching:', error);
}
}
// Start the server
await this.smartServe.start();
console.log(`TypedServer listening on port ${port}`);
// Setup TypedSocket using SmartServe integration
try {
this.typedsocket = plugins.typedsocket.TypedSocket.fromSmartServe(this.smartServe, this.typedrouter);
// Setup typedrouter handlers
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('getLatestServerChangeTime', async () => {
return {
time: this.lastReload,
};
}));
// Speedtest handler for service worker dashboard
// Client calls this in a loop for the test duration to get accurate time-based measurements
this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('serviceworker_speedtest', async (reqArg) => {
const chunkSizeKB = reqArg.chunkSizeKB || 64;
const sizeBytes = chunkSizeKB * 1024;
let payload;
let bytesTransferred = 0;
switch (reqArg.type) {
case 'download_chunk':
// Generate chunk data for download test
payload = 'x'.repeat(sizeBytes);
bytesTransferred = sizeBytes;
break;
case 'upload_chunk':
// Acknowledge received upload data
bytesTransferred = reqArg.payload?.length || 0;
break;
case 'latency':
// Simple ping - minimal data
bytesTransferred = 0;
break;
}
return { bytesTransferred, timestamp: Date.now(), payload };
}));
}
catch (error) {
console.error('Failed to initialize TypedSocket:', error);
}
}
/**
* Create an IRequestContext from a Request
*/
createContext(request, params) {
const url = new URL(request.url);
const method = request.method.toUpperCase();
// Parse query params
const query = {};
url.searchParams.forEach((value, key) => {
query[key] = value;
});
// Cached body parsers (lazy evaluation)
let jsonCache;
let textCache;
let arrayBufferCache;
let formDataCache;
return {
request,
params,
query,
headers: request.headers,
path: url.pathname,
method,
url,
runtime: 'node',
state: {},
async json() {
if (jsonCache === undefined) {
jsonCache = await request.clone().json();
}
return jsonCache;
},
async text() {
if (textCache === undefined) {
textCache = await request.clone().text();
}
return textCache;
},
async arrayBuffer() {
if (arrayBufferCache === undefined) {
arrayBufferCache = await request.clone().arrayBuffer();
}
return arrayBufferCache;
},
async formData() {
if (formDataCache === undefined) {
formDataCache = await request.clone().formData();
}
return formDataCache;
},
};
}
/**
* Build CSP header string from configuration
*/
buildCspHeader(csp) {
const directives = [];
const addDirective = (name, value) => {
if (value) {
const sources = Array.isArray(value) ? value.join(' ') : value;
directives.push(`${name} ${sources}`);
}
};
addDirective('default-src', csp.defaultSrc);
addDirective('script-src', csp.scriptSrc);
addDirective('style-src', csp.styleSrc);
addDirective('img-src', csp.imgSrc);
addDirective('font-src', csp.fontSrc);
addDirective('connect-src', csp.connectSrc);
addDirective('media-src', csp.mediaSrc);
addDirective('frame-src', csp.frameSrc);
addDirective('object-src', csp.objectSrc);
addDirective('worker-src', csp.workerSrc);
addDirective('form-action', csp.formAction);
addDirective('frame-ancestors', csp.frameAncestors);
addDirective('base-uri', csp.baseUri);
if (csp.reportUri) {
directives.push(`report-uri ${csp.reportUri}`);
}
if (csp.reportTo) {
directives.push(`report-to ${csp.reportTo}`);
}
if (csp.upgradeInsecureRequests) {
directives.push('upgrade-insecure-requests');
}
if (csp.blockAllMixedContent) {
directives.push('block-all-mixed-content');
}
return directives.join('; ');
}
/**
* Apply all configured headers (CORS, security) to a response
*/
applyResponseHeaders(response) {
const headers = new Headers(response.headers);
// CORS headers
if (this.options.cors) {
headers.set('Access-Control-Allow-Origin', '*');
headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH');
headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
headers.set('Access-Control-Max-Age', '86400');
}
// Security headers
const security = this.options.securityHeaders;
if (security) {
// Content Security Policy
if (security.csp) {
const cspHeader = this.buildCspHeader(security.csp);
if (cspHeader) {
headers.set('Content-Security-Policy', cspHeader);
}
}
// X-Frame-Options
if (security.xFrameOptions) {
headers.set('X-Frame-Options', security.xFrameOptions);
}
// X-Content-Type-Options
if (security.xContentTypeOptions) {
headers.set('X-Content-Type-Options', 'nosniff');
}
// X-XSS-Protection
if (security.xXssProtection) {
const value = typeof security.xXssProtection === 'string'
? security.xXssProtection
: '1; mode=block';
headers.set('X-XSS-Protection', value);
}
// Referrer-Policy
if (security.referrerPolicy) {
headers.set('Referrer-Policy', security.referrerPolicy);
}
// Strict-Transport-Security (HSTS)
if (security.hstsMaxAge !== undefined) {
let hsts = `max-age=${security.hstsMaxAge}`;
if (security.hstsIncludeSubDomains) {
hsts += '; includeSubDomains';
}
if (security.hstsPreload) {
hsts += '; preload';
}
headers.set('Strict-Transport-Security', hsts);
}
// Permissions-Policy
if (security.permissionsPolicy) {
const policies = Object.entries(security.permissionsPolicy)
.map(([feature, allowlist]) => `${feature}=(${allowlist.join(' ')})`)
.join(', ');
if (policies) {
headers.set('Permissions-Policy', policies);
}
}
// Cross-Origin-Opener-Policy
if (security.crossOriginOpenerPolicy) {
headers.set('Cross-Origin-Opener-Policy', security.crossOriginOpenerPolicy);
}
// Cross-Origin-Embedder-Policy
if (security.crossOriginEmbedderPolicy) {
headers.set('Cross-Origin-Embedder-Policy', security.crossOriginEmbedderPolicy);
}
// Cross-Origin-Resource-Policy
if (security.crossOriginResourcePolicy) {
headers.set('Cross-Origin-Resource-Policy', security.crossOriginResourcePolicy);
}
}
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers
});
}
/**
* Main request handler - routes to appropriate sub-handlers
*/
async handleRequest(request) {
const url = new URL(request.url);
const path = url.pathname;
const method = request.method.toUpperCase();
// Handle OPTIONS preflight for CORS
if (method === 'OPTIONS' && this.options.cors) {
return this.applyResponseHeaders(new Response(null, { status: 204 }));
}
// Process the request and wrap response with all configured headers
const response = await this.handleRequestInternal(request, path, method);
return this.applyResponseHeaders(response);
}
/**
* Internal request handler - routes to appropriate sub-handlers
*/
async handleRequestInternal(request, path, method) {
// First, try to match via ControllerRegistry (decorated routes)
const match = plugins.smartserve.ControllerRegistry.matchRoute(path, method);
if (match) {
try {
const context = this.createContext(request, match.params);
const result = await match.route.handler(context);
// Handle Response or convert to Response
if (result instanceof Response) {
return result;
}
return new Response(JSON.stringify(result), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
catch (error) {
if (error instanceof plugins.smartserve.RouteNotFoundError) {
// Route explicitly threw "not found", continue to other handlers
}
else {
console.error('Controller error:', error);
return new Response('Internal Server Error', { status: 500 });
}
}
}
// HTML injection for reload (if enabled)
if (this.options.injectReload && this.options.serveDir) {
const response = await this.handleHtmlWithInjection(request);
if (response)
return response;
}
// Try static file serving
if (this.fileServer && (method === 'GET' || method === 'HEAD')) {
try {
const staticResponse = await this.fileServer.serve(request);
if (staticResponse) {
return staticResponse;
}
}
catch (error) {
// Fall through to 404
}
}
// Default answer for root
if (path === '/' && method === 'GET' && this.options.defaultAnswer) {
const html = await this.options.defaultAnswer();
return new Response(html, {
status: 200,
headers: { 'Content-Type': 'text/html' },
});
}
// SPA fallback - serve index.html for non-file routes
if (this.options.spaFallback && this.options.serveDir && method === 'GET' && !path.includes('.')) {
try {
const indexPath = plugins.path.join(this.options.serveDir, 'index.html');
let html = await plugins.fsInstance.file(indexPath).encoding('utf8').read();
// Inject reload script if enabled
if (this.options.injectReload && html.includes('<head>')) {
const injection = `<head>
<!-- injected by @apiglobal/typedserver start -->
<script async defer type="module" src="/typedserver/devtools"></script>
<script>
globalThis.typedserver = {
lastReload: ${this.lastReload},
versionInfo: ${JSON.stringify({}, null, 2)},
}
</script>
<!-- injected by @apiglobal/typedserver stop -->
`;
html = html.replace('<head>', injection);
}
return new Response(html, {
status: 200,
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'no-cache, no-store, must-revalidate',
appHash: this.serveHash,
},
});
}
catch {
// Fall through to 404
}
}
// Not found
return new Response('Not Found', { status: 404 });
}
/**
* Handle HTML files with reload script injection
*/
async handleHtmlWithInjection(request) {
const url = new URL(request.url);
const requestPath = url.pathname;
// Check if this is a request for an HTML file or root
if (requestPath === '/' || requestPath.endsWith('.html') || !requestPath.includes('.')) {
try {
let filePath = requestPath === '/' ? 'index.html' : requestPath.slice(1);
if (!filePath.endsWith('.html') && !filePath.includes('.')) {
filePath = plugins.path.join(filePath, 'index.html');
}
const fullPath = plugins.path.join(this.options.serveDir, filePath);
// Security check
if (!fullPath.startsWith(this.options.serveDir)) {
return new Response('Forbidden', { status: 403 });
}
let fileContent = (await plugins.fsInstance
.file(fullPath)
.encoding('utf8')
.read());
// Inject reload script
if (fileContent.includes('<head>')) {
const injection = `<head>
<!-- injected by @apiglobal/typedserver start -->
<script async defer type="module" src="/typedserver/devtools"></script>
<script>
globalThis.typedserver = {
lastReload: ${this.lastReload},
versionInfo: ${JSON.stringify({}, null, 2)},
}
</script>
<!-- injected by @apiglobal/typedserver stop -->
`;
fileContent = fileContent.replace('<head>', injection);
console.log('injected typedserver script.');
}
return new Response(fileContent, {
status: 200,
headers: {
'Content-Type': 'text/html; charset=utf-8',
'Cache-Control': 'no-cache, no-store, must-revalidate',
Pragma: 'no-cache',
Expires: '0',
appHash: this.serveHash,
},
});
}
catch (error) {
// Fall through to default handling
}
}
return null;
}
/**
* reloads the page
*/
async reload() {
this.lastReload = Date.now();
if (!this.typedsocket) {
console.warn('TypedSocket not initialized, skipping client notifications');
return;
}
// Push cache invalidation to service workers first
try {
const swConnections = await this.typedsocket.findAllTargetConnectionsByTag('serviceworker');
for (const connection of swConnections) {
const pushCacheInvalidate = this.typedsocket.createTypedRequest('serviceworker_cacheInvalidate', connection);
pushCacheInvalidate.fire({
reason: 'File change detected',
timestamp: this.lastReload,
}).catch(err => {
console.warn('Failed to push cache invalidation to service worker:', err);
});
}
if (swConnections.length > 0) {
console.log(`Pushed cache invalidation to ${swConnections.length} service worker(s)`);
}
}
catch (error) {
console.warn('Failed to notify service workers:', error);
}
// Notify frontend clients
try {
const connections = await this.typedsocket.findAllTargetConnectionsByTag('typedserver_frontend');
for (const connection of connections) {
const pushTime = this.typedsocket.createTypedRequest('pushLatestServerChangeTime', connection);
pushTime.fire({
time: this.lastReload,
}).catch(err => {
console.warn('Failed to push latest server change time to client:', err);
});
}
}
catch (error) {
console.error('Failed to notify clients about reload:', error);
}
}
/**
* Stops the server and cleans up resources
*/
async stop() {
this.ended = true;
const stopWithErrorHandling = async (stopFn, componentName) => {
try {
await stopFn();
}
catch (err) {
console.error(`Error stopping ${componentName}:`, err);
}
};
const tasks = [];
// Stop SmartServe
if (this.smartServe) {
tasks.push(stopWithErrorHandling(() => this.smartServe.stop(), 'SmartServe'));
}
// Stop TypedSocket (in SmartServe mode, this is a no-op but good for cleanup)
if (this.typedsocket) {
tasks.push(stopWithErrorHandling(() => this.typedsocket.stop(), 'TypedSocket'));
}
// Stop file watcher
if (this.smartwatchInstance) {
tasks.push(stopWithErrorHandling(() => this.smartwatchInstance.stop(), 'file watcher'));
}
await Promise.all(tasks);
}
/**
* Calculates a hash of the served directory for cache busting
*/
async createServeDirHash() {
try {
const serveDirHash = await plugins.fsInstance
.directory(this.options.serveDir)
.recursive()
.treeHash();
this.serveHash = serveDirHash.slice(0, 12);
console.log('Current ServeDir hash: ' + this.serveHash);
this.serveDirHashSubject.next(this.serveHash);
}
catch (error) {
console.error('Failed to create serve directory hash:', error);
const fallbackHash = Date.now().toString(16).slice(-6);
this.serveHash = fallbackHash;
console.log('Using fallback hash: ' + fallbackHash);
this.serveDirHashSubject.next(fallbackHash);
}
}
}
// ============================================================================
// Helper Classes
// ============================================================================
/**
* Sitemap helper class
*/
class SitemapHelper {
smartSitemap = new plugins.smartsitemap.SmartSitemap();
urls = [];
constructor(domain) {
if (domain) {
this.urls.push({
url: `https://${domain}/`,
timestamp: Date.now(),
frequency: 'daily',
});
}
}
async createSitemap() {
return this.smartSitemap.createSitemapFromUrlInfoArray(this.urls);
}
async createSitemapNews(articles) {
return this.smartSitemap.createSitemapNewsFromArticleArray(articles);
}
replaceUrls(urlsArg) {
this.urls = urlsArg;
}
addUrls(urlsArg) {
this.urls = this.urls.concat(urlsArg);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50eXBlZHNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMudHlwZWRzZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLFVBQVUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM3RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUMxRSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQUNsRixPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQXFKOUUsTUFBTSxPQUFPLFdBQVc7SUFDdEIsV0FBVztJQUNKLE9BQU8sQ0FBaUI7SUFDeEIsVUFBVSxDQUFnQztJQUMxQyxrQkFBa0IsQ0FBZ0M7SUFDbEQsbUJBQW1CLEdBQUcsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQVMsQ0FBQyxDQUFDLENBQUM7SUFDeEUsU0FBUyxHQUFXLFFBQVEsQ0FBQztJQUM3QixXQUFXLENBQWtDO0lBQzdDLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUM7SUFFNUQsaUJBQWlCO0lBQ1QsYUFBYSxDQUFnQjtJQUM3QixxQkFBcUIsQ0FBc0M7SUFFbkUsd0JBQXdCO0lBQ2hCLGtCQUFrQixDQUFxQjtJQUN2QyxzQkFBc0IsQ0FBeUI7SUFDL0MsdUJBQXVCLENBQTBCO0lBRXpELCtCQUErQjtJQUN2QixVQUFVLENBQWdDO0lBRTNDLFVBQVUsR0FBVyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDaEMsS0FBSyxHQUFHLEtBQUssQ0FBQztJQUVyQixZQUFZLFVBQTBCO1FBQ3BDLE1BQU0sZUFBZSxHQUFtQjtZQUN0QyxJQUFJLEVBQUUsSUFBSTtZQUNWLFlBQVksRUFBRSxLQUFLO1lBQ25CLFFB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