veryfront 0.1.1144 → 0.1.1146
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/esm/deno.d.ts +1 -0
- package/esm/deno.js +2 -1
- package/esm/src/modules/react-loader/ssr-module-loader/concurrency/semaphore.d.ts +3 -1
- package/esm/src/modules/react-loader/ssr-module-loader/concurrency/semaphore.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/concurrency/semaphore.js +59 -20
- package/esm/src/rendering/renderer-concurrency.d.ts +16 -4
- package/esm/src/rendering/renderer-concurrency.d.ts.map +1 -1
- package/esm/src/rendering/renderer-concurrency.js +103 -11
- package/esm/src/rendering/renderer.d.ts +4 -0
- package/esm/src/rendering/renderer.d.ts.map +1 -1
- package/esm/src/rendering/renderer.js +123 -35
- package/esm/src/server/runtime-handler/project-middleware.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/project-middleware.js +6 -1
- package/esm/src/server/services/rendering/ssr.service.d.ts.map +1 -1
- package/esm/src/server/services/rendering/ssr.service.js +11 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +5 -5
package/esm/deno.d.ts
CHANGED
|
@@ -285,6 +285,7 @@ declare namespace _default {
|
|
|
285
285
|
"#std/testing.ts": string;
|
|
286
286
|
"#std/testing/bdd": string;
|
|
287
287
|
"#std/testing/bdd.ts": string;
|
|
288
|
+
"#std/testing/mock": string;
|
|
288
289
|
"#std/testing/time": string;
|
|
289
290
|
"#std/testing/time.ts": string;
|
|
290
291
|
"#std/expect": string;
|
package/esm/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1146",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"nodeModulesDir": "auto",
|
|
6
6
|
"minimumDependencyAge": {
|
|
@@ -345,6 +345,7 @@ export default {
|
|
|
345
345
|
"#std/testing.ts": "jsr:@std/testing@1.0.17",
|
|
346
346
|
"#std/testing/bdd": "jsr:@std/testing@1.0.17/bdd",
|
|
347
347
|
"#std/testing/bdd.ts": "jsr:@std/testing@1.0.17/bdd",
|
|
348
|
+
"#std/testing/mock": "jsr:@std/testing@1.0.17/mock",
|
|
348
349
|
"#std/testing/time": "jsr:@std/testing@1.0.17/time",
|
|
349
350
|
"#std/testing/time.ts": "jsr:@std/testing@1.0.17/time",
|
|
350
351
|
"#std/expect": "jsr:@std/expect@1.0.18",
|
|
@@ -5,7 +5,9 @@ export declare class Semaphore {
|
|
|
5
5
|
constructor(permits: number, options?: {
|
|
6
6
|
maxQueueSize?: number;
|
|
7
7
|
});
|
|
8
|
-
tryAcquire(timeoutMs?: number
|
|
8
|
+
tryAcquire(timeoutMs?: number, options?: {
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
}): Promise<boolean>;
|
|
9
11
|
release(): void;
|
|
10
12
|
get available(): number;
|
|
11
13
|
get waiting(): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semaphore.d.ts","sourceRoot":"","sources":["../../../../../../src/src/modules/react-loader/ssr-module-loader/concurrency/semaphore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"semaphore.d.ts","sourceRoot":"","sources":["../../../../../../src/src/modules/react-loader/ssr-module-loader/concurrency/semaphore.ts"],"names":[],"mappings":"AAYA,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,SAAS,CAAyB;gBAE9B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;IAKhE,UAAU,CACR,SAAS,SAA6B,EACtC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACrC,OAAO,CAAC,OAAO,CAAC;IAqEnB,OAAO,IAAI,IAAI;IAgBf,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF"}
|
|
@@ -8,39 +8,78 @@ export class Semaphore {
|
|
|
8
8
|
this.permits = permits;
|
|
9
9
|
this.maxQueueSize = options?.maxQueueSize ?? Infinity;
|
|
10
10
|
}
|
|
11
|
-
tryAcquire(timeoutMs = DEFAULT_ACQUIRE_TIMEOUT_MS) {
|
|
11
|
+
tryAcquire(timeoutMs = DEFAULT_ACQUIRE_TIMEOUT_MS, options = {}) {
|
|
12
|
+
const { signal } = options;
|
|
13
|
+
if (signal?.aborted) {
|
|
14
|
+
return Promise.reject(signal.reason ?? new DOMException("The operation was aborted", "AbortError"));
|
|
15
|
+
}
|
|
12
16
|
if (this.permits > 0) {
|
|
13
17
|
this.permits--;
|
|
14
18
|
return Promise.resolve(true);
|
|
15
19
|
}
|
|
20
|
+
if (timeoutMs <= 0) {
|
|
21
|
+
return Promise.resolve(false);
|
|
22
|
+
}
|
|
16
23
|
if (this.waitQueue.length >= this.maxQueueSize) {
|
|
17
24
|
return Promise.resolve(false);
|
|
18
25
|
}
|
|
19
|
-
return new Promise((resolve) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
clearTimeout(timeoutId);
|
|
26
|
-
resolve(true);
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const waiter = {
|
|
28
|
+
resolve,
|
|
29
|
+
reject,
|
|
30
|
+
settled: false,
|
|
31
|
+
signal,
|
|
27
32
|
};
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
return;
|
|
31
|
-
settled = true;
|
|
32
|
-
const index = this.waitQueue.findIndex((w) => w.resolve === onAcquire);
|
|
33
|
+
const removeWaiter = () => {
|
|
34
|
+
const index = this.waitQueue.indexOf(waiter);
|
|
33
35
|
if (index !== -1)
|
|
34
36
|
this.waitQueue.splice(index, 1);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
};
|
|
38
|
+
const cleanup = () => {
|
|
39
|
+
if (waiter.timeoutId !== undefined)
|
|
40
|
+
clearTimeout(waiter.timeoutId);
|
|
41
|
+
if (waiter.signal && waiter.onAbort) {
|
|
42
|
+
waiter.signal.removeEventListener("abort", waiter.onAbort);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
waiter.onAbort = () => {
|
|
46
|
+
if (waiter.settled)
|
|
47
|
+
return;
|
|
48
|
+
waiter.settled = true;
|
|
49
|
+
removeWaiter();
|
|
50
|
+
cleanup();
|
|
51
|
+
reject(signal?.reason ?? new DOMException("The operation was aborted", "AbortError"));
|
|
52
|
+
};
|
|
53
|
+
signal?.addEventListener("abort", waiter.onAbort, { once: true });
|
|
54
|
+
if (signal?.aborted) {
|
|
55
|
+
waiter.onAbort();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (Number.isFinite(timeoutMs)) {
|
|
59
|
+
waiter.timeoutId = setTimeout(() => {
|
|
60
|
+
if (waiter.settled)
|
|
61
|
+
return;
|
|
62
|
+
waiter.settled = true;
|
|
63
|
+
removeWaiter();
|
|
64
|
+
cleanup();
|
|
65
|
+
resolve(false);
|
|
66
|
+
}, timeoutMs);
|
|
67
|
+
}
|
|
68
|
+
this.waitQueue.push(waiter);
|
|
38
69
|
});
|
|
39
70
|
}
|
|
40
71
|
release() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
next.
|
|
72
|
+
let next;
|
|
73
|
+
while ((next = this.waitQueue.shift())) {
|
|
74
|
+
if (next.settled)
|
|
75
|
+
continue;
|
|
76
|
+
next.settled = true;
|
|
77
|
+
if (next.timeoutId !== undefined)
|
|
78
|
+
clearTimeout(next.timeoutId);
|
|
79
|
+
if (next.signal && next.onAbort) {
|
|
80
|
+
next.signal.removeEventListener("abort", next.onAbort);
|
|
81
|
+
}
|
|
82
|
+
next.resolve(true);
|
|
44
83
|
return;
|
|
45
84
|
}
|
|
46
85
|
this.permits++;
|
|
@@ -22,9 +22,16 @@ export declare const RENDER_MAX_CONCURRENT: number;
|
|
|
22
22
|
* Configurable via RENDER_PER_PROJECT_LIMIT env var.
|
|
23
23
|
*/
|
|
24
24
|
export declare const RENDER_PER_PROJECT_LIMIT: number;
|
|
25
|
+
/**
|
|
26
|
+
* Maximum queued foreground renders per project.
|
|
27
|
+
* Defaults to one additional per-project concurrency window. Background work
|
|
28
|
+
* never enters this queue.
|
|
29
|
+
*/
|
|
30
|
+
export declare const RENDER_PER_PROJECT_QUEUE_SIZE: number;
|
|
25
31
|
/**
|
|
26
32
|
* Timeout for acquiring render permit (ms).
|
|
27
|
-
*
|
|
33
|
+
* This is the total foreground admission budget across project and global
|
|
34
|
+
* capacity gates. Background work does not wait.
|
|
28
35
|
*/
|
|
29
36
|
export declare const RENDER_ACQUIRE_TIMEOUT_MS = 5000;
|
|
30
37
|
/**
|
|
@@ -47,10 +54,15 @@ export declare const renderSemaphore: Semaphore;
|
|
|
47
54
|
*/
|
|
48
55
|
export declare const projectRenderCounts: Map<string, number>;
|
|
49
56
|
/**
|
|
50
|
-
* Attempt to acquire a project render slot with proper locking.
|
|
51
|
-
*
|
|
57
|
+
* Attempt to acquire a project render slot with proper locking. Foreground
|
|
58
|
+
* callers may opt into a bounded FIFO wait; background callers use the
|
|
59
|
+
* fail-fast default.
|
|
52
60
|
*/
|
|
53
|
-
export declare function acquireProjectSlot(projectId: string
|
|
61
|
+
export declare function acquireProjectSlot(projectId: string, options?: {
|
|
62
|
+
wait?: boolean;
|
|
63
|
+
timeoutMs?: number;
|
|
64
|
+
signal?: AbortSignal;
|
|
65
|
+
}): Promise<boolean>;
|
|
54
66
|
/**
|
|
55
67
|
* Release a project render slot with proper locking.
|
|
56
68
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer-concurrency.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/renderer-concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oEAAoE,CAAC;AAQ/F;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"renderer-concurrency.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/renderer-concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oEAAoE,CAAC;AAQ/F;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAA4C,CAAC;AAE/E;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,QAGpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,QAMzC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAS/C;;;;GAIG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,KAAK,CAA4C;IACzD,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IA+BhD,OAAO,CAAC,OAAO;IASf,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF;AAYD,eAAO,MAAM,eAAe,WAE1B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,qBAA4B,CAAC;AAyD7D;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACjB,GACL,OAAO,CAAC,OAAO,CAAC,CA0ElB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAqCf"}
|
|
@@ -19,18 +19,24 @@ import { RENDER_ERROR } from "../errors/index.js";
|
|
|
19
19
|
* Configurable via RENDER_MAX_CONCURRENT env var.
|
|
20
20
|
* Prevents one pod from being overwhelmed when multiple projects have issues.
|
|
21
21
|
*/
|
|
22
|
-
export const RENDER_MAX_CONCURRENT = getEnvNumber("RENDER_MAX_CONCURRENT"
|
|
22
|
+
export const RENDER_MAX_CONCURRENT = getEnvNumber("RENDER_MAX_CONCURRENT", 30);
|
|
23
23
|
/**
|
|
24
24
|
* Maximum concurrent renders per project (noisy-neighbor protection).
|
|
25
25
|
* Defaults to ceil(RENDER_MAX_CONCURRENT / 3) so no single project can consume
|
|
26
26
|
* more than ~1/3 of pod capacity. Set to 0 to disable per-project limits.
|
|
27
27
|
* Configurable via RENDER_PER_PROJECT_LIMIT env var.
|
|
28
28
|
*/
|
|
29
|
-
export const RENDER_PER_PROJECT_LIMIT = getEnvNumber("RENDER_PER_PROJECT_LIMIT")
|
|
30
|
-
|
|
29
|
+
export const RENDER_PER_PROJECT_LIMIT = getEnvNumber("RENDER_PER_PROJECT_LIMIT", Math.ceil(RENDER_MAX_CONCURRENT / 3));
|
|
30
|
+
/**
|
|
31
|
+
* Maximum queued foreground renders per project.
|
|
32
|
+
* Defaults to one additional per-project concurrency window. Background work
|
|
33
|
+
* never enters this queue.
|
|
34
|
+
*/
|
|
35
|
+
export const RENDER_PER_PROJECT_QUEUE_SIZE = Math.max(0, getEnvNumber("RENDER_PER_PROJECT_QUEUE_SIZE", Math.max(0, RENDER_PER_PROJECT_LIMIT)));
|
|
31
36
|
/**
|
|
32
37
|
* Timeout for acquiring render permit (ms).
|
|
33
|
-
*
|
|
38
|
+
* This is the total foreground admission budget across project and global
|
|
39
|
+
* capacity gates. Background work does not wait.
|
|
34
40
|
*/
|
|
35
41
|
export const RENDER_ACQUIRE_TIMEOUT_MS = 5_000;
|
|
36
42
|
/** Maximum time to wait for a project lock before giving up (10 seconds) */
|
|
@@ -111,6 +117,7 @@ export const projectRenderCounts = new Map();
|
|
|
111
117
|
* Each project gets its own mutex so different projects don't block each other.
|
|
112
118
|
*/
|
|
113
119
|
const projectMutexes = new Map();
|
|
120
|
+
const projectSlotWaitQueues = new Map();
|
|
114
121
|
function getProjectMutex(projectId) {
|
|
115
122
|
let mutex = projectMutexes.get(projectId);
|
|
116
123
|
if (!mutex) {
|
|
@@ -119,6 +126,21 @@ function getProjectMutex(projectId) {
|
|
|
119
126
|
}
|
|
120
127
|
return mutex;
|
|
121
128
|
}
|
|
129
|
+
function getProjectSlotWaitQueue(projectId) {
|
|
130
|
+
let queue = projectSlotWaitQueues.get(projectId);
|
|
131
|
+
if (!queue) {
|
|
132
|
+
queue = [];
|
|
133
|
+
projectSlotWaitQueues.set(projectId, queue);
|
|
134
|
+
}
|
|
135
|
+
return queue;
|
|
136
|
+
}
|
|
137
|
+
function cleanupProjectSlotWaiter(waiter) {
|
|
138
|
+
if (waiter.timeoutId !== undefined)
|
|
139
|
+
clearTimeout(waiter.timeoutId);
|
|
140
|
+
if (waiter.signal && waiter.onAbort) {
|
|
141
|
+
waiter.signal.removeEventListener("abort", waiter.onAbort);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
122
144
|
// ---------------------------------------------------------------------------
|
|
123
145
|
// Slot Functions
|
|
124
146
|
// ---------------------------------------------------------------------------
|
|
@@ -129,19 +151,74 @@ function acquireProjectLock(projectId) {
|
|
|
129
151
|
return getProjectMutex(projectId).acquire(LOCK_TIMEOUT_MS);
|
|
130
152
|
}
|
|
131
153
|
/**
|
|
132
|
-
* Attempt to acquire a project render slot with proper locking.
|
|
133
|
-
*
|
|
154
|
+
* Attempt to acquire a project render slot with proper locking. Foreground
|
|
155
|
+
* callers may opt into a bounded FIFO wait; background callers use the
|
|
156
|
+
* fail-fast default.
|
|
134
157
|
*/
|
|
135
|
-
export async function acquireProjectSlot(projectId) {
|
|
158
|
+
export async function acquireProjectSlot(projectId, options = {}) {
|
|
159
|
+
const { signal } = options;
|
|
160
|
+
if (signal?.aborted) {
|
|
161
|
+
throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
162
|
+
}
|
|
136
163
|
if (RENDER_PER_PROJECT_LIMIT <= 0)
|
|
137
164
|
return true;
|
|
138
165
|
const release = await acquireProjectLock(projectId);
|
|
139
166
|
try {
|
|
167
|
+
if (signal?.aborted) {
|
|
168
|
+
throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
169
|
+
}
|
|
140
170
|
const current = projectRenderCounts.get(projectId) ?? 0;
|
|
141
|
-
if (current
|
|
171
|
+
if (current < RENDER_PER_PROJECT_LIMIT) {
|
|
172
|
+
projectRenderCounts.set(projectId, current + 1);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (!options.wait || RENDER_PER_PROJECT_QUEUE_SIZE <= 0)
|
|
142
176
|
return false;
|
|
143
|
-
|
|
144
|
-
|
|
177
|
+
const timeoutMs = options.timeoutMs ?? RENDER_ACQUIRE_TIMEOUT_MS;
|
|
178
|
+
if (timeoutMs <= 0)
|
|
179
|
+
return false;
|
|
180
|
+
const queue = getProjectSlotWaitQueue(projectId);
|
|
181
|
+
if (queue.length >= RENDER_PER_PROJECT_QUEUE_SIZE)
|
|
182
|
+
return false;
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
const waiter = {
|
|
185
|
+
resolve,
|
|
186
|
+
reject,
|
|
187
|
+
settled: false,
|
|
188
|
+
signal,
|
|
189
|
+
};
|
|
190
|
+
const removeWaiter = () => {
|
|
191
|
+
const index = queue.indexOf(waiter);
|
|
192
|
+
if (index !== -1)
|
|
193
|
+
queue.splice(index, 1);
|
|
194
|
+
if (queue.length === 0 && projectSlotWaitQueues.get(projectId) === queue) {
|
|
195
|
+
projectSlotWaitQueues.delete(projectId);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const settleWithoutSlot = (reason) => {
|
|
199
|
+
if (waiter.settled)
|
|
200
|
+
return;
|
|
201
|
+
waiter.settled = true;
|
|
202
|
+
removeWaiter();
|
|
203
|
+
cleanupProjectSlotWaiter(waiter);
|
|
204
|
+
if (reason !== undefined) {
|
|
205
|
+
reject(reason);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
resolve(false);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
waiter.onAbort = () => settleWithoutSlot(signal?.reason ?? new DOMException("The operation was aborted", "AbortError"));
|
|
212
|
+
signal?.addEventListener("abort", waiter.onAbort, { once: true });
|
|
213
|
+
if (signal?.aborted) {
|
|
214
|
+
waiter.onAbort();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (Number.isFinite(timeoutMs)) {
|
|
218
|
+
waiter.timeoutId = setTimeout(() => settleWithoutSlot(), timeoutMs);
|
|
219
|
+
}
|
|
220
|
+
queue.push(waiter);
|
|
221
|
+
});
|
|
145
222
|
}
|
|
146
223
|
finally {
|
|
147
224
|
release();
|
|
@@ -158,9 +235,24 @@ export async function releaseProjectSlot(projectId) {
|
|
|
158
235
|
let deleteIdleMutex = false;
|
|
159
236
|
try {
|
|
160
237
|
const current = projectRenderCounts.get(projectId) ?? 0;
|
|
238
|
+
const queue = projectSlotWaitQueues.get(projectId);
|
|
239
|
+
let next;
|
|
240
|
+
while ((next = queue?.shift())) {
|
|
241
|
+
if (next.settled)
|
|
242
|
+
continue;
|
|
243
|
+
next.settled = true;
|
|
244
|
+
cleanupProjectSlotWaiter(next);
|
|
245
|
+
if (queue?.length === 0)
|
|
246
|
+
projectSlotWaitQueues.delete(projectId);
|
|
247
|
+
projectRenderCounts.set(projectId, Math.max(1, current));
|
|
248
|
+
next.resolve(true);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (queue?.length === 0)
|
|
252
|
+
projectSlotWaitQueues.delete(projectId);
|
|
161
253
|
if (current <= 1) {
|
|
162
254
|
projectRenderCounts.delete(projectId);
|
|
163
|
-
deleteIdleMutex = mutex.waiting === 0;
|
|
255
|
+
deleteIdleMutex = mutex.waiting === 0 && !projectSlotWaitQueues.has(projectId);
|
|
164
256
|
return;
|
|
165
257
|
}
|
|
166
258
|
projectRenderCounts.set(projectId, current - 1);
|
|
@@ -41,10 +41,13 @@ export declare class Renderer {
|
|
|
41
41
|
* Key format: {cachePrefix}:{slug}:{colorScheme}
|
|
42
42
|
*/
|
|
43
43
|
private renderFlight;
|
|
44
|
+
/** Lets foreground followers recover when a background leader fails fast at capacity. */
|
|
45
|
+
private renderFlightAdmissions;
|
|
44
46
|
private productionPrewarmContexts;
|
|
45
47
|
constructor(options?: RendererOptions);
|
|
46
48
|
initialize(options?: SharedServicesOptions): Promise<void>;
|
|
47
49
|
renderPage(slug: string, ctx: RenderContext, options?: RenderOptions): Promise<RenderResult>;
|
|
50
|
+
private renderPageWithAdmission;
|
|
48
51
|
private resolveReleaseAssetManifest;
|
|
49
52
|
private withManifestCachePrefix;
|
|
50
53
|
/**
|
|
@@ -69,6 +72,7 @@ export declare class Renderer {
|
|
|
69
72
|
private getProductionPrewarmKey;
|
|
70
73
|
private rememberProductionPrewarm;
|
|
71
74
|
private buildCanonicalPrewarmOptions;
|
|
75
|
+
private buildRoutePrewarmOptions;
|
|
72
76
|
private buildStaleRefreshOptions;
|
|
73
77
|
private scheduleProductionRenderRefresh;
|
|
74
78
|
private runProductionRenderPrewarm;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/renderer.ts"],"names":[],"mappings":"AA8CA,OAAO,EACL,mBAAmB,EACnB,+BAA+B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,aAAa,EACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,iCAAiC,CAAC;AAgBzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC7F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
import * as dntShim from "../../_dnt.shims.js";
|
|
33
33
|
import { rendererLogger } from "../utils/index.js";
|
|
34
34
|
import { MDXCacheAdapter } from "../transforms/mdx/index.js";
|
|
35
|
-
import { INITIALIZATION_ERROR, SERVICE_OVERLOADED } from "../errors/index.js";
|
|
35
|
+
import { INITIALIZATION_ERROR, SERVICE_OVERLOADED, VeryfrontError } from "../errors/index.js";
|
|
36
36
|
import { withSpan } from "../observability/tracing/otlp-setup.js";
|
|
37
37
|
import { buildQueryAwareCacheKey, buildRenderCachePrefix, } from "../cache/keys.js";
|
|
38
38
|
import { requestHasCacheSensitiveState } from "../cache/request-cacheability.js";
|
|
@@ -159,6 +159,8 @@ export class Renderer {
|
|
|
159
159
|
* Key format: {cachePrefix}:{slug}:{colorScheme}
|
|
160
160
|
*/
|
|
161
161
|
renderFlight = new Singleflight();
|
|
162
|
+
/** Lets foreground followers recover when a background leader fails fast at capacity. */
|
|
163
|
+
renderFlightAdmissions = new Map();
|
|
162
164
|
productionPrewarmContexts = new Map();
|
|
163
165
|
constructor(options = {}) {
|
|
164
166
|
this.cache = new ContextAwareCacheCoordinator(options.cache);
|
|
@@ -181,6 +183,9 @@ export class Renderer {
|
|
|
181
183
|
this.initializationPromise = null;
|
|
182
184
|
}
|
|
183
185
|
renderPage(slug, ctx, options) {
|
|
186
|
+
return this.renderPageWithAdmission(slug, ctx, options, "foreground");
|
|
187
|
+
}
|
|
188
|
+
renderPageWithAdmission(slug, ctx, options, admission) {
|
|
184
189
|
return withSpan("renderer.renderPage", async () => {
|
|
185
190
|
if (!this.initialized) {
|
|
186
191
|
throw INITIALIZATION_ERROR.create({
|
|
@@ -219,7 +224,7 @@ export class Renderer {
|
|
|
219
224
|
}
|
|
220
225
|
return cacheResult.cachedResult;
|
|
221
226
|
}
|
|
222
|
-
const result = await this.doRenderPage(slug, effectiveCtx, effectiveOptions, startTime, cacheKey, effectiveOptions.abortSignal ?? effectiveOptions.request?.signal);
|
|
227
|
+
const result = await this.doRenderPage(slug, effectiveCtx, effectiveOptions, startTime, cacheKey, effectiveOptions.abortSignal ?? effectiveOptions.request?.signal, admission);
|
|
223
228
|
this.scheduleProductionRenderPrewarm(slug, effectiveCtx, effectiveOptions, cacheKey);
|
|
224
229
|
return result;
|
|
225
230
|
}, {
|
|
@@ -332,6 +337,9 @@ export class Renderer {
|
|
|
332
337
|
this.productionPrewarmContexts.set(key, promise);
|
|
333
338
|
}
|
|
334
339
|
buildCanonicalPrewarmOptions(ctx, options) {
|
|
340
|
+
const sourceUrl = options?.url ??
|
|
341
|
+
(options?.request ? new URL(options.request.url) : undefined);
|
|
342
|
+
const canonicalUrl = sourceUrl ? new URL("/", sourceUrl) : undefined;
|
|
335
343
|
return {
|
|
336
344
|
environment: ctx.environment,
|
|
337
345
|
projectId: ctx.projectId,
|
|
@@ -341,6 +349,20 @@ export class Renderer {
|
|
|
341
349
|
releaseAssetManifest: options?.releaseAssetManifest ?? null,
|
|
342
350
|
noHmr: options?.noHmr,
|
|
343
351
|
forceProductionScripts: options?.forceProductionScripts,
|
|
352
|
+
url: canonicalUrl,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
buildRoutePrewarmOptions(slug, options) {
|
|
356
|
+
if (!options.url)
|
|
357
|
+
return options;
|
|
358
|
+
const url = new URL(normalizeComparableSlug(slug), options.url);
|
|
359
|
+
return {
|
|
360
|
+
...options,
|
|
361
|
+
url,
|
|
362
|
+
request: new Request(url, {
|
|
363
|
+
method: "GET",
|
|
364
|
+
headers: { accept: "text/html" },
|
|
365
|
+
}),
|
|
344
366
|
};
|
|
345
367
|
}
|
|
346
368
|
buildStaleRefreshOptions(ctx, options) {
|
|
@@ -369,7 +391,7 @@ export class Renderer {
|
|
|
369
391
|
});
|
|
370
392
|
this.rememberProductionPrewarm(refreshKey, promise);
|
|
371
393
|
setTimeout(() => {
|
|
372
|
-
void this.doRenderPage(slug, ctx, refreshOptions, performance.now(), cacheKey, undefined)
|
|
394
|
+
void this.doRenderPage(slug, ctx, refreshOptions, performance.now(), cacheKey, undefined, "background")
|
|
373
395
|
.then(() => {
|
|
374
396
|
this.scheduleProductionRenderPrewarm(slug, ctx, refreshOptions, cacheKey);
|
|
375
397
|
resolvePromise();
|
|
@@ -378,6 +400,14 @@ export class Renderer {
|
|
|
378
400
|
promise.finally(() => {
|
|
379
401
|
this.productionPrewarmContexts.delete(refreshKey);
|
|
380
402
|
}).catch((error) => {
|
|
403
|
+
if (error instanceof VeryfrontError && error.slug === "service-overloaded") {
|
|
404
|
+
logger.debug("Production stale render refresh skipped at capacity", {
|
|
405
|
+
slug,
|
|
406
|
+
projectId: ctx.projectId,
|
|
407
|
+
releaseId: ctx.releaseId,
|
|
408
|
+
});
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
381
411
|
logger.warn("Production stale render refresh failed", {
|
|
382
412
|
slug,
|
|
383
413
|
projectId: ctx.projectId,
|
|
@@ -410,9 +440,17 @@ export class Renderer {
|
|
|
410
440
|
return;
|
|
411
441
|
const slug = slugs[index];
|
|
412
442
|
try {
|
|
413
|
-
await this.
|
|
443
|
+
await this.renderPageWithAdmission(slug, ctx, this.buildRoutePrewarmOptions(slug, options), "background");
|
|
414
444
|
}
|
|
415
445
|
catch (error) {
|
|
446
|
+
if (error instanceof VeryfrontError && error.slug === "service-overloaded") {
|
|
447
|
+
logger.debug("Production render prewarm route skipped at capacity", {
|
|
448
|
+
slug,
|
|
449
|
+
projectId: ctx.projectId,
|
|
450
|
+
releaseId: ctx.releaseId,
|
|
451
|
+
});
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
416
454
|
logger.warn("Production render prewarm route failed", {
|
|
417
455
|
slug,
|
|
418
456
|
projectId: ctx.projectId,
|
|
@@ -429,13 +467,17 @@ export class Renderer {
|
|
|
429
467
|
routeCount: slugs.length,
|
|
430
468
|
});
|
|
431
469
|
}
|
|
432
|
-
async doRenderPage(slug, ctx, options, startTime, cacheKey, callerSignal) {
|
|
470
|
+
async doRenderPage(slug, ctx, options, startTime, cacheKey, callerSignal, admission, retryBackgroundOverload = true) {
|
|
433
471
|
const effectiveKey = cacheKey ?? dntShim.crypto.randomUUID();
|
|
434
472
|
const flightKey = this.getSingleflightKey(effectiveKey, ctx, options?.colorScheme);
|
|
435
473
|
const isFollower = cacheKey !== null ? this.renderFlight.has(flightKey) : false;
|
|
474
|
+
const leaderAdmission = isFollower ? this.renderFlightAdmissions.get(flightKey) : admission;
|
|
436
475
|
const runRender = async () => {
|
|
476
|
+
if (cacheKey !== null) {
|
|
477
|
+
this.renderFlightAdmissions.set(flightKey, admission);
|
|
478
|
+
}
|
|
437
479
|
try {
|
|
438
|
-
return await this.runRenderWithCapacity(slug, ctx, options, startTime, cacheKey, callerSignal);
|
|
480
|
+
return await this.runRenderWithCapacity(slug, ctx, options, startTime, cacheKey, callerSignal, admission);
|
|
439
481
|
}
|
|
440
482
|
catch (error) {
|
|
441
483
|
if (error instanceof TimeoutError) {
|
|
@@ -447,10 +489,32 @@ export class Renderer {
|
|
|
447
489
|
}
|
|
448
490
|
throw error;
|
|
449
491
|
}
|
|
492
|
+
finally {
|
|
493
|
+
if (cacheKey !== null &&
|
|
494
|
+
this.renderFlightAdmissions.get(flightKey) === admission) {
|
|
495
|
+
this.renderFlightAdmissions.delete(flightKey);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
450
498
|
};
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
499
|
+
let cachedData;
|
|
500
|
+
try {
|
|
501
|
+
cachedData = cacheKey !== null
|
|
502
|
+
? await waitForSharedPromise(this.renderFlight.do(flightKey, runRender), callerSignal)
|
|
503
|
+
: await runRender();
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
if (retryBackgroundOverload &&
|
|
507
|
+
admission === "foreground" &&
|
|
508
|
+
isFollower &&
|
|
509
|
+
leaderAdmission === "background" &&
|
|
510
|
+
error instanceof VeryfrontError &&
|
|
511
|
+
error.slug === "service-overloaded") {
|
|
512
|
+
// Background leaders never wait for capacity. Give a foreground follower
|
|
513
|
+
// its own bounded admission attempt instead of inheriting that fail-fast result.
|
|
514
|
+
return await this.doRenderPage(slug, ctx, options, startTime, cacheKey, callerSignal, admission, false);
|
|
515
|
+
}
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
454
518
|
if (isFollower) {
|
|
455
519
|
logger.debug("Render deduplicated (follower)", {
|
|
456
520
|
slug,
|
|
@@ -468,40 +532,63 @@ export class Renderer {
|
|
|
468
532
|
stream: null,
|
|
469
533
|
};
|
|
470
534
|
}
|
|
471
|
-
async runRenderWithCapacity(slug, ctx, options, startTime, cacheKey, callerSignal) {
|
|
472
|
-
|
|
535
|
+
async runRenderWithCapacity(slug, ctx, options, startTime, cacheKey, callerSignal, admission) {
|
|
536
|
+
const admissionStartedAt = performance.now();
|
|
537
|
+
const admissionSignal = cacheKey === null ? callerSignal : undefined;
|
|
538
|
+
const waitForCapacity = admission === "foreground";
|
|
539
|
+
const projectAcquired = await acquireProjectSlot(ctx.projectId, {
|
|
540
|
+
wait: waitForCapacity,
|
|
541
|
+
timeoutMs: waitForCapacity ? RENDER_ACQUIRE_TIMEOUT_MS : 0,
|
|
542
|
+
...(admissionSignal ? { signal: admissionSignal } : {}),
|
|
543
|
+
});
|
|
544
|
+
if (!projectAcquired) {
|
|
473
545
|
const activeCount = projectRenderCounts.get(ctx.projectId) ?? 0;
|
|
474
|
-
|
|
546
|
+
const context = {
|
|
475
547
|
slug,
|
|
476
548
|
projectId: ctx.projectId,
|
|
477
549
|
activeRenders: activeCount,
|
|
478
550
|
limit: RENDER_PER_PROJECT_LIMIT,
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
context
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
},
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
const acquired = await renderSemaphore.tryAcquire(RENDER_ACQUIRE_TIMEOUT_MS);
|
|
491
|
-
if (!acquired) {
|
|
492
|
-
await releaseProjectSlot(ctx.projectId);
|
|
493
|
-
logger.error("Render capacity exceeded - service overloaded", {
|
|
494
|
-
slug,
|
|
495
|
-
projectId: ctx.projectId,
|
|
496
|
-
waiting: renderSemaphore.waiting,
|
|
497
|
-
available: renderSemaphore.available,
|
|
498
|
-
});
|
|
551
|
+
admission,
|
|
552
|
+
};
|
|
553
|
+
if (admission === "background") {
|
|
554
|
+
logger.debug("Background render skipped at per-project capacity", context);
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
logger.warn("Per-project render admission exhausted", context);
|
|
558
|
+
}
|
|
499
559
|
throw SERVICE_OVERLOADED.create({
|
|
500
|
-
detail: `
|
|
501
|
-
context
|
|
560
|
+
detail: `Per-project render admission exhausted (${activeCount}/${RENDER_PER_PROJECT_LIMIT} active). Try again shortly.`,
|
|
561
|
+
context,
|
|
502
562
|
});
|
|
503
563
|
}
|
|
564
|
+
let globalAcquired = false;
|
|
504
565
|
try {
|
|
566
|
+
const elapsedAdmissionMs = performance.now() - admissionStartedAt;
|
|
567
|
+
const globalWaitMs = waitForCapacity
|
|
568
|
+
? Math.max(0, RENDER_ACQUIRE_TIMEOUT_MS - elapsedAdmissionMs)
|
|
569
|
+
: 0;
|
|
570
|
+
globalAcquired = await renderSemaphore.tryAcquire(globalWaitMs, {
|
|
571
|
+
...(admissionSignal ? { signal: admissionSignal } : {}),
|
|
572
|
+
});
|
|
573
|
+
if (!globalAcquired) {
|
|
574
|
+
const context = {
|
|
575
|
+
slug,
|
|
576
|
+
projectId: ctx.projectId,
|
|
577
|
+
waiting: renderSemaphore.waiting,
|
|
578
|
+
available: renderSemaphore.available,
|
|
579
|
+
admission,
|
|
580
|
+
};
|
|
581
|
+
if (admission === "background") {
|
|
582
|
+
logger.debug("Background render skipped at global capacity", context);
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
logger.warn("Global render admission exhausted", context);
|
|
586
|
+
}
|
|
587
|
+
throw SERVICE_OVERLOADED.create({
|
|
588
|
+
detail: `Render admission exhausted (${renderSemaphore.waiting} waiting). Service is overloaded.`,
|
|
589
|
+
context,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
505
592
|
const services = this.createServicesForContext(ctx, options?.colorScheme);
|
|
506
593
|
const renderAbortController = new AbortController();
|
|
507
594
|
const request = cacheKey !== null && options?.request
|
|
@@ -541,7 +628,8 @@ export class Renderer {
|
|
|
541
628
|
};
|
|
542
629
|
}
|
|
543
630
|
finally {
|
|
544
|
-
|
|
631
|
+
if (globalAcquired)
|
|
632
|
+
renderSemaphore.release();
|
|
545
633
|
await releaseProjectSlot(ctx.projectId);
|
|
546
634
|
}
|
|
547
635
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/project-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAOtE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"project-middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/project-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAOtE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQ3D,KAAK,gBAAgB,GAAG,CACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;AAEnC,UAAU,+BAA+B;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;CAC3C;AAcD,uEAAuE;AACvE,qBAAa,wBAAwB;;gBAIvB,OAAO,GAAE,+BAAoC;IAYzD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM;IAYlD,KAAK,IAAI,IAAI;IAIP,OAAO,CAAC,KAAK,EAAE,+BAA+B,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;CAiHrF;AAED,eAAO,MAAM,wBAAwB,0BAEnC,CAAC;AAEH,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAOR"}
|
|
@@ -7,6 +7,7 @@ import { getProjectEnvSnapshot } from "../project-env/index.js";
|
|
|
7
7
|
import { loadMiddlewareFile, } from "../dev-server/middleware.js";
|
|
8
8
|
import { LRUCache } from "../../utils/lru-wrapper.js";
|
|
9
9
|
import { serverLogger } from "../../utils/index.js";
|
|
10
|
+
import { isWebSocketPath } from "./request-utils.js";
|
|
10
11
|
const DEFAULT_MAX_ENTRIES = 100;
|
|
11
12
|
const logger = serverLogger.component("project-middleware");
|
|
12
13
|
function cacheSegment(value) {
|
|
@@ -51,7 +52,11 @@ export class ProjectMiddlewareRuntime {
|
|
|
51
52
|
}
|
|
52
53
|
async execute(input) {
|
|
53
54
|
const { handlerContext: ctx, isSharedProxy, next, request } = input;
|
|
54
|
-
|
|
55
|
+
const pathname = new URL(request.url).pathname;
|
|
56
|
+
if (isWebSocketPath(pathname)) {
|
|
57
|
+
return next();
|
|
58
|
+
}
|
|
59
|
+
if (isConfigOptionalControlPlaneRunRequest(request.method, pathname)) {
|
|
55
60
|
return next();
|
|
56
61
|
}
|
|
57
62
|
const environment = resolvedEnvironment(ctx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr.service.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/services/rendering/ssr.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,kCAAkC,CAAC;AAwB1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC5D;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,mBAAmB,IAAI,YAAY,CAAC;IACpC,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACrF,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC;CAC3D;AAUD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,UAAU,GAAG,OAAO,CAAC;IACpC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,SAAS,CAAC,EACN,WAAW,GACX,YAAY,GACZ,UAAU,GACV,cAAc,GACd,SAAS,GACT,2BAA2B,CAAC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AA4FD,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAA0B;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;gBAExC,OAAO,CAAC,EAAE;QACpB,SAAS,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;QACpC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KACrC;IAKD,mBAAmB,IAAI,YAAY;IAW7B,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAI1D,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IA6H1F,OAAO,CAAC,iBAAiB;
|
|
1
|
+
{"version":3,"file":"ssr.service.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/services/rendering/ssr.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,kCAAkC,CAAC;AAwB1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC5D;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,mBAAmB,IAAI,YAAY,CAAC;IACpC,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACrF,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC;CAC3D;AAUD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,UAAU,GAAG,OAAO,CAAC;IACpC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,SAAS,CAAC,EACN,WAAW,GACX,YAAY,GACZ,UAAU,GACV,cAAc,GACd,SAAS,GACT,2BAA2B,CAAC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AA4FD,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAA0B;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;gBAExC,OAAO,CAAC,EAAE;QACpB,SAAS,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;QACpC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KACrC;IAKD,mBAAmB,IAAI,YAAY;IAW7B,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAI1D,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IA6H1F,OAAO,CAAC,iBAAiB;IAyJzB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe;CAS1D"}
|
|
@@ -285,6 +285,17 @@ export class SSRService {
|
|
|
285
285
|
return buildRedirectResult(redirect, errorObj, slug);
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
|
+
if (error instanceof VeryfrontError && error.slug === "service-overloaded") {
|
|
289
|
+
return {
|
|
290
|
+
status: error.status ?? HTTP_UNAVAILABLE,
|
|
291
|
+
html: ErrorPages.memoryPressure(),
|
|
292
|
+
isStreaming: false,
|
|
293
|
+
cacheStrategy: "no-cache",
|
|
294
|
+
error: errorObj,
|
|
295
|
+
errorType: "server-error",
|
|
296
|
+
slug,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
288
299
|
logger.error("Render failed", {
|
|
289
300
|
slug,
|
|
290
301
|
error: errorObj.message,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1146",
|
|
4
4
|
"description": "The simplest way to build AI-powered apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -332,10 +332,10 @@
|
|
|
332
332
|
"@types/react": "19.2.14",
|
|
333
333
|
"@types/react-dom": "19.2.3",
|
|
334
334
|
"ws": "8.21.0",
|
|
335
|
-
"@veryfront/ext-bundler-esbuild": "0.1.
|
|
336
|
-
"@veryfront/ext-content-mdx": "0.1.
|
|
337
|
-
"@veryfront/ext-css-tailwind": "0.1.
|
|
338
|
-
"@veryfront/ext-parser-babel": "0.1.
|
|
335
|
+
"@veryfront/ext-bundler-esbuild": "0.1.1146",
|
|
336
|
+
"@veryfront/ext-content-mdx": "0.1.1146",
|
|
337
|
+
"@veryfront/ext-css-tailwind": "0.1.1146",
|
|
338
|
+
"@veryfront/ext-parser-babel": "0.1.1146"
|
|
339
339
|
},
|
|
340
340
|
"devDependencies": {
|
|
341
341
|
"@types/node": "20.9.0"
|