@live-change/server 0.9.121 → 0.9.123
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.
- package/lib/RenderContext.js +144 -0
- package/lib/Renderer.js +176 -61
- package/lib/SsrServer.js +2 -1
- package/package.json +7 -7
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import vm from 'vm'
|
|
2
|
+
|
|
3
|
+
class RenderContext {
|
|
4
|
+
constructor(settings, baseContext, script) {
|
|
5
|
+
this.settings = settings
|
|
6
|
+
this.root = this.settings.root || process.cwd()
|
|
7
|
+
this.timeouts = new Set()
|
|
8
|
+
this.intervals = new Set()
|
|
9
|
+
this.running = false
|
|
10
|
+
this.script = script
|
|
11
|
+
this.useCount = 0
|
|
12
|
+
this.maxUses = this.settings.contextMaxUses || 128
|
|
13
|
+
|
|
14
|
+
// Create wrapped setTimeout and setInterval that track IDs
|
|
15
|
+
const originalSetTimeout = setTimeout
|
|
16
|
+
const originalSetInterval = setInterval
|
|
17
|
+
const originalClearTimeout = clearTimeout
|
|
18
|
+
const originalClearInterval = clearInterval
|
|
19
|
+
const originalNextTick = process.nextTick
|
|
20
|
+
|
|
21
|
+
const wrappedSetTimeout = (callback, delay, ...args) => {
|
|
22
|
+
if(!this.running) return
|
|
23
|
+
const id = originalSetTimeout(callback, delay, ...args)
|
|
24
|
+
this.timeouts.add(id)
|
|
25
|
+
return id
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const wrappedSetInterval = (callback, delay, ...args) => {
|
|
29
|
+
if(!this.running) return
|
|
30
|
+
const id = originalSetInterval(callback, delay, ...args)
|
|
31
|
+
this.intervals.add(id)
|
|
32
|
+
return id
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const wrappedClearTimeout = (id) => {
|
|
36
|
+
if(!this.running) return
|
|
37
|
+
this.timeouts.delete(id)
|
|
38
|
+
return originalClearTimeout(id)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const wrappedClearInterval = (id) => {
|
|
42
|
+
if(!this.running) return
|
|
43
|
+
this.intervals.delete(id)
|
|
44
|
+
return originalClearInterval(id)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const wrappedNextTick = (callback, ...args) => {
|
|
48
|
+
if (!this.running) return
|
|
49
|
+
return originalNextTick(callback, ...args)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.contextObject = {
|
|
53
|
+
//...globalThis,
|
|
54
|
+
...baseContext,
|
|
55
|
+
exports: {},
|
|
56
|
+
setTimeout: wrappedSetTimeout,
|
|
57
|
+
setInterval: wrappedSetInterval,
|
|
58
|
+
clearTimeout: wrappedClearTimeout,
|
|
59
|
+
clearInterval: wrappedClearInterval,
|
|
60
|
+
//process: process,
|
|
61
|
+
process: {
|
|
62
|
+
env: {
|
|
63
|
+
NODE_ENV: 'production'
|
|
64
|
+
},
|
|
65
|
+
stdout: process.stdout,
|
|
66
|
+
stderr: process.stderr,
|
|
67
|
+
nextTick: wrappedNextTick
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.vmContext = vm.createContext(this.contextObject, {
|
|
72
|
+
name: 'SSR '+(new Date().toISOString()),
|
|
73
|
+
///microtaskMode: 'afterEvaluate'
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Start context and execute script immediately
|
|
77
|
+
this.start()
|
|
78
|
+
this.script.runInContext(this.vmContext)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
runScript(script) {
|
|
82
|
+
// Execute script in the existing context
|
|
83
|
+
// Note: this doesn't reset exports, so previous state is preserved
|
|
84
|
+
script.runInContext(this.vmContext)
|
|
85
|
+
return this.contextObject
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Increment use count and check if context should be replaced
|
|
89
|
+
incrementUse() {
|
|
90
|
+
this.useCount++
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
shouldReplace() {
|
|
94
|
+
return this.useCount >= this.maxUses
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getExports() {
|
|
98
|
+
return this.contextObject.exports
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Start the context - allow nextTick operations
|
|
102
|
+
start() {
|
|
103
|
+
this.running = true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Stop the context - prevent nextTick operations and clear timers
|
|
107
|
+
stop() {
|
|
108
|
+
this.running = false
|
|
109
|
+
this.clearTimeouts()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
isRunning() {
|
|
113
|
+
return this.running
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Clear all active timeouts and intervals
|
|
117
|
+
clearTimeouts() {
|
|
118
|
+
// Clear all timeouts
|
|
119
|
+
for (const timeoutId of this.timeouts) {
|
|
120
|
+
clearTimeout(timeoutId)
|
|
121
|
+
}
|
|
122
|
+
this.timeouts.clear()
|
|
123
|
+
|
|
124
|
+
// Clear all intervals
|
|
125
|
+
for (const intervalId of this.intervals) {
|
|
126
|
+
clearInterval(intervalId)
|
|
127
|
+
}
|
|
128
|
+
this.intervals.clear()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Get stats about active timers (useful for debugging)
|
|
132
|
+
getTimerStats() {
|
|
133
|
+
return {
|
|
134
|
+
running: this.running,
|
|
135
|
+
timeouts: this.timeouts.size,
|
|
136
|
+
intervals: this.intervals.size,
|
|
137
|
+
useCount: this.useCount,
|
|
138
|
+
maxUses: this.maxUses,
|
|
139
|
+
usagePercentage: Math.round((this.useCount / this.maxUses) * 100)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export default RenderContext
|
package/lib/Renderer.js
CHANGED
|
@@ -6,12 +6,17 @@ import renderTemplate from './renderTemplate.js'
|
|
|
6
6
|
import { SitemapStream } from 'sitemap'
|
|
7
7
|
import vm from 'vm'
|
|
8
8
|
import { createRequire } from 'module'
|
|
9
|
+
import RenderContext from './RenderContext.js'
|
|
9
10
|
|
|
10
11
|
class Renderer {
|
|
11
12
|
constructor(manifest, settings) {
|
|
12
13
|
this.manifest = manifest
|
|
13
14
|
this.settings = settings
|
|
14
15
|
this.root = this.settings.root || process.cwd()
|
|
16
|
+
this.contextPool = []
|
|
17
|
+
this.waitingQueue = []
|
|
18
|
+
this.poolSize = this.settings.contextPoolSize || 2
|
|
19
|
+
this.maxQueueSize = this.settings.maxQueueSize || 20 // 10x pool size seems reasonable
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
async start() {
|
|
@@ -29,11 +34,94 @@ class Renderer {
|
|
|
29
34
|
require: createRequire(serverEntryPath),
|
|
30
35
|
__dirname: path.dirname(serverEntryPath),
|
|
31
36
|
}
|
|
37
|
+
|
|
38
|
+
// Create pool of render contexts
|
|
39
|
+
for (let i = 0; i < this.poolSize; i++) {
|
|
40
|
+
const context = new RenderContext(this.settings, this.baseContext, this.script)
|
|
41
|
+
this.contextPool.push(context)
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
const templatePath = path.resolve(this.root, this.settings.templatePath ?? './dist/client/index.html')
|
|
33
45
|
this.template = await fs.promises.readFile(templatePath, { encoding: 'utf-8' })
|
|
34
46
|
}
|
|
35
47
|
}
|
|
36
48
|
|
|
49
|
+
// Get a context from pool, wait if none available
|
|
50
|
+
async getContext() {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
if (this.contextPool.length > 0) {
|
|
53
|
+
// Context available immediately
|
|
54
|
+
const context = this.contextPool.pop()
|
|
55
|
+
resolve(context)
|
|
56
|
+
} else {
|
|
57
|
+
// Check queue limit before adding to waiting queue
|
|
58
|
+
if (this.waitingQueue.length >= this.maxQueueSize) {
|
|
59
|
+
reject(new Error(`Render queue is full (${this.maxQueueSize} waiting requests). Server overloaded.`))
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
// No context available, add to waiting queue
|
|
63
|
+
this.waitingQueue.push(resolve)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Release context back to pool and serve waiting requests
|
|
69
|
+
releaseContext(context) {
|
|
70
|
+
if (this.waitingQueue.length > 0) {
|
|
71
|
+
// Someone is waiting, give context directly to them
|
|
72
|
+
const resolve = this.waitingQueue.shift()
|
|
73
|
+
resolve(context)
|
|
74
|
+
} else {
|
|
75
|
+
// No one waiting, return to pool
|
|
76
|
+
this.contextPool.push(context)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Higher-order function for context management with timeout
|
|
81
|
+
async withContext(callback) {
|
|
82
|
+
if (this.settings.dev) {
|
|
83
|
+
// In dev mode, no context needed
|
|
84
|
+
return await callback(null)
|
|
85
|
+
} else {
|
|
86
|
+
const context = await this.getContext()
|
|
87
|
+
const timeout = this.settings.renderTimeout || 10000 // 10 seconds default
|
|
88
|
+
let isTimedOut = false
|
|
89
|
+
let shouldReplaceContext = false
|
|
90
|
+
|
|
91
|
+
// Increment usage counter
|
|
92
|
+
context.incrementUse()
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// Race between callback and timeout
|
|
96
|
+
const result = await Promise.race([
|
|
97
|
+
callback(context),
|
|
98
|
+
new Promise((_, reject) => {
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
isTimedOut = true
|
|
101
|
+
reject(new Error(`Render timeout after ${timeout}ms`))
|
|
102
|
+
}, timeout)
|
|
103
|
+
})
|
|
104
|
+
])
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
} catch (error) {
|
|
108
|
+
// Mark context for replacement on any error
|
|
109
|
+
shouldReplaceContext = true
|
|
110
|
+
throw error
|
|
111
|
+
} finally {
|
|
112
|
+
if (isTimedOut || shouldReplaceContext || context.shouldReplace()) {
|
|
113
|
+
// Stop the context and create a replacement
|
|
114
|
+
context.stop()
|
|
115
|
+
const newContext = new RenderContext(this.settings, this.baseContext, this.script)
|
|
116
|
+
this.releaseContext(newContext)
|
|
117
|
+
} else {
|
|
118
|
+
// Normal release back to pool
|
|
119
|
+
this.releaseContext(context)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
37
125
|
async setupVite() {
|
|
38
126
|
this.vite = await vite.createServer({
|
|
39
127
|
root: this.root,
|
|
@@ -54,38 +142,46 @@ class Renderer {
|
|
|
54
142
|
|
|
55
143
|
async renderPage(params) {
|
|
56
144
|
const { url, headers, dao, clientIp, credentials, windowId, version, now, domain } = params
|
|
145
|
+
const startTime = Date.now()
|
|
57
146
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
: ` window.__CREDENTIALS__= ${serialize(credentials, { isJSON: true })}\n`)+
|
|
69
|
-
` window.__VERSION__ = ${serialize(version, { isJSON: true })}\n`+
|
|
70
|
-
` window.__WINDOW_ID__ = ${serialize(windowId, { isJSON: true })}\n`+
|
|
71
|
-
` window.__NOW__ = ${serialize(now, { isJSON: true })}\n`+
|
|
72
|
-
` console.info("SOFTWARE VERSION:" + window.__VERSION__)\n`+
|
|
73
|
-
`</script>\n`
|
|
74
|
-
|
|
75
|
-
const template = await this.prepareTemplate(url)
|
|
76
|
-
|
|
77
|
-
const html = renderTemplate(template, {
|
|
78
|
-
'<html>': (meta.htmlAttrs ? `<html ${meta.htmlAttrs}>` : '<html>'),
|
|
79
|
-
'<head>': (meta.headAttrs ? `<head ${meta.headAttrs}>` : '<head>'),
|
|
80
|
-
'<!--head-->': (meta.headTags || '') + '\n' + preloadLinks,
|
|
81
|
-
'<body>': (meta.bodyAttrs ? `<body ${meta.bodyAttrs}>` : '<body>') + '\n' + (meta.bodyPrepend || ''),
|
|
82
|
-
'<!--body-tags-open-->': meta.bodyTagsOpen || '',
|
|
83
|
-
'<!--body-tags-->': meta.bodyTags || '',
|
|
84
|
-
'<!--app-html-->': appHtml,
|
|
85
|
-
'<!--app-data-->': appDataScript
|
|
86
|
-
})
|
|
147
|
+
try {
|
|
148
|
+
// Use withContext only for the actual rendering part
|
|
149
|
+
const { html: appHtml, modules, data, meta, response } = await this.withContext(async (context) => {
|
|
150
|
+
const render = await this.getRenderFunction(context)
|
|
151
|
+
return await render(params)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
//console.log("META:", meta)
|
|
155
|
+
|
|
156
|
+
const preloadLinks = this.renderPreloadLinks(modules)
|
|
87
157
|
|
|
88
|
-
|
|
158
|
+
const appDataScript = ` <script>` +
|
|
159
|
+
` window.__DAO_CACHE__= ${serialize(data, { isJSON: true })}\n`+
|
|
160
|
+
(this.settings.fastAuth ? ''
|
|
161
|
+
: ` window.__CREDENTIALS__= ${serialize(credentials, { isJSON: true })}\n`)+
|
|
162
|
+
` window.__VERSION__ = ${serialize(version, { isJSON: true })}\n`+
|
|
163
|
+
` window.__WINDOW_ID__ = ${serialize(windowId, { isJSON: true })}\n`+
|
|
164
|
+
` window.__NOW__ = ${serialize(now, { isJSON: true })}\n`+
|
|
165
|
+
` console.info("SOFTWARE VERSION:" + window.__VERSION__)\n`+
|
|
166
|
+
`</script>\n`
|
|
167
|
+
|
|
168
|
+
const template = await this.prepareTemplate(url)
|
|
169
|
+
|
|
170
|
+
const html = renderTemplate(template, {
|
|
171
|
+
'<html>': (meta.htmlAttrs ? `<html ${meta.htmlAttrs}>` : '<html>'),
|
|
172
|
+
'<head>': (meta.headAttrs ? `<head ${meta.headAttrs}>` : '<head>'),
|
|
173
|
+
'<!--head-->': (meta.headTags || '') + '\n' + preloadLinks,
|
|
174
|
+
'<body>': (meta.bodyAttrs ? `<body ${meta.bodyAttrs}>` : '<body>') + '\n' + (meta.bodyPrepend || ''),
|
|
175
|
+
'<!--body-tags-open-->': meta.bodyTagsOpen || '',
|
|
176
|
+
'<!--body-tags-->': meta.bodyTags || '',
|
|
177
|
+
'<!--app-html-->': appHtml,
|
|
178
|
+
'<!--app-data-->': appDataScript
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
return { html, response }
|
|
182
|
+
} finally {
|
|
183
|
+
this.logRenderStats(url, startTime)
|
|
184
|
+
}
|
|
89
185
|
}
|
|
90
186
|
|
|
91
187
|
renderPreloadLink(file) {
|
|
@@ -127,61 +223,42 @@ class Renderer {
|
|
|
127
223
|
return template
|
|
128
224
|
}
|
|
129
225
|
|
|
130
|
-
async
|
|
131
|
-
const contextObject = {
|
|
132
|
-
//...globalThis,
|
|
133
|
-
...this.baseContext,
|
|
134
|
-
exports: {},
|
|
135
|
-
//process: process,
|
|
136
|
-
process: {
|
|
137
|
-
env: {
|
|
138
|
-
NODE_ENV: 'production'
|
|
139
|
-
},
|
|
140
|
-
stdout: process.stdout,
|
|
141
|
-
stderr: process.stderr,
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const requestContext = vm.createContext(contextObject, {
|
|
145
|
-
name: 'SSR Render '+(new Date().toISOString()),
|
|
146
|
-
///microtaskMode: 'afterEvaluate'
|
|
147
|
-
})
|
|
148
|
-
this.script.runInContext(requestContext)
|
|
149
|
-
return contextObject
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async getRenderFunction() {
|
|
226
|
+
async getRenderFunction(context = null) {
|
|
153
227
|
if(this.settings.dev) {
|
|
154
228
|
/// Reload every request
|
|
155
229
|
const entryPath = path.resolve(this.root, this.settings.serverEntry || 'src/entry-server.js')
|
|
156
230
|
return (await this.vite.ssrLoadModule(entryPath)).render
|
|
157
231
|
} else {
|
|
158
|
-
|
|
159
|
-
return context.exports.render
|
|
232
|
+
return context.getExports().render
|
|
160
233
|
}
|
|
161
234
|
}
|
|
162
235
|
|
|
163
|
-
async getSitemapRenderFunction() {
|
|
236
|
+
async getSitemapRenderFunction(context = null) {
|
|
164
237
|
if(this.settings.dev) {
|
|
165
238
|
/// Reload every request
|
|
166
239
|
const entryPath = path.resolve(this.root, this.settings.serverEntry || 'src/entry-server.js')
|
|
167
240
|
return (await this.vite.ssrLoadModule(entryPath)).sitemap
|
|
168
241
|
} else {
|
|
169
|
-
|
|
170
|
-
return context.exports.sitemap
|
|
242
|
+
return context.getExports().sitemap
|
|
171
243
|
}
|
|
172
244
|
}
|
|
173
245
|
|
|
174
246
|
async renderSitemap(params, res) {
|
|
175
|
-
|
|
176
|
-
|
|
247
|
+
const { url, headers, dao, clientIp, credentials, windowId, version, now, domain } = params
|
|
248
|
+
const startTime = Date.now()
|
|
177
249
|
|
|
250
|
+
try {
|
|
178
251
|
res.header('Content-Type', 'application/xml')
|
|
179
252
|
res.status(200)
|
|
180
253
|
const smStream = new SitemapStream({
|
|
181
254
|
hostname: (process.env.BASE_HREF ?? (domain ? `https://${domain}` : "https://sitemap.com"))+'/'
|
|
182
255
|
})
|
|
183
256
|
smStream.pipe(res)
|
|
184
|
-
|
|
257
|
+
|
|
258
|
+
// Use withContext only for the actual sitemap function execution
|
|
259
|
+
const sitemapFunction = await this.withContext(async (context) => {
|
|
260
|
+
return await this.getSitemapRenderFunction(context)
|
|
261
|
+
})
|
|
185
262
|
|
|
186
263
|
function write(routeInfo) {
|
|
187
264
|
smStream.write(routeInfo)
|
|
@@ -195,6 +272,8 @@ class Renderer {
|
|
|
195
272
|
res.status(503)
|
|
196
273
|
res.end(`<h4>Internal server error</h4><pre>${err.stack || err.code || err}</pre>`)
|
|
197
274
|
//if(profileOp) await profileLog.end({ ...profileOp, state: 'error', error: err })
|
|
275
|
+
} finally {
|
|
276
|
+
this.logRenderStats(`${url} (sitemap)`, startTime)
|
|
198
277
|
}
|
|
199
278
|
}
|
|
200
279
|
|
|
@@ -206,11 +285,47 @@ class Renderer {
|
|
|
206
285
|
}
|
|
207
286
|
}
|
|
208
287
|
|
|
288
|
+
// Log render statistics in compact format
|
|
289
|
+
logRenderStats(url, startTime) {
|
|
290
|
+
if (this.settings.dev) return // Skip logging in dev mode
|
|
291
|
+
|
|
292
|
+
const renderTime = Date.now() - startTime
|
|
293
|
+
const stats = this.getPoolStats()
|
|
294
|
+
|
|
295
|
+
// Get context usage info if available
|
|
296
|
+
let contextInfo = ''
|
|
297
|
+
for(const context of this.contextPool) {
|
|
298
|
+
const contextStats = context.getTimerStats()
|
|
299
|
+
contextInfo += `ctx:${contextStats.useCount}/${contextStats.maxUses} `
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Compact one-line format: URL | time | pool status | queue status | context usage
|
|
303
|
+
console.log(`RENDER ${url} | ${renderTime}ms | pool:${stats.availableContexts}/${stats.poolSize} | queue:${stats.waitingRequests}/${stats.maxQueueSize} | ${contextInfo}`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Get pool statistics for monitoring
|
|
307
|
+
getPoolStats() {
|
|
308
|
+
return {
|
|
309
|
+
poolSize: this.poolSize,
|
|
310
|
+
maxQueueSize: this.maxQueueSize,
|
|
311
|
+
availableContexts: this.contextPool.length,
|
|
312
|
+
waitingRequests: this.waitingQueue.length,
|
|
313
|
+
activeContexts: this.poolSize - this.contextPool.length,
|
|
314
|
+
queueUtilization: Math.round((this.waitingQueue.length / this.maxQueueSize) * 100)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
209
318
|
async close() {
|
|
210
319
|
if(this.vite) {
|
|
211
320
|
console.log("VITE CLOSE!!!")
|
|
212
321
|
await this.vite.close()
|
|
213
322
|
}
|
|
323
|
+
// Clear any pending timeouts/intervals from all contexts in pool
|
|
324
|
+
for (const context of this.contextPool) {
|
|
325
|
+
context.clearTimeouts()
|
|
326
|
+
}
|
|
327
|
+
// Clear waiting queue
|
|
328
|
+
this.waitingQueue = []
|
|
214
329
|
}
|
|
215
330
|
|
|
216
331
|
}
|
package/lib/SsrServer.js
CHANGED
|
@@ -101,7 +101,7 @@ class SsrServer {
|
|
|
101
101
|
}
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
this.express.get('/sitemap.xml', async (req, res) => {
|
|
104
|
+
this.express.get('/sitemap.xml', async (req, res) => {
|
|
105
105
|
if(this.settings.spa) {
|
|
106
106
|
res.status(404).end()
|
|
107
107
|
return
|
|
@@ -132,6 +132,7 @@ class SsrServer {
|
|
|
132
132
|
}
|
|
133
133
|
})
|
|
134
134
|
this.express.use('*', async (req, res) => {
|
|
135
|
+
console.log("RENDERING PAGE", req.originalUrl)
|
|
135
136
|
if(fbRedirect(req, res)) return
|
|
136
137
|
if(this.settings.spa) {
|
|
137
138
|
if(this.settings.dev) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.123",
|
|
4
4
|
"description": "Live Change Framework - server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"type": "module",
|
|
23
23
|
"homepage": "https://github.com/live-change/live-change-stack",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@live-change/dao": "^0.9.
|
|
26
|
-
"@live-change/dao-sockjs": "^0.9.
|
|
27
|
-
"@live-change/db-server": "^0.9.
|
|
28
|
-
"@live-change/framework": "^0.9.
|
|
25
|
+
"@live-change/dao": "^0.9.123",
|
|
26
|
+
"@live-change/dao-sockjs": "^0.9.123",
|
|
27
|
+
"@live-change/db-server": "^0.9.123",
|
|
28
|
+
"@live-change/framework": "^0.9.123",
|
|
29
29
|
"@live-change/sockjs": "0.4.1",
|
|
30
|
-
"@live-change/uid": "^0.9.
|
|
30
|
+
"@live-change/uid": "^0.9.123",
|
|
31
31
|
"dotenv": "^17.2.1",
|
|
32
32
|
"express": "^4.18.2",
|
|
33
33
|
"express-static-gzip": "2.1.7",
|
|
@@ -39,5 +39,5 @@
|
|
|
39
39
|
"websocket": "^1.0.34",
|
|
40
40
|
"yargs": "^17.7.2"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "302f2ca92788cd4e3414fc9a5815263a3f063f3e"
|
|
43
43
|
}
|