@singhey/spa-ssr-renderer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -0
  3. package/dist/__tests__/setup.test.d.ts +2 -0
  4. package/dist/__tests__/setup.test.d.ts.map +1 -0
  5. package/dist/__tests__/setup.test.js +22 -0
  6. package/dist/__tests__/setup.test.js.map +1 -0
  7. package/dist/components/BotDetector.d.ts +12 -0
  8. package/dist/components/BotDetector.d.ts.map +1 -0
  9. package/dist/components/BotDetector.js +46 -0
  10. package/dist/components/BotDetector.js.map +1 -0
  11. package/dist/components/CacheManager.d.ts +13 -0
  12. package/dist/components/CacheManager.d.ts.map +1 -0
  13. package/dist/components/CacheManager.js +46 -0
  14. package/dist/components/CacheManager.js.map +1 -0
  15. package/dist/components/FileServer.d.ts +7 -0
  16. package/dist/components/FileServer.d.ts.map +1 -0
  17. package/dist/components/FileServer.js +15 -0
  18. package/dist/components/FileServer.js.map +1 -0
  19. package/dist/components/RequestRouter.d.ts +7 -0
  20. package/dist/components/RequestRouter.d.ts.map +1 -0
  21. package/dist/components/RequestRouter.js +15 -0
  22. package/dist/components/RequestRouter.js.map +1 -0
  23. package/dist/components/SSRRenderer.d.ts +9 -0
  24. package/dist/components/SSRRenderer.d.ts.map +1 -0
  25. package/dist/components/SSRRenderer.js +55 -0
  26. package/dist/components/SSRRenderer.js.map +1 -0
  27. package/dist/components/index.d.ts +6 -0
  28. package/dist/components/index.d.ts.map +1 -0
  29. package/dist/components/index.js +6 -0
  30. package/dist/components/index.js.map +1 -0
  31. package/dist/config/index.d.ts +4 -0
  32. package/dist/config/index.d.ts.map +1 -0
  33. package/dist/config/index.js +44 -0
  34. package/dist/config/index.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +21 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/server.d.ts +27 -0
  40. package/dist/server.d.ts.map +1 -0
  41. package/dist/server.js +245 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/types/index.d.ts +101 -0
  44. package/dist/types/index.d.ts.map +1 -0
  45. package/dist/types/index.js +2 -0
  46. package/dist/types/index.js.map +1 -0
  47. package/dist/utils/index.d.ts +2 -0
  48. package/dist/utils/index.d.ts.map +1 -0
  49. package/dist/utils/index.js +2 -0
  50. package/dist/utils/index.js.map +1 -0
  51. package/dist/utils/logger.d.ts +13 -0
  52. package/dist/utils/logger.d.ts.map +1 -0
  53. package/dist/utils/logger.js +17 -0
  54. package/dist/utils/logger.js.map +1 -0
  55. package/package.json +80 -0
@@ -0,0 +1,44 @@
1
+ import { config } from 'dotenv';
2
+ config();
3
+ export const defaultConfig = {
4
+ port: parseInt(process.env.PORT || '3001', 10),
5
+ staticDir: process.env.STATIC_DIR || 'public',
6
+ spaEntryPoint: process.env.SPA_ENTRY_POINT || 'index.html',
7
+ prerender: {
8
+ paths: process.env.PRERENDER_PATHS
9
+ ? process.env.PRERENDER_PATHS.split(',').map(p => p.trim())
10
+ : undefined,
11
+ sitemaps: process.env.PRERENDER_SITEMAPS
12
+ ? process.env.PRERENDER_SITEMAPS.split(',').map(s => s.trim())
13
+ : undefined,
14
+ exclude: process.env.PRERENDER_EXCLUDE
15
+ ? process.env.PRERENDER_EXCLUDE.split(',').map(e => e.trim())
16
+ : undefined,
17
+ },
18
+ // Backward compatibility
19
+ prerenderPaths: process.env.PRERENDER_PATHS
20
+ ? process.env.PRERENDER_PATHS.split(',').map(p => p.trim())
21
+ : undefined,
22
+ cache: {
23
+ type: process.env.CACHE_TYPE || 'memory',
24
+ ttl: parseInt(process.env.CACHE_TTL || '300000', 10), // 5 minutes
25
+ maxSize: parseInt(process.env.CACHE_MAX_SIZE || '100', 10),
26
+ redisUrl: process.env.REDIS_URL,
27
+ },
28
+ renderer: {
29
+ timeout: parseInt(process.env.RENDER_TIMEOUT || '30000', 10), // 30 seconds
30
+ viewport: {
31
+ width: parseInt(process.env.VIEWPORT_WIDTH || '1280', 10),
32
+ height: parseInt(process.env.VIEWPORT_HEIGHT || '720', 10),
33
+ },
34
+ waitForNetworkIdle: process.env.WAIT_FOR_NETWORK_IDLE === 'true',
35
+ },
36
+ botDetection: {
37
+ customPatterns: [],
38
+ enableVerification: process.env.ENABLE_BOT_VERIFICATION === 'true',
39
+ },
40
+ };
41
+ export function loadConfig() {
42
+ return { ...defaultConfig };
43
+ }
44
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,QAAQ,CAAA;AAC7B,MAAM,EAAE,CAAA;AAER,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,CAAC;IAC9C,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,QAAQ;IAC7C,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,YAAY;IAC1D,SAAS,EAAE;QACT,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;YAChC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3D,CAAC,CAAC,SAAS;QACb,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;YACtC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9D,CAAC,CAAC,SAAS;QACb,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;YACpC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7D,CAAC,CAAC,SAAS;KACd;IACD,yBAAyB;IACzB,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;QACzC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3D,CAAC,CAAC,SAAS;IACb,KAAK,EAAE;QACL,IAAI,EAAG,OAAO,CAAC,GAAG,CAAC,UAAiC,IAAI,QAAQ;QAChE,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,QAAQ,EAAE,EAAE,CAAC,EAAE,YAAY;QAClE,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,KAAK,EAAE,EAAE,CAAC;QAC1D,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;KAChC;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,EAAE,EAAE,CAAC,EAAE,aAAa;QAC3E,QAAQ,EAAE;YACR,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,MAAM,EAAE,EAAE,CAAC;YACzD,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,KAAK,EAAE,EAAE,CAAC;SAC3D;QACD,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,MAAM;KACjE;IACD,YAAY,EAAE;QACZ,cAAc,EAAE,EAAE;QAClB,kBAAkB,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM;KACnE;CACF,CAAC;AAEF,MAAM,UAAU,UAAU;IACxB,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ import { SPASSRServer } from './server.js';
2
+ import { loadConfig } from './config/index.js';
3
+ import { ConsoleLogger } from './utils/index.js';
4
+ const config = loadConfig();
5
+ const logger = new ConsoleLogger();
6
+ // Create and start server
7
+ const server = new SPASSRServer({ config, logger });
8
+ // Graceful shutdown
9
+ const shutdown = async () => {
10
+ logger.info('Shutting down gracefully');
11
+ await server.stop();
12
+ process.exit(0);
13
+ };
14
+ process.on('SIGTERM', shutdown);
15
+ process.on('SIGINT', shutdown);
16
+ // Start the server
17
+ server.start().catch((err) => {
18
+ logger.error('Failed to start server:', err);
19
+ process.exit(1);
20
+ });
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,MAAM,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;AAEnC,0BAA0B;AAC1B,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAEpD,oBAAoB;AACpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;IAC1B,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACxC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;AAEF,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAChC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAE/B,mBAAmB;AACnB,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC3B,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { FastifyInstance } from 'fastify';
2
+ import { ServerConfig } from './types/index.js';
3
+ import { ConsoleLogger } from './utils/index.js';
4
+ export interface ServerOptions {
5
+ config: ServerConfig;
6
+ logger?: ConsoleLogger;
7
+ }
8
+ export declare class SPASSRServer {
9
+ private server;
10
+ private config;
11
+ private logger;
12
+ private botDetector;
13
+ private cacheManager;
14
+ private renderer;
15
+ private isPrerendering;
16
+ constructor(options: ServerOptions);
17
+ private setupRoutes;
18
+ private getPrerenderPaths;
19
+ private parseSitemap;
20
+ private extractUrlsFromSitemapXml;
21
+ private prerenderPaths;
22
+ start(): Promise<void>;
23
+ stop(): Promise<void>;
24
+ getServer(): FastifyInstance;
25
+ }
26
+ export { type ServerConfig } from './types/index.js';
27
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAInD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAUjD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,EAAE,aAAa;IA4BlC,OAAO,CAAC,WAAW;YAmDL,iBAAiB;YAkDjB,YAAY;IAmC1B,OAAO,CAAC,yBAAyB;YAmBnB,cAAc;IA0CtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,SAAS,IAAI,eAAe;CAG7B;AAED,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,245 @@
1
+ import fastify from 'fastify';
2
+ import fastifyStatic from '@fastify/static';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { ConsoleLogger } from './utils/index.js';
6
+ import { BotDetector } from './components/BotDetector.js';
7
+ import { CacheManager } from './components/CacheManager.js';
8
+ import { SSRRenderer } from './components/SSRRenderer.js';
9
+ import Sitemapper from 'sitemapper';
10
+ import { readFileSync } from 'fs';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ export class SPASSRServer {
14
+ constructor(options) {
15
+ this.isPrerendering = false;
16
+ this.config = options.config;
17
+ this.logger = options.logger || new ConsoleLogger();
18
+ this.botDetector = new BotDetector();
19
+ this.cacheManager = new CacheManager(this.config.cache.maxSize, this.config.cache.ttl);
20
+ this.renderer = new SSRRenderer();
21
+ // Configure Fastify with pretty logging in development
22
+ const isDev = process.env.NODE_ENV !== 'production';
23
+ this.server = fastify({
24
+ // logger: isDev ? {
25
+ // transport: {
26
+ // target: 'pino-pretty',
27
+ // options: {
28
+ // colorize: true,
29
+ // translateTime: 'HH:MM:ss Z',
30
+ // ignore: 'pid,hostname'
31
+ // }
32
+ // },
33
+ // } : true
34
+ });
35
+ this.setupRoutes();
36
+ }
37
+ setupRoutes() {
38
+ // Register static file serving for public directory
39
+ this.server.register(fastifyStatic, {
40
+ root: path.join(__dirname, '..', this.config.staticDir),
41
+ prefix: '/',
42
+ });
43
+ // Main request handler - check for bots and serve cached content
44
+ this.server.addHook('onRequest', async (request, reply) => {
45
+ const userAgent = request.headers['user-agent'] || '';
46
+ const isBot = this.botDetector.isBot(userAgent);
47
+ if (isBot) {
48
+ const cachedContent = await this.cacheManager.get(request.url);
49
+ if (cachedContent) {
50
+ this.logger.info(`Serving cached content for bot: ${request.url}`);
51
+ reply.type('text/html').send(cachedContent);
52
+ return;
53
+ }
54
+ }
55
+ });
56
+ // SPA fallback - serve index.html for non-file routes
57
+ this.server.setNotFoundHandler(async (request, reply) => {
58
+ // If the request is for a file that doesn't exist (has extension), return 404
59
+ const hasExtension = /\.[a-zA-Z0-9]+$/.test(request.url);
60
+ if (hasExtension) {
61
+ reply.code(404).send({ error: 'File not found' });
62
+ return;
63
+ }
64
+ // For routes without extensions, serve the SPA entry point
65
+ reply.type('text/html');
66
+ return reply.sendFile(this.config.spaEntryPoint);
67
+ });
68
+ // Basic health check route
69
+ this.server.get('/health', async () => {
70
+ const cacheStats = this.cacheManager.getStats();
71
+ return {
72
+ status: 'OK',
73
+ timestamp: new Date().toISOString(),
74
+ version: '1.0.0',
75
+ cache: cacheStats,
76
+ prerendering: this.isPrerendering,
77
+ };
78
+ });
79
+ }
80
+ async getPrerenderPaths() {
81
+ const paths = new Set();
82
+ // Support backward compatibility with prerenderPaths
83
+ const legacyPaths = this.config.prerenderPaths;
84
+ if (legacyPaths && legacyPaths.length > 0) {
85
+ legacyPaths.forEach(p => paths.add(p));
86
+ }
87
+ // Get paths from new prerender config
88
+ const prerenderConfig = this.config.prerender;
89
+ if (!prerenderConfig) {
90
+ return Array.from(paths);
91
+ }
92
+ // Add explicit paths
93
+ if (prerenderConfig.paths && prerenderConfig.paths.length > 0) {
94
+ prerenderConfig.paths.forEach(p => paths.add(p));
95
+ }
96
+ // Parse sitemaps
97
+ if (prerenderConfig.sitemaps && prerenderConfig.sitemaps.length > 0) {
98
+ for (const sitemapSource of prerenderConfig.sitemaps) {
99
+ try {
100
+ const sitemapPaths = await this.parseSitemap(sitemapSource);
101
+ sitemapPaths.forEach(p => paths.add(p));
102
+ }
103
+ catch (error) {
104
+ this.logger.error(`Failed to parse sitemap ${sitemapSource}:`, error);
105
+ }
106
+ }
107
+ }
108
+ // Apply exclusions
109
+ if (prerenderConfig.exclude && prerenderConfig.exclude.length > 0) {
110
+ const excludePatterns = prerenderConfig.exclude.map(pattern => {
111
+ // Convert glob-like patterns to regex
112
+ const regexPattern = pattern
113
+ .replace(/\*/g, '.*')
114
+ .replace(/\?/g, '.');
115
+ return new RegExp(`^${regexPattern}$`);
116
+ });
117
+ return Array.from(paths).filter(path => {
118
+ return !excludePatterns.some(pattern => pattern.test(path));
119
+ });
120
+ }
121
+ return Array.from(paths);
122
+ }
123
+ async parseSitemap(source) {
124
+ try {
125
+ // Check if it's a local file path
126
+ if (source.startsWith('./') || source.startsWith('../') || !source.startsWith('http')) {
127
+ const filePath = path.isAbsolute(source)
128
+ ? source
129
+ : path.join(process.cwd(), source);
130
+ const content = readFileSync(filePath, 'utf-8');
131
+ return this.extractUrlsFromSitemapXml(content);
132
+ }
133
+ // It's a URL, fetch it
134
+ const sitemap = new Sitemapper({
135
+ url: source,
136
+ timeout: 15000,
137
+ });
138
+ const { sites } = await sitemap.fetch();
139
+ // Extract paths from full URLs
140
+ return sites.map(url => {
141
+ try {
142
+ const urlObj = new URL(url);
143
+ return urlObj.pathname;
144
+ }
145
+ catch {
146
+ return url;
147
+ }
148
+ });
149
+ }
150
+ catch (error) {
151
+ this.logger.error(`Error parsing sitemap ${source}:`, error);
152
+ return [];
153
+ }
154
+ }
155
+ extractUrlsFromSitemapXml(xml) {
156
+ const urls = [];
157
+ const locRegex = /<loc>(.*?)<\/loc>/g;
158
+ let match;
159
+ while ((match = locRegex.exec(xml)) !== null) {
160
+ try {
161
+ const url = match[1];
162
+ const urlObj = new URL(url);
163
+ urls.push(urlObj.pathname);
164
+ }
165
+ catch {
166
+ // If it's not a full URL, treat it as a path
167
+ urls.push(match[1]);
168
+ }
169
+ }
170
+ return urls;
171
+ }
172
+ async prerenderPaths() {
173
+ const pathsToRender = await this.getPrerenderPaths();
174
+ if (pathsToRender.length === 0) {
175
+ this.logger.info('No paths to pre-render');
176
+ return;
177
+ }
178
+ this.isPrerendering = true;
179
+ this.logger.info(`Starting pre-rendering of ${pathsToRender.length} paths...`);
180
+ try {
181
+ await this.renderer.initialize();
182
+ const baseUrl = `http://localhost:${this.config.port}`;
183
+ for (const path of pathsToRender) {
184
+ try {
185
+ this.logger.info(`Pre-rendering: ${path}`);
186
+ const url = `${baseUrl}${path}`;
187
+ const html = await this.renderer.render(url, {
188
+ timeout: this.config.renderer.timeout,
189
+ viewport: this.config.renderer.viewport,
190
+ waitForNetworkIdle: this.config.renderer.waitForNetworkIdle,
191
+ });
192
+ await this.cacheManager.set(path, html, this.config.cache.ttl);
193
+ this.logger.info(`✓ Pre-rendered and cached: ${path}`);
194
+ }
195
+ catch (error) {
196
+ this.logger.error(`✗ Failed to pre-render ${path}:`, error);
197
+ }
198
+ }
199
+ this.logger.info('Pre-rendering complete!');
200
+ }
201
+ catch (error) {
202
+ this.logger.error('Pre-rendering failed:', error);
203
+ }
204
+ finally {
205
+ this.isPrerendering = false;
206
+ }
207
+ }
208
+ async start() {
209
+ try {
210
+ await this.server.listen({ port: this.config.port, host: '0.0.0.0' });
211
+ this.logger.info(`SPA SSR Server running on port ${this.config.port}`);
212
+ // Pre-render paths after server starts
213
+ const hasPrerenderConfig = this.config.prerender && ((this.config.prerender.paths && this.config.prerender.paths.length > 0) ||
214
+ (this.config.prerender.sitemaps && this.config.prerender.sitemaps.length > 0));
215
+ const hasLegacyConfig = this.config.prerenderPaths && this.config.prerenderPaths.length > 0;
216
+ if (hasPrerenderConfig || hasLegacyConfig) {
217
+ // Give the server a moment to be fully ready
218
+ setTimeout(() => {
219
+ this.prerenderPaths().catch((err) => {
220
+ this.logger.error('Pre-rendering error:', err);
221
+ });
222
+ }, 1000);
223
+ }
224
+ }
225
+ catch (err) {
226
+ this.logger.error('Failed to start server:', err);
227
+ throw err;
228
+ }
229
+ }
230
+ async stop() {
231
+ try {
232
+ await this.renderer.cleanup();
233
+ await this.server.close();
234
+ this.logger.info('Server stopped gracefully');
235
+ }
236
+ catch (err) {
237
+ this.logger.error('Error stopping server:', err);
238
+ throw err;
239
+ }
240
+ }
241
+ getServer() {
242
+ return this.server;
243
+ }
244
+ }
245
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ import { FastifyRequest, FastifyReply } from 'fastify';
2
+ export interface Request extends FastifyRequest {
3
+ }
4
+ export interface Response extends FastifyReply {
5
+ }
6
+ export interface BotInfo {
7
+ name: string;
8
+ type: 'search' | 'social' | 'ai' | 'other';
9
+ verified: boolean;
10
+ parsedUA: UAParser.IResult;
11
+ }
12
+ export interface BotDetector {
13
+ isBot(userAgent: string): boolean;
14
+ addBotPattern(pattern: RegExp): void;
15
+ getBotInfo(userAgent: string): BotInfo | null;
16
+ parseUserAgent(userAgent: string): UAParser.IResult;
17
+ }
18
+ export interface RequestRouter {
19
+ handleRequest(req: Request, res: Response): Promise<void>;
20
+ isFileRequest(path: string): boolean;
21
+ fileExists(path: string): boolean;
22
+ }
23
+ export interface FileServer {
24
+ serveFile(filePath: string, res: Response): Promise<void>;
25
+ getMimeType(extension: string): string;
26
+ setSecurityHeaders(res: Response): void;
27
+ }
28
+ export interface RenderOptions {
29
+ timeout?: number;
30
+ waitForSelector?: string;
31
+ waitForNetworkIdle?: boolean;
32
+ viewport?: {
33
+ width: number;
34
+ height: number;
35
+ };
36
+ }
37
+ export interface SSRRenderer {
38
+ render(url: string, options?: RenderOptions): Promise<string>;
39
+ initialize(): Promise<void>;
40
+ cleanup(): Promise<void>;
41
+ }
42
+ export interface CacheStats {
43
+ hits: number;
44
+ misses: number;
45
+ size: number;
46
+ hitRate: number;
47
+ }
48
+ export interface CacheManager {
49
+ get(key: string): Promise<string | null>;
50
+ set(key: string, value: string, ttl?: number): Promise<void>;
51
+ delete(key: string): Promise<void>;
52
+ clear(): Promise<void>;
53
+ getStats(): CacheStats;
54
+ }
55
+ export interface RequestContext {
56
+ path: string;
57
+ userAgent: string;
58
+ isBot: boolean;
59
+ botInfo?: BotInfo;
60
+ isFileRequest: boolean;
61
+ timestamp: Date;
62
+ }
63
+ export interface CacheEntry {
64
+ key: string;
65
+ content: string;
66
+ createdAt: Date;
67
+ expiresAt: Date;
68
+ hitCount: number;
69
+ size: number;
70
+ }
71
+ export interface PrerenderConfig {
72
+ paths?: string[];
73
+ sitemaps?: string[];
74
+ exclude?: string[];
75
+ }
76
+ export interface ServerConfig {
77
+ port: number;
78
+ staticDir: string;
79
+ spaEntryPoint: string;
80
+ prerender?: PrerenderConfig;
81
+ prerenderPaths?: string[];
82
+ cache: {
83
+ type: 'memory' | 'redis';
84
+ ttl: number;
85
+ maxSize: number;
86
+ redisUrl?: string;
87
+ };
88
+ renderer: {
89
+ timeout: number;
90
+ viewport: {
91
+ width: number;
92
+ height: number;
93
+ };
94
+ waitForNetworkIdle: boolean;
95
+ };
96
+ botDetection: {
97
+ customPatterns: RegExp[];
98
+ enableVerification: boolean;
99
+ };
100
+ }
101
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { type Logger, ConsoleLogger } from './logger.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ConsoleLogger } from './logger.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface Logger {
2
+ info(message: string, ...args: any[]): void;
3
+ warn(message: string, ...args: any[]): void;
4
+ error(message: string, ...args: any[]): void;
5
+ debug(message: string, ...args: any[]): void;
6
+ }
7
+ export declare class ConsoleLogger implements Logger {
8
+ info(message: string, ...args: any[]): void;
9
+ warn(message: string, ...args: any[]): void;
10
+ error(message: string, ...args: any[]): void;
11
+ debug(message: string, ...args: any[]): void;
12
+ }
13
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC7C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CAC9C;AAED,qBAAa,aAAc,YAAW,MAAM;IAC1C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAI3C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAI3C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAI5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;CAK7C"}
@@ -0,0 +1,17 @@
1
+ export class ConsoleLogger {
2
+ info(message, ...args) {
3
+ console.log(`[INFO] ${message}`, ...args);
4
+ }
5
+ warn(message, ...args) {
6
+ console.warn(`[WARN] ${message}`, ...args);
7
+ }
8
+ error(message, ...args) {
9
+ console.error(`[ERROR] ${message}`, ...args);
10
+ }
11
+ debug(message, ...args) {
12
+ if (process.env.NODE_ENV === 'development') {
13
+ console.debug(`[DEBUG] ${message}`, ...args);
14
+ }
15
+ }
16
+ }
17
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,aAAa;IACxB,IAAI,CAAC,OAAe,EAAE,GAAG,IAAW;QAClC,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAG,IAAW;QAClC,OAAO,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW;QACnC,OAAO,CAAC,KAAK,CAAC,WAAW,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAG,IAAW;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;YAC3C,OAAO,CAAC,KAAK,CAAC,WAAW,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@singhey/spa-ssr-renderer",
3
+ "version": "1.0.0",
4
+ "description": "A Node.js server that intelligently serves SPAs with server-side rendering for bots and crawlers",
5
+ "main": "dist/server.js",
6
+ "types": "dist/server.d.ts",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/server.js",
11
+ "types": "./dist/server.d.ts"
12
+ }
13
+ },
14
+ "bin": {
15
+ "spa-ssr-renderer": "./dist/index.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "start": "node dist/index.js",
25
+ "dev": "tsx src/index.ts",
26
+ "watch": "tsx watch src/index.ts",
27
+ "test": "jest",
28
+ "test:watch": "jest --watch",
29
+ "test:coverage": "jest --coverage",
30
+ "prepublishOnly": "pnpm run build"
31
+ },
32
+ "keywords": [
33
+ "spa",
34
+ "ssr",
35
+ "server-side-rendering",
36
+ "seo",
37
+ "bot-detection",
38
+ "prerendering",
39
+ "playwright",
40
+ "fastify",
41
+ "cache",
42
+ "sitemap",
43
+ "typescript",
44
+ "nodejs"
45
+ ],
46
+ "author": "singhey",
47
+ "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/singhey/spa-ssr-renderer.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/singhey/spa-ssr-renderer/issues"
54
+ },
55
+ "homepage": "https://github.com/singhey/spa-ssr-renderer#readme",
56
+ "devDependencies": {
57
+ "@types/node": "^20.0.0",
58
+ "ts-node": "^10.9.0",
59
+ "tsx": "^4.21.0",
60
+ "typescript": "^5.0.0"
61
+ },
62
+ "dependencies": {
63
+ "@fastify/static": "^9.0.0",
64
+ "@isaacs/ttlcache": "^2.1.4",
65
+ "@types/jest": "^30.0.0",
66
+ "@types/ua-parser-js": "^0.7.39",
67
+ "dotenv": "^17.2.3",
68
+ "fast-check": "^4.5.3",
69
+ "fastify": "^5.7.1",
70
+ "jest": "^30.2.0",
71
+ "pino-pretty": "^13.1.3",
72
+ "playwright": "^1.57.0",
73
+ "sitemapper": "^4.0.2",
74
+ "ts-jest": "^29.4.6",
75
+ "ua-parser-js": "^2.0.8"
76
+ },
77
+ "engines": {
78
+ "node": ">=18.0.0"
79
+ }
80
+ }