@bleedingdev/modern-js-prod-server 3.2.0-ultramodern.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.
- package/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/cjs/apply.js +97 -0
- package/dist/cjs/index.js +89 -0
- package/dist/cjs/libs/contractGateAutopilot.js +36 -0
- package/dist/cjs/libs/loadConfig.js +72 -0
- package/dist/cjs/libs/metrics.js +41 -0
- package/dist/cjs/libs/render/index.js +125 -0
- package/dist/cjs/libs/render/ssr.js +118 -0
- package/dist/cjs/libs/render/utils.js +72 -0
- package/dist/cjs/libs/runtimeFallbackWorkerLane.js +167 -0
- package/dist/cjs/libs/telemetry.js +87 -0
- package/dist/cjs/netlify.js +56 -0
- package/dist/cjs/server/index.js +579 -0
- package/dist/cjs/server/modernServer.js +472 -0
- package/dist/cjs/server/modernServerSplit.js +38 -0
- package/dist/cjs/types.js +18 -0
- package/dist/cjs/utils.js +38 -0
- package/dist/esm/apply.mjs +63 -0
- package/dist/esm/index.mjs +26 -0
- package/dist/esm/libs/contractGateAutopilot.mjs +1 -0
- package/dist/esm/libs/loadConfig.mjs +22 -0
- package/dist/esm/libs/metrics.mjs +7 -0
- package/dist/esm/libs/render/index.mjs +81 -0
- package/dist/esm/libs/render/ssr.mjs +73 -0
- package/dist/esm/libs/render/utils.mjs +35 -0
- package/dist/esm/libs/runtimeFallbackWorkerLane.mjs +130 -0
- package/dist/esm/libs/telemetry.mjs +1 -0
- package/dist/esm/netlify.mjs +22 -0
- package/dist/esm/rslib-runtime.mjs +18 -0
- package/dist/esm/server/index.mjs +535 -0
- package/dist/esm/server/modernServer.mjs +419 -0
- package/dist/esm/server/modernServerSplit.mjs +4 -0
- package/dist/esm/types.mjs +0 -0
- package/dist/esm/utils.mjs +4 -0
- package/dist/esm-node/apply.mjs +64 -0
- package/dist/esm-node/index.mjs +27 -0
- package/dist/esm-node/libs/contractGateAutopilot.mjs +2 -0
- package/dist/esm-node/libs/loadConfig.mjs +23 -0
- package/dist/esm-node/libs/metrics.mjs +8 -0
- package/dist/esm-node/libs/render/index.mjs +82 -0
- package/dist/esm-node/libs/render/ssr.mjs +75 -0
- package/dist/esm-node/libs/render/utils.mjs +36 -0
- package/dist/esm-node/libs/runtimeFallbackWorkerLane.mjs +131 -0
- package/dist/esm-node/libs/telemetry.mjs +2 -0
- package/dist/esm-node/netlify.mjs +23 -0
- package/dist/esm-node/rslib-runtime.mjs +19 -0
- package/dist/esm-node/server/index.mjs +536 -0
- package/dist/esm-node/server/modernServer.mjs +421 -0
- package/dist/esm-node/server/modernServerSplit.mjs +5 -0
- package/dist/esm-node/types.mjs +1 -0
- package/dist/esm-node/utils.mjs +5 -0
- package/dist/types/apply.d.ts +6 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/libs/metrics.d.ts +8 -0
- package/dist/types/libs/telemetry.d.ts +2 -0
- package/dist/types/netlify.d.ts +3 -0
- package/dist/types/types.d.ts +15 -0
- package/package.json +79 -0
- package/rslib.config.mts +4 -0
- package/rstest.config.mts +7 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
12
|
+
(()=>{
|
|
13
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
14
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: definition[key]
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
20
|
+
(()=>{
|
|
21
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
22
|
+
})();
|
|
23
|
+
(()=>{
|
|
24
|
+
__webpack_require__.r = (exports1)=>{
|
|
25
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
26
|
+
value: 'Module'
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
29
|
+
value: true
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
})();
|
|
33
|
+
var __webpack_exports__ = {};
|
|
34
|
+
__webpack_require__.r(__webpack_exports__);
|
|
35
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
36
|
+
Server: ()=>Server
|
|
37
|
+
});
|
|
38
|
+
const server_core_namespaceObject = require("@modern-js/server-core");
|
|
39
|
+
const utils_namespaceObject = require("@modern-js/utils");
|
|
40
|
+
const external_fs_namespaceObject = require("fs");
|
|
41
|
+
const external_path_namespaceObject = require("path");
|
|
42
|
+
var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
|
|
43
|
+
const contractGateAutopilot_js_namespaceObject = require("../libs/contractGateAutopilot.js");
|
|
44
|
+
const loadConfig_js_namespaceObject = require("../libs/loadConfig.js");
|
|
45
|
+
const metrics_js_namespaceObject = require("../libs/metrics.js");
|
|
46
|
+
const runtimeFallbackWorkerLane_js_namespaceObject = require("../libs/runtimeFallbackWorkerLane.js");
|
|
47
|
+
const telemetry_js_namespaceObject = require("../libs/telemetry.js");
|
|
48
|
+
const external_utils_js_namespaceObject = require("../utils.js");
|
|
49
|
+
const external_modernServerSplit_js_namespaceObject = require("./modernServerSplit.js");
|
|
50
|
+
const CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
51
|
+
const DEFAULT_RUNTIME_FALLBACK_GATE_NAME = 'runtime-mf-fallback-health';
|
|
52
|
+
const DEFAULT_RUNTIME_FALLBACK_FAILURE_HOLD_MS = 300000;
|
|
53
|
+
const DEFAULT_RUNTIME_FALLBACK_MAX_BODY_BYTES = 16384;
|
|
54
|
+
class Server {
|
|
55
|
+
async init({ disableHttpServer = false } = {
|
|
56
|
+
disableHttpServer: false
|
|
57
|
+
}) {
|
|
58
|
+
const { options } = this;
|
|
59
|
+
await this.loadServerEnv(options);
|
|
60
|
+
this.initServerConfig(options);
|
|
61
|
+
await this.injectContext(this.runner, options);
|
|
62
|
+
this.runner = await this.createHookRunner();
|
|
63
|
+
await this.initConfig(this.runner, options);
|
|
64
|
+
await this.initTelemetry(options);
|
|
65
|
+
await this.injectContext(this.runner, options);
|
|
66
|
+
this.server = this.serverImpl(options);
|
|
67
|
+
await this.runPrepareHook(this.runner);
|
|
68
|
+
if (!disableHttpServer) this.app = await this.server.createHTTPServer(this.getRequestHandler());
|
|
69
|
+
{
|
|
70
|
+
const result = await this.runner.beforeServerInit({
|
|
71
|
+
app: this.app,
|
|
72
|
+
server: this.server
|
|
73
|
+
});
|
|
74
|
+
({ app: this.app = this.app, server: this.server } = result);
|
|
75
|
+
}
|
|
76
|
+
await this.server.onInit(this.runner, this.app);
|
|
77
|
+
{
|
|
78
|
+
const result = await this.runner.afterServerInit({
|
|
79
|
+
app: this.app,
|
|
80
|
+
server: this.server
|
|
81
|
+
});
|
|
82
|
+
({ app: this.app = this.app, server: this.server } = result);
|
|
83
|
+
}
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
runConfigHook(runner, serverConfig) {
|
|
87
|
+
const newServerConfig = runner.config(serverConfig || {});
|
|
88
|
+
return newServerConfig;
|
|
89
|
+
}
|
|
90
|
+
async runPrepareHook(runner) {
|
|
91
|
+
runner.prepare();
|
|
92
|
+
}
|
|
93
|
+
initServerConfig(options) {
|
|
94
|
+
const { pwd, serverConfigFile } = options;
|
|
95
|
+
const distDirectory = external_path_default().join(pwd, options.config.output.path || 'dist');
|
|
96
|
+
const serverConfigPath = (0, loadConfig_js_namespaceObject.getServerConfigPath)(distDirectory, serverConfigFile);
|
|
97
|
+
const serverConfig = (0, loadConfig_js_namespaceObject.requireConfig)(serverConfigPath);
|
|
98
|
+
this.serverConfig = serverConfig;
|
|
99
|
+
}
|
|
100
|
+
async initConfig(runner, options) {
|
|
101
|
+
const { pwd, config } = options;
|
|
102
|
+
const { serverConfig } = this;
|
|
103
|
+
const finalServerConfig = this.runConfigHook(runner, serverConfig);
|
|
104
|
+
const resolvedConfigPath = (0, utils_namespaceObject.ensureAbsolutePath)(pwd, external_path_default().join(config.output.path || 'dist', utils_namespaceObject.OUTPUT_CONFIG_FILE));
|
|
105
|
+
options.config = (0, loadConfig_js_namespaceObject.loadConfig)({
|
|
106
|
+
cliConfig: config,
|
|
107
|
+
serverConfig: finalServerConfig,
|
|
108
|
+
resolvedConfigPath
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async initTelemetry(options) {
|
|
112
|
+
const telemetryConfig = options.config.server?.telemetry;
|
|
113
|
+
if (!telemetryConfig) return;
|
|
114
|
+
const hasEnabledExporters = (0, telemetry_js_namespaceObject.hasEnabledTelemetryExporters)(telemetryConfig);
|
|
115
|
+
if (true !== telemetryConfig.enabled && !hasEnabledExporters) return;
|
|
116
|
+
const registry = new telemetry_js_namespaceObject.TelemetryRegistry({
|
|
117
|
+
service: telemetryConfig.service || options.appContext?.metaName || 'modern-js',
|
|
118
|
+
module: telemetryConfig.module || 'server',
|
|
119
|
+
environment: telemetryConfig.environment || process.env.MODERN_ENV || process.env.NODE_ENV || 'development',
|
|
120
|
+
samplingRate: telemetryConfig.samplingRate,
|
|
121
|
+
flushIntervalMs: telemetryConfig.flushIntervalMs,
|
|
122
|
+
maxBatchSize: telemetryConfig.maxBatchSize,
|
|
123
|
+
maxQueueSize: telemetryConfig.maxQueueSize,
|
|
124
|
+
redactionKeys: telemetryConfig.redactionKeys,
|
|
125
|
+
slo: {
|
|
126
|
+
queueUtilizationWarnThreshold: telemetryConfig.slo?.queueUtilizationWarnThreshold,
|
|
127
|
+
queueDroppedWarnThreshold: telemetryConfig.slo?.queueDroppedWarnThreshold,
|
|
128
|
+
alertCooldownMs: telemetryConfig.slo?.alertCooldownMs,
|
|
129
|
+
onAlert: (alert)=>{
|
|
130
|
+
options.logger?.warn(`[telemetry.slo] ${alert.type} threshold=${alert.threshold} value=${alert.value} depth=${alert.queueDepth}/${alert.queueCapacity} dropped=${alert.totalDropped}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
if (telemetryConfig.exporters?.otlp?.enabled) await registry.register((0, telemetry_js_namespaceObject.createOtlpTelemetryExporter)(telemetryConfig.exporters.otlp));
|
|
135
|
+
if (telemetryConfig.exporters?.victoriaMetrics?.enabled) await registry.register((0, telemetry_js_namespaceObject.createVictoriaMetricsTelemetryExporter)(telemetryConfig.exporters.victoriaMetrics));
|
|
136
|
+
try {
|
|
137
|
+
await registry.startupHealthCheck({
|
|
138
|
+
failLoud: telemetryConfig.failLoudStartup ?? true
|
|
139
|
+
});
|
|
140
|
+
} catch (error) {
|
|
141
|
+
await registry.shutdown();
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
options.metrics = (0, telemetry_js_namespaceObject.createTelemetryAwareMetrics)(options.metrics || metrics_js_namespaceObject.metrics, registry);
|
|
145
|
+
this.telemetryRegistry = registry;
|
|
146
|
+
const canaryConfig = telemetryConfig.canary;
|
|
147
|
+
if (canaryConfig?.enabled) {
|
|
148
|
+
const contractGates = canaryConfig.contractGates;
|
|
149
|
+
const orchestrator = new telemetry_js_namespaceObject.TelemetryCanaryOrchestrator({
|
|
150
|
+
registry,
|
|
151
|
+
evaluationIntervalMs: canaryConfig.evaluationIntervalMs,
|
|
152
|
+
minConsecutiveHealthyEvaluations: canaryConfig.minConsecutiveHealthyEvaluations,
|
|
153
|
+
rollbackConsecutiveFailures: canaryConfig.rollbackConsecutiveFailures,
|
|
154
|
+
maxQueueUtilization: canaryConfig.maxQueueUtilization,
|
|
155
|
+
maxTotalDropped: canaryConfig.maxTotalDropped,
|
|
156
|
+
maxUnhealthyExporters: canaryConfig.maxUnhealthyExporters,
|
|
157
|
+
requiredContractGates: Object.keys(contractGates || {}),
|
|
158
|
+
onPromote: (decision)=>{
|
|
159
|
+
options.logger?.info(`[telemetry.canary] promoted after ${decision.consecutiveHealthy} healthy evaluations`);
|
|
160
|
+
this.emitCanaryDecisionMetric(registry, decision, 'promote');
|
|
161
|
+
},
|
|
162
|
+
onRollback: (decision)=>{
|
|
163
|
+
options.logger?.error(`[telemetry.canary] rollback triggered failures=${decision.failures.map((item)=>item.reason).join(',')}`);
|
|
164
|
+
this.emitCanaryDecisionMetric(registry, decision, 'rollback');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
if (contractGates) orchestrator.setContractGates(contractGates);
|
|
168
|
+
this.canaryOrchestrator = orchestrator;
|
|
169
|
+
orchestrator.start();
|
|
170
|
+
const autopilotEnabled = canaryConfig.autopilot?.enabled ?? true;
|
|
171
|
+
if (autopilotEnabled) {
|
|
172
|
+
const gateSnapshotPath = this.resolveContractGateSnapshotPath(options, canaryConfig.autopilot?.gateSnapshotPath);
|
|
173
|
+
this.contractGateAutopilot = new contractGateAutopilot_js_namespaceObject.ContractGateAutopilot({
|
|
174
|
+
orchestrator,
|
|
175
|
+
gateSnapshotPath,
|
|
176
|
+
pollIntervalMs: canaryConfig.autopilot?.pollIntervalMs,
|
|
177
|
+
gateStaleAfterMs: canaryConfig.autopilot?.gateStaleAfterMs,
|
|
178
|
+
logger: {
|
|
179
|
+
info: (message)=>{
|
|
180
|
+
options.logger?.info(message);
|
|
181
|
+
},
|
|
182
|
+
warn: (message)=>{
|
|
183
|
+
options.logger?.warn(message);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
await this.contractGateAutopilot.start();
|
|
188
|
+
const runtimeSignalConfig = canaryConfig.autopilot?.runtimeFallbackSignal;
|
|
189
|
+
const runtimeSignalEnabled = runtimeSignalConfig?.enabled ?? true;
|
|
190
|
+
if (runtimeSignalEnabled) {
|
|
191
|
+
const workerLaneConfig = runtimeSignalConfig?.workerLane;
|
|
192
|
+
const workerLaneEnabledFromEnv = 'true' === process.env.MODERN_RUNTIME_FALLBACK_WORKER_LANE;
|
|
193
|
+
const workerLaneEnabled = 'boolean' == typeof workerLaneConfig?.enabled ? workerLaneConfig.enabled : workerLaneEnabledFromEnv;
|
|
194
|
+
this.runtimeFallbackSignalConfig = {
|
|
195
|
+
endpoint: (0, telemetry_js_namespaceObject.resolveRuntimeFallbackSignalEndpoint)(runtimeSignalConfig?.endpoint),
|
|
196
|
+
gateName: runtimeSignalConfig?.gateName?.trim() || DEFAULT_RUNTIME_FALLBACK_GATE_NAME,
|
|
197
|
+
gateSnapshotPath,
|
|
198
|
+
failureHoldMs: Math.max(1000, runtimeSignalConfig?.failureHoldMs ?? DEFAULT_RUNTIME_FALLBACK_FAILURE_HOLD_MS),
|
|
199
|
+
maxBodyBytes: Math.max(512, runtimeSignalConfig?.maxBodyBytes ?? DEFAULT_RUNTIME_FALLBACK_MAX_BODY_BYTES),
|
|
200
|
+
auth: (0, telemetry_js_namespaceObject.normalizeRuntimeFallbackSignalAuthConfig)(runtimeSignalConfig?.auth),
|
|
201
|
+
trustPolicy: (0, telemetry_js_namespaceObject.normalizeRuntimeFallbackTrustPolicy)(runtimeSignalConfig?.trustPolicy),
|
|
202
|
+
runtimeState: (0, telemetry_js_namespaceObject.createRuntimeFallbackSignalRuntimeState)(),
|
|
203
|
+
workerLane: {
|
|
204
|
+
enabled: workerLaneEnabled,
|
|
205
|
+
timeoutMs: Math.max(25, workerLaneConfig?.timeoutMs ?? runtimeFallbackWorkerLane_js_namespaceObject.DEFAULT_RUNTIME_FALLBACK_WORKER_TIMEOUT_MS),
|
|
206
|
+
workerSuccessCount: 0,
|
|
207
|
+
fallbackToMainThreadCount: 0
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
orchestrator.evaluate();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
resolveContractGateSnapshotPath(options, configuredPath) {
|
|
216
|
+
const rawPath = configuredPath || process.env.MODERN_CONTRACT_GATES_FILE || '.modern/contract-gates.json';
|
|
217
|
+
if (external_path_default().isAbsolute(rawPath)) return rawPath;
|
|
218
|
+
return external_path_default().resolve(options.pwd, rawPath);
|
|
219
|
+
}
|
|
220
|
+
emitCanaryDecisionMetric(registry, decision, action) {
|
|
221
|
+
try {
|
|
222
|
+
registry.enqueueMetric({
|
|
223
|
+
name: `telemetry.canary.${action}`,
|
|
224
|
+
value: 1,
|
|
225
|
+
unit: 'count',
|
|
226
|
+
tags: {
|
|
227
|
+
action,
|
|
228
|
+
state: decision.state,
|
|
229
|
+
failures: String(decision.failures.length)
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
} catch (_error) {}
|
|
233
|
+
}
|
|
234
|
+
async close() {
|
|
235
|
+
if (this.contractGateAutopilot) {
|
|
236
|
+
this.contractGateAutopilot.stop();
|
|
237
|
+
this.contractGateAutopilot = void 0;
|
|
238
|
+
}
|
|
239
|
+
this.runtimeFallbackSignalConfig = void 0;
|
|
240
|
+
if (this.canaryOrchestrator) this.canaryOrchestrator.stop();
|
|
241
|
+
if (this.telemetryRegistry) await this.telemetryRegistry.shutdown();
|
|
242
|
+
if (!this.app) return;
|
|
243
|
+
await new Promise((resolve)=>{
|
|
244
|
+
this.app.close(()=>resolve());
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
listen(options, listener) {
|
|
248
|
+
const callback = ()=>{
|
|
249
|
+
listener?.();
|
|
250
|
+
};
|
|
251
|
+
if ('object' == typeof options) {
|
|
252
|
+
if (process.env.PORT) Object.assign(options, {
|
|
253
|
+
port: process.env.PORT
|
|
254
|
+
});
|
|
255
|
+
this.app.listen(options, callback);
|
|
256
|
+
} else this.app.listen(process.env.PORT || options || 8080, callback);
|
|
257
|
+
}
|
|
258
|
+
getRequestHandler() {
|
|
259
|
+
const requestHandler = this.server.getRequestHandler();
|
|
260
|
+
return (req, res, next)=>{
|
|
261
|
+
if (this.shouldHandleRuntimeStatus(req)) return void this.handleRuntimeStatus(req, res);
|
|
262
|
+
if (this.shouldHandleRuntimeFallbackSignal(req)) return void this.handleRuntimeFallbackSignal(req, res);
|
|
263
|
+
return requestHandler(req, res, next);
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
shouldHandleRuntimeFallbackSignal(req) {
|
|
267
|
+
const runtimeSignalConfig = this.runtimeFallbackSignalConfig;
|
|
268
|
+
if (!runtimeSignalConfig) return false;
|
|
269
|
+
if ('POST' !== (req.method || 'GET').toUpperCase()) return false;
|
|
270
|
+
const pathName = this.getRequestPath(req.url);
|
|
271
|
+
return pathName === runtimeSignalConfig.endpoint;
|
|
272
|
+
}
|
|
273
|
+
shouldHandleRuntimeStatus(req) {
|
|
274
|
+
if ('GET' !== (req.method || 'GET').toUpperCase()) return false;
|
|
275
|
+
const pathName = this.getRequestPath(req.url);
|
|
276
|
+
return pathName === this.runtimeStatusEndpoint;
|
|
277
|
+
}
|
|
278
|
+
getRequestPath(urlValue) {
|
|
279
|
+
try {
|
|
280
|
+
const requestUrl = new URL(urlValue || '/', 'http://127.0.0.1');
|
|
281
|
+
return requestUrl.pathname;
|
|
282
|
+
} catch (_error) {
|
|
283
|
+
return '/';
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async readRequestBody(req, maxBodyBytes) {
|
|
287
|
+
return new Promise((resolve, reject)=>{
|
|
288
|
+
const chunks = [];
|
|
289
|
+
let totalBytes = 0;
|
|
290
|
+
let done = false;
|
|
291
|
+
const cleanup = ()=>{
|
|
292
|
+
req.off('data', onData);
|
|
293
|
+
req.off('end', onEnd);
|
|
294
|
+
req.off('error', onError);
|
|
295
|
+
};
|
|
296
|
+
const onData = (chunk)=>{
|
|
297
|
+
if (done) return;
|
|
298
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
299
|
+
totalBytes += buffer.length;
|
|
300
|
+
if (totalBytes > maxBodyBytes) {
|
|
301
|
+
const error = new Error('runtime fallback signal payload too large');
|
|
302
|
+
error.code = 'PAYLOAD_TOO_LARGE';
|
|
303
|
+
done = true;
|
|
304
|
+
cleanup();
|
|
305
|
+
reject(error);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
chunks.push(buffer);
|
|
309
|
+
};
|
|
310
|
+
const onEnd = ()=>{
|
|
311
|
+
if (done) return;
|
|
312
|
+
done = true;
|
|
313
|
+
cleanup();
|
|
314
|
+
resolve(Buffer.concat(chunks).toString('utf8'));
|
|
315
|
+
};
|
|
316
|
+
const onError = (error)=>{
|
|
317
|
+
if (done) return;
|
|
318
|
+
done = true;
|
|
319
|
+
cleanup();
|
|
320
|
+
reject(error);
|
|
321
|
+
};
|
|
322
|
+
req.on('data', onData);
|
|
323
|
+
req.on('end', onEnd);
|
|
324
|
+
req.on('error', onError);
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
async handleRuntimeFallbackSignal(req, res) {
|
|
328
|
+
const runtimeSignalConfig = this.runtimeFallbackSignalConfig;
|
|
329
|
+
if (!runtimeSignalConfig) {
|
|
330
|
+
res.statusCode = 404;
|
|
331
|
+
res.end();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
(0, telemetry_js_namespaceObject.enforceRuntimeFallbackSignalAuthToken)(this.getRequestHeader(req, runtimeSignalConfig.auth.headerName), runtimeSignalConfig.auth);
|
|
336
|
+
const rawBody = await this.readRequestBody(req, runtimeSignalConfig.maxBodyBytes);
|
|
337
|
+
const payload = (0, telemetry_js_namespaceObject.parseRuntimeFallbackSignalPayloadFromRawBody)(rawBody, runtimeSignalConfig.maxBodyBytes);
|
|
338
|
+
const trustResult = (0, telemetry_js_namespaceObject.enforceRuntimeFallbackSignalTrustPolicy)(payload, {
|
|
339
|
+
trustPolicy: runtimeSignalConfig.trustPolicy,
|
|
340
|
+
runtimeState: runtimeSignalConfig.runtimeState
|
|
341
|
+
});
|
|
342
|
+
if (trustResult.deduped) {
|
|
343
|
+
res.statusCode = 202;
|
|
344
|
+
res.setHeader('content-type', 'application/json');
|
|
345
|
+
res.end('{"ok":true,"deduped":true}');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
let persistedByWorkerLane = false;
|
|
349
|
+
if (runtimeSignalConfig.workerLane.enabled) {
|
|
350
|
+
const workerResult = await (0, runtimeFallbackWorkerLane_js_namespaceObject.persistRuntimeFallbackContractGateInWorker)({
|
|
351
|
+
snapshotPath: runtimeSignalConfig.gateSnapshotPath,
|
|
352
|
+
gateName: runtimeSignalConfig.gateName,
|
|
353
|
+
failureHoldMs: runtimeSignalConfig.failureHoldMs,
|
|
354
|
+
payload: payload,
|
|
355
|
+
schemaVersion: CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION
|
|
356
|
+
}, {
|
|
357
|
+
enabled: true,
|
|
358
|
+
timeoutMs: runtimeSignalConfig.workerLane.timeoutMs
|
|
359
|
+
});
|
|
360
|
+
if (workerResult.ok) {
|
|
361
|
+
persistedByWorkerLane = true;
|
|
362
|
+
runtimeSignalConfig.workerLane.workerSuccessCount += 1;
|
|
363
|
+
runtimeSignalConfig.workerLane.lastError = void 0;
|
|
364
|
+
const payloadRecord = payload;
|
|
365
|
+
const reason = 'string' == typeof payloadRecord.reason ? payloadRecord.reason : 'runtime_fallback';
|
|
366
|
+
const phase = 'string' == typeof payloadRecord.phase ? payloadRecord.phase : 'unknown';
|
|
367
|
+
const appName = 'string' == typeof payloadRecord.appName ? payloadRecord.appName : 'unknown';
|
|
368
|
+
this.options.logger?.warn(`[telemetry.canary.autopilot] runtime fallback signal gate=${runtimeSignalConfig.gateName} reason=${reason} phase=${phase} app=${appName} workerLane=true`);
|
|
369
|
+
} else {
|
|
370
|
+
runtimeSignalConfig.workerLane.fallbackToMainThreadCount += 1;
|
|
371
|
+
runtimeSignalConfig.workerLane.lastError = workerResult.error;
|
|
372
|
+
this.options.logger?.warn(`[telemetry.canary.autopilot] runtime fallback worker lane fallback: ${workerResult.error || 'unknown_error'}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!persistedByWorkerLane) await this.persistRuntimeFallbackContractGate(payload, runtimeSignalConfig);
|
|
376
|
+
res.statusCode = 202;
|
|
377
|
+
res.setHeader('content-type', 'application/json');
|
|
378
|
+
res.end('{"ok":true}');
|
|
379
|
+
} catch (error) {
|
|
380
|
+
const signalError = error;
|
|
381
|
+
res.statusCode = (0, telemetry_js_namespaceObject.getRuntimeSignalErrorStatusCode)(signalError);
|
|
382
|
+
res.setHeader('content-type', 'application/json');
|
|
383
|
+
res.end(`{"ok":false,"error":${JSON.stringify(signalError instanceof Error ? signalError.message : String(signalError))}}`);
|
|
384
|
+
this.options.logger?.warn(`[telemetry.canary.autopilot] runtime fallback signal rejected: ${error instanceof Error ? error.message : String(error)}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async handleRuntimeStatus(req, res) {
|
|
388
|
+
try {
|
|
389
|
+
if (this.runtimeFallbackSignalConfig?.auth.enabled) (0, telemetry_js_namespaceObject.enforceRuntimeFallbackSignalAuthToken)(this.getRequestHeader(req, this.runtimeFallbackSignalConfig.auth.headerName), this.runtimeFallbackSignalConfig.auth);
|
|
390
|
+
res.statusCode = 200;
|
|
391
|
+
res.setHeader('content-type', 'application/json');
|
|
392
|
+
res.end(JSON.stringify(this.buildRuntimeStatusPayload()));
|
|
393
|
+
} catch (error) {
|
|
394
|
+
const signalError = error;
|
|
395
|
+
res.statusCode = (0, telemetry_js_namespaceObject.getRuntimeSignalErrorStatusCode)(signalError);
|
|
396
|
+
res.setHeader('content-type', 'application/json');
|
|
397
|
+
res.end(`{"ok":false,"error":${JSON.stringify(signalError instanceof Error ? signalError.message : String(signalError))}}`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
buildRuntimeStatusPayload() {
|
|
401
|
+
const telemetry = this.telemetryRegistry ? {
|
|
402
|
+
enabled: true,
|
|
403
|
+
queueStats: this.telemetryRegistry.getQueueStats(),
|
|
404
|
+
exporterHealth: this.telemetryRegistry.getExporterHealth()
|
|
405
|
+
} : {
|
|
406
|
+
enabled: false,
|
|
407
|
+
queueStats: null,
|
|
408
|
+
exporterHealth: []
|
|
409
|
+
};
|
|
410
|
+
const canary = this.canaryOrchestrator ? {
|
|
411
|
+
enabled: true,
|
|
412
|
+
...this.canaryOrchestrator.getStatusSnapshot()
|
|
413
|
+
} : {
|
|
414
|
+
enabled: false
|
|
415
|
+
};
|
|
416
|
+
const runtimeFallbackSignal = this.runtimeFallbackSignalConfig ? {
|
|
417
|
+
enabled: true,
|
|
418
|
+
endpoint: this.runtimeFallbackSignalConfig.endpoint,
|
|
419
|
+
gateName: this.runtimeFallbackSignalConfig.gateName,
|
|
420
|
+
failureHoldMs: this.runtimeFallbackSignalConfig.failureHoldMs,
|
|
421
|
+
maxBodyBytes: this.runtimeFallbackSignalConfig.maxBodyBytes,
|
|
422
|
+
auth: {
|
|
423
|
+
enabled: this.runtimeFallbackSignalConfig.auth.enabled,
|
|
424
|
+
headerName: this.runtimeFallbackSignalConfig.auth.headerName
|
|
425
|
+
},
|
|
426
|
+
trustPolicy: {
|
|
427
|
+
allowedApps: this.runtimeFallbackSignalConfig.trustPolicy.allowedApps,
|
|
428
|
+
allowedEntryOrigins: this.runtimeFallbackSignalConfig.trustPolicy.allowedEntryOrigins,
|
|
429
|
+
enforceRuntimeDigest: this.runtimeFallbackSignalConfig.trustPolicy.enforceRuntimeDigest,
|
|
430
|
+
expectedRuntimeDigestsCount: Object.keys(this.runtimeFallbackSignalConfig.trustPolicy.expectedRuntimeDigests).length,
|
|
431
|
+
maxSignalsPerWindow: this.runtimeFallbackSignalConfig.trustPolicy.maxSignalsPerWindow,
|
|
432
|
+
windowMs: this.runtimeFallbackSignalConfig.trustPolicy.windowMs,
|
|
433
|
+
dedupeWindowMs: this.runtimeFallbackSignalConfig.trustPolicy.dedupeWindowMs
|
|
434
|
+
},
|
|
435
|
+
workerLane: {
|
|
436
|
+
enabled: this.runtimeFallbackSignalConfig.workerLane.enabled,
|
|
437
|
+
timeoutMs: this.runtimeFallbackSignalConfig.workerLane.timeoutMs,
|
|
438
|
+
workerSuccessCount: this.runtimeFallbackSignalConfig.workerLane.workerSuccessCount,
|
|
439
|
+
fallbackToMainThreadCount: this.runtimeFallbackSignalConfig.workerLane.fallbackToMainThreadCount,
|
|
440
|
+
lastError: this.runtimeFallbackSignalConfig.workerLane.lastError
|
|
441
|
+
}
|
|
442
|
+
} : {
|
|
443
|
+
enabled: false
|
|
444
|
+
};
|
|
445
|
+
return {
|
|
446
|
+
ok: true,
|
|
447
|
+
timestamp: Date.now(),
|
|
448
|
+
telemetry,
|
|
449
|
+
canary,
|
|
450
|
+
runtimeFallbackSignal
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
getRequestHeader(req, headerName) {
|
|
454
|
+
const raw = req.headers[headerName.toLowerCase()];
|
|
455
|
+
if (Array.isArray(raw)) return raw[0];
|
|
456
|
+
if ('string' == typeof raw) return raw;
|
|
457
|
+
}
|
|
458
|
+
async persistRuntimeFallbackContractGate(payload, runtimeSignalConfig) {
|
|
459
|
+
const now = Date.now();
|
|
460
|
+
const snapshotPath = runtimeSignalConfig.gateSnapshotPath;
|
|
461
|
+
let snapshot = {
|
|
462
|
+
schemaVersion: CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION,
|
|
463
|
+
updatedAt: now,
|
|
464
|
+
gates: {}
|
|
465
|
+
};
|
|
466
|
+
if (await utils_namespaceObject.fs.pathExists(snapshotPath)) try {
|
|
467
|
+
const raw = await external_fs_namespaceObject.promises.readFile(snapshotPath, 'utf8');
|
|
468
|
+
const parsed = JSON.parse(raw);
|
|
469
|
+
if (parsed && 'object' == typeof parsed) snapshot = {
|
|
470
|
+
schemaVersion: 'number' == typeof parsed.schemaVersion ? parsed.schemaVersion : CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION,
|
|
471
|
+
updatedAt: 'number' == typeof parsed.updatedAt ? parsed.updatedAt : now,
|
|
472
|
+
gates: parsed.gates && 'object' == typeof parsed.gates ? parsed.gates : {}
|
|
473
|
+
};
|
|
474
|
+
} catch (_error) {
|
|
475
|
+
snapshot = {
|
|
476
|
+
schemaVersion: CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION,
|
|
477
|
+
updatedAt: now,
|
|
478
|
+
gates: {}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
const reason = 'string' == typeof payload.reason ? payload.reason : 'runtime_fallback';
|
|
482
|
+
const phase = 'string' == typeof payload.phase ? payload.phase : 'unknown';
|
|
483
|
+
const appName = 'string' == typeof payload.appName ? payload.appName : 'unknown';
|
|
484
|
+
const entry = 'string' == typeof payload.entry ? payload.entry : void 0;
|
|
485
|
+
snapshot.schemaVersion = CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION;
|
|
486
|
+
snapshot.updatedAt = now;
|
|
487
|
+
snapshot.gates = snapshot.gates || {};
|
|
488
|
+
snapshot.gates[runtimeSignalConfig.gateName] = {
|
|
489
|
+
passed: false,
|
|
490
|
+
reason: `runtime_fallback:${reason} phase=${phase} app=${appName}${entry ? ` entry=${entry}` : ''}`,
|
|
491
|
+
updatedAt: now,
|
|
492
|
+
expiresAt: now + runtimeSignalConfig.failureHoldMs,
|
|
493
|
+
source: 'runtime-mf-fallback-signal',
|
|
494
|
+
metadata: payload
|
|
495
|
+
};
|
|
496
|
+
await external_fs_namespaceObject.promises.mkdir(external_path_default().dirname(snapshotPath), {
|
|
497
|
+
recursive: true
|
|
498
|
+
});
|
|
499
|
+
await external_fs_namespaceObject.promises.writeFile(snapshotPath, `${JSON.stringify(snapshot, null, 2)}\n`);
|
|
500
|
+
this.options.logger?.warn(`[telemetry.canary.autopilot] runtime fallback signal gate=${runtimeSignalConfig.gateName} reason=${reason} phase=${phase} app=${appName}`);
|
|
501
|
+
}
|
|
502
|
+
async render(req, res, url) {
|
|
503
|
+
return this.server.render(req, res, url);
|
|
504
|
+
}
|
|
505
|
+
async createHookRunner() {
|
|
506
|
+
server_core_namespaceObject.serverManager.clear();
|
|
507
|
+
const { options } = this;
|
|
508
|
+
const { internalPlugins = utils_namespaceObject.INTERNAL_SERVER_PLUGINS, pwd, plugins = [] } = options;
|
|
509
|
+
const serverPlugins = this.serverConfig.plugins || [];
|
|
510
|
+
const loadedPlugins = (0, server_core_namespaceObject.loadPlugins)(pwd, [
|
|
511
|
+
...serverPlugins,
|
|
512
|
+
...plugins
|
|
513
|
+
], {
|
|
514
|
+
internalPlugins
|
|
515
|
+
});
|
|
516
|
+
(0, external_utils_js_namespaceObject.debug)('plugins', loadedPlugins);
|
|
517
|
+
loadedPlugins.forEach((p)=>{
|
|
518
|
+
server_core_namespaceObject.serverManager.usePlugin(p);
|
|
519
|
+
});
|
|
520
|
+
const hooksRunner = await server_core_namespaceObject.serverManager.init();
|
|
521
|
+
return hooksRunner;
|
|
522
|
+
}
|
|
523
|
+
async injectContext(runner, options) {
|
|
524
|
+
const appContext = this.initAppContext();
|
|
525
|
+
const { config, pwd } = options;
|
|
526
|
+
server_core_namespaceObject.ConfigContext.set(config);
|
|
527
|
+
server_core_namespaceObject.AppContext.set({
|
|
528
|
+
...appContext,
|
|
529
|
+
distDirectory: external_path_default().join(pwd, config.output.path || 'dist')
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
initAppContext() {
|
|
533
|
+
const { options } = this;
|
|
534
|
+
const { pwd: appDirectory, plugins = [], config, appContext } = options;
|
|
535
|
+
const serverPlugins = plugins.map((p)=>({
|
|
536
|
+
server: p
|
|
537
|
+
}));
|
|
538
|
+
return {
|
|
539
|
+
appDirectory,
|
|
540
|
+
apiDirectory: appContext?.apiDirectory,
|
|
541
|
+
lambdaDirectory: appContext?.lambdaDirectory,
|
|
542
|
+
sharedDirectory: appContext?.sharedDirectory || external_path_default().resolve(appDirectory, utils_namespaceObject.SHARED_DIR),
|
|
543
|
+
distDirectory: external_path_default().join(appDirectory, config.output.path || 'dist'),
|
|
544
|
+
plugins: serverPlugins
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
async loadServerEnv(options) {
|
|
548
|
+
const { pwd: appDirectory } = options;
|
|
549
|
+
const serverEnv = process.env.MODERN_ENV;
|
|
550
|
+
const defaultEnvPath = external_path_default().resolve(appDirectory, ".env");
|
|
551
|
+
const serverEnvPath = external_path_default().resolve(appDirectory, `.env.${serverEnv}`);
|
|
552
|
+
for (const envPath of [
|
|
553
|
+
serverEnvPath,
|
|
554
|
+
defaultEnvPath
|
|
555
|
+
])if (await utils_namespaceObject.fs.pathExists(envPath) && !(await utils_namespaceObject.fs.stat(envPath)).isDirectory()) {
|
|
556
|
+
const envConfig = utils_namespaceObject.dotenv.config({
|
|
557
|
+
path: envPath
|
|
558
|
+
});
|
|
559
|
+
(0, utils_namespaceObject.dotenvExpand)(envConfig);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
constructor(options){
|
|
563
|
+
this.serverImpl = external_modernServerSplit_js_namespaceObject.createProdServer;
|
|
564
|
+
this.runtimeStatusEndpoint = telemetry_js_namespaceObject.DEFAULT_RUNTIME_STATUS_ENDPOINT;
|
|
565
|
+
options.logger = options.logger || (0, utils_namespaceObject.createLogger)({
|
|
566
|
+
level: 'warn'
|
|
567
|
+
});
|
|
568
|
+
options.metrics = options.metrics || metrics_js_namespaceObject.metrics;
|
|
569
|
+
this.options = options;
|
|
570
|
+
this.serverConfig = {};
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
exports.Server = __webpack_exports__.Server;
|
|
574
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
575
|
+
"Server"
|
|
576
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
577
|
+
Object.defineProperty(exports, '__esModule', {
|
|
578
|
+
value: true
|
|
579
|
+
});
|