react-on-rails-pro-node-renderer 17.0.0-rc.2 → 17.0.0-rc.4
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/master.js +169 -29
- package/lib/shared/checkRscPeerCompatibility.js +24 -6
- package/lib/shared/configBuilder.d.ts +1 -0
- package/lib/shared/configBuilder.js +9 -0
- package/lib/shared/rscPeerSupport.d.ts +9 -2
- package/lib/shared/rscPeerSupport.js +8 -1
- package/lib/shared/utils.d.ts +7 -1
- package/lib/shared/utils.js +21 -5
- package/lib/worker/handleIncrementalRenderRequest.d.ts +3 -1
- package/lib/worker/handleIncrementalRenderRequest.js +10 -5
- package/lib/worker/vm.d.ts +16 -0
- package/lib/worker/vm.js +167 -13
- package/lib/worker/vmSourceMapSupport.d.ts +110 -0
- package/lib/worker/vmSourceMapSupport.js +664 -0
- package/lib/worker.d.ts +9 -1
- package/lib/worker.js +336 -12
- package/package.json +5 -2
package/lib/worker/vm.js
CHANGED
|
@@ -53,6 +53,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
53
53
|
exports.VMContextNotFoundError = void 0;
|
|
54
54
|
exports.hasVMContextForBundle = hasVMContextForBundle;
|
|
55
55
|
exports.getVMContext = getVMContext;
|
|
56
|
+
exports.hasAnyVMContext = hasAnyVMContext;
|
|
56
57
|
exports.buildExecutionContext = buildExecutionContext;
|
|
57
58
|
exports.resetVM = resetVM;
|
|
58
59
|
exports.removeVM = removeVM;
|
|
@@ -72,12 +73,55 @@ const log_js_1 = __importDefault(require("../shared/log.js"));
|
|
|
72
73
|
const configBuilder_js_1 = require("../shared/configBuilder.js");
|
|
73
74
|
const utils_js_1 = require("../shared/utils.js");
|
|
74
75
|
const errorReporter = __importStar(require("../shared/errorReporter.js"));
|
|
76
|
+
const vmSourceMapSupport_js_1 = require("./vmSourceMapSupport.js");
|
|
75
77
|
const readFileAsync = (0, util_1.promisify)(fs_1.default.readFile);
|
|
78
|
+
// Length of the `Module.wrap` prefix that is prepended to the first line of a
|
|
79
|
+
// wrapped bundle. Needed to correct first-line stack-frame columns before
|
|
80
|
+
// source map lookups.
|
|
81
|
+
const MODULE_WRAP_FIRST_LINE_PREFIX_LENGTH = module_1.default.wrap('\n').indexOf('\n');
|
|
76
82
|
const writeFileAsync = (0, util_1.promisify)(fs_1.default.writeFile);
|
|
77
83
|
// Store contexts by their bundle file paths
|
|
78
84
|
const vmContexts = new Map();
|
|
79
85
|
// Track VM creation promises to handle concurrent buildVM requests
|
|
80
86
|
const vmCreationPromises = new Map();
|
|
87
|
+
// Execution contexts can outlive VM pool entries while a request is still
|
|
88
|
+
// running. Keep source maps for those evicted contexts until the request
|
|
89
|
+
// releases them, then drop both the registration and parsed-map cache.
|
|
90
|
+
const activeSourceMapRequestCounts = new Map();
|
|
91
|
+
const evictedSourceMapRegistrations = new Set();
|
|
92
|
+
function retainSourceMapRegistration(sourceMapRegistration) {
|
|
93
|
+
activeSourceMapRequestCounts.set(sourceMapRegistration, (activeSourceMapRequestCounts.get(sourceMapRegistration) ?? 0) + 1);
|
|
94
|
+
}
|
|
95
|
+
function releaseSourceMapRegistration(sourceMapRegistration) {
|
|
96
|
+
const currentCount = activeSourceMapRequestCounts.get(sourceMapRegistration) ?? 0;
|
|
97
|
+
if (currentCount <= 1) {
|
|
98
|
+
activeSourceMapRequestCounts.delete(sourceMapRegistration);
|
|
99
|
+
const wasEvicted = evictedSourceMapRegistrations.delete(sourceMapRegistration);
|
|
100
|
+
// Eviction deletes the VM first today; keep the guard so future callers do
|
|
101
|
+
// not unregister a source map for a live pooled VM.
|
|
102
|
+
let isStillInPool = false;
|
|
103
|
+
for (const vmContext of vmContexts.values()) {
|
|
104
|
+
if (vmContext.sourceMapRegistration === sourceMapRegistration) {
|
|
105
|
+
isStillInPool = true;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (wasEvicted && !isStillInPool) {
|
|
110
|
+
(0, vmSourceMapSupport_js_1.unregisterBundleForSourceMaps)(sourceMapRegistration);
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
activeSourceMapRequestCounts.set(sourceMapRegistration, currentCount - 1);
|
|
115
|
+
}
|
|
116
|
+
function retireSourceMapRegistrationAfterEviction(sourceMapRegistration) {
|
|
117
|
+
(0, vmSourceMapSupport_js_1.retireMissingSourceMapRetry)(sourceMapRegistration);
|
|
118
|
+
if ((activeSourceMapRequestCounts.get(sourceMapRegistration) ?? 0) > 0) {
|
|
119
|
+
evictedSourceMapRegistrations.add(sourceMapRegistration);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
(0, vmSourceMapSupport_js_1.unregisterBundleForSourceMaps)(sourceMapRegistration);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
81
125
|
/**
|
|
82
126
|
* Returns all bundle paths that have a VM context
|
|
83
127
|
* @internal Used in tests
|
|
@@ -91,12 +135,35 @@ function hasVMContextForBundle(bundlePath) {
|
|
|
91
135
|
function getVMContext(bundlePath) {
|
|
92
136
|
return vmContexts.get(bundlePath);
|
|
93
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Whether this worker has at least one bundle compiled into a VM context.
|
|
140
|
+
* Used by the built-in /ready readiness endpoint: a worker with zero loaded
|
|
141
|
+
* bundles cannot serve render requests until a bundle is uploaded.
|
|
142
|
+
*
|
|
143
|
+
* This intentionally stays false while a bundle is still compiling in
|
|
144
|
+
* vmCreationPromises; /ready flips to 200 only after compilation finishes and
|
|
145
|
+
* the compiled context is stored in vmContexts.
|
|
146
|
+
*
|
|
147
|
+
* Pool eviction can remove older bundle contexts, but readiness remains true as
|
|
148
|
+
* long as at least one compiled bundle remains in the pool.
|
|
149
|
+
*/
|
|
150
|
+
function hasAnyVMContext() {
|
|
151
|
+
return vmContexts.size > 0;
|
|
152
|
+
}
|
|
94
153
|
const extendContext = (contextObject, additionalContext) => {
|
|
95
154
|
if (log_js_1.default.level === 'debug') {
|
|
96
155
|
log_js_1.default.debug(`Adding ${Object.keys(additionalContext).join(', ')} to context object.`);
|
|
97
156
|
}
|
|
98
157
|
Object.assign(contextObject, additionalContext);
|
|
99
158
|
};
|
|
159
|
+
const readPrepareStackTraceHook = (context) => {
|
|
160
|
+
try {
|
|
161
|
+
return vm_1.default.runInContext('typeof Error === "undefined" ? undefined : Error.prepareStackTrace', context);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
100
167
|
// Helper function to manage VM pool size
|
|
101
168
|
function manageVMPoolSize() {
|
|
102
169
|
const { maxVMPoolSize } = (0, configBuilder_js_1.getConfig)();
|
|
@@ -105,9 +172,11 @@ function manageVMPoolSize() {
|
|
|
105
172
|
}
|
|
106
173
|
const sortedEntries = Array.from(vmContexts.entries()).sort(([, a], [, b]) => a.lastUsed - b.lastUsed);
|
|
107
174
|
while (sortedEntries.length > maxVMPoolSize) {
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
175
|
+
const oldestEntry = sortedEntries.shift();
|
|
176
|
+
if (oldestEntry) {
|
|
177
|
+
const [oldestPath, oldestContext] = oldestEntry;
|
|
110
178
|
vmContexts.delete(oldestPath);
|
|
179
|
+
retireSourceMapRegistrationAfterEviction(oldestContext.sourceMapRegistration);
|
|
111
180
|
log_js_1.default.debug(`Removed VM for bundle ${oldestPath} due to pool size limit (max: ${maxVMPoolSize})`);
|
|
112
181
|
}
|
|
113
182
|
}
|
|
@@ -139,12 +208,29 @@ async function buildVM(filePath) {
|
|
|
139
208
|
// than a try/finally inside the IIFE, because an IIFE's finally block can
|
|
140
209
|
// execute synchronously (before `vmCreationPromises.set`) when the code throws
|
|
141
210
|
// before the first `await`, which would leave a stale rejected promise in the map.
|
|
211
|
+
let sourceMapRegistration;
|
|
142
212
|
const vmCreationPromise = (async () => {
|
|
143
213
|
try {
|
|
144
214
|
const { supportModules, stubTimers, additionalContext } = (0, configBuilder_js_1.getConfig)();
|
|
145
215
|
const additionalContextIsObject = additionalContext !== null && additionalContext.constructor === Object;
|
|
146
216
|
const sharedConsoleHistory = new sharedConsoleHistory_js_1.default();
|
|
147
|
-
|
|
217
|
+
// Request-derived bundle paths are built from validated timestamp path components.
|
|
218
|
+
// Direct `buildExecutionContext` callers pass trusted internal bundle paths.
|
|
219
|
+
// codeql[js/path-injection]
|
|
220
|
+
const bundleContents = await readFileAsync(filePath, 'utf8');
|
|
221
|
+
const firstLineColumnOffset = additionalContextIsObject || supportModules ? MODULE_WRAP_FIRST_LINE_PREFIX_LENGTH : 0;
|
|
222
|
+
const preloadedSourceMap = await (0, vmSourceMapSupport_js_1.preloadSourceMapJsonForBundle)(filePath, bundleContents);
|
|
223
|
+
const currentSourceMapRegistration = (0, vmSourceMapSupport_js_1.registerBundleForSourceMaps)(filePath, firstLineColumnOffset, bundleContents, preloadedSourceMap.sourceMapJson, preloadedSourceMap.retryMissingSourceMap);
|
|
224
|
+
sourceMapRegistration = currentSourceMapRegistration;
|
|
225
|
+
// Host callback used by the in-VM `Error.prepareStackTrace` hook (see
|
|
226
|
+
// vmSourceMapSupport.ts) to remap bundle stack frames to original
|
|
227
|
+
// TS/JS sources. Only resolves positions for registered bundle paths.
|
|
228
|
+
const contextObject = {
|
|
229
|
+
sharedConsoleHistory,
|
|
230
|
+
[vmSourceMapSupport_js_1.SOURCE_MAP_LOOKUP_ATTEMPT_CONTEXT_KEY]: vmSourceMapSupport_js_1.createSourceMapLookupAttempt,
|
|
231
|
+
[vmSourceMapSupport_js_1.SOURCE_MAP_RESOLVER_CONTEXT_KEY]: (fileName, lineNumber, columnNumber, lookupAttempt) => (0, vmSourceMapSupport_js_1.resolveOriginalPositionForRegistration)(currentSourceMapRegistration, fileName, lineNumber, columnNumber, lookupAttempt),
|
|
232
|
+
[vmSourceMapSupport_js_1.SOURCE_MAP_STACK_REMAPPER_CONTEXT_KEY]: (stack) => (0, vmSourceMapSupport_js_1.remapStackTrace)(stack, currentSourceMapRegistration),
|
|
233
|
+
};
|
|
148
234
|
if (supportModules) {
|
|
149
235
|
// IMPORTANT: When adding anything to this object, update:
|
|
150
236
|
// 1. docs/oss/building-features/node-renderer/js-configuration.md
|
|
@@ -217,20 +303,29 @@ async function buildVM(filePath) {
|
|
|
217
303
|
vm_1.default.runInContext(`function clearImmediate() {}`, context);
|
|
218
304
|
vm_1.default.runInContext(`function queueMicrotask() {}`, context);
|
|
219
305
|
}
|
|
220
|
-
//
|
|
221
|
-
|
|
306
|
+
// Install lazy source-mapped stack traces for errors created inside the
|
|
307
|
+
// VM. Must run before the bundle is evaluated so the bundle's own error
|
|
308
|
+
// handling (e.g. react-on-rails `handleError`) sees remapped stacks.
|
|
309
|
+
vm_1.default.runInContext(vmSourceMapSupport_js_1.PREPARE_STACK_TRACE_INSTALL_SCRIPT, context);
|
|
310
|
+
const installedPrepareStackTrace = readPrepareStackTraceHook(context);
|
|
222
311
|
// If node-specific code is provided then it must be wrapped into a module wrapper. The bundle
|
|
223
312
|
// may need the `require` function, which is not available when running in vm unless passed in.
|
|
313
|
+
// Pass `filename` so stack frames point at the real bundle path (instead of
|
|
314
|
+
// `evalmachine.<anonymous>`), which also keys lazy source map resolution.
|
|
224
315
|
if (additionalContextIsObject || supportModules) {
|
|
225
|
-
vm_1.default.runInContext(module_1.default.wrap(bundleContents), context)(exports, require, module, filePath, path_1.default.dirname(filePath));
|
|
316
|
+
vm_1.default.runInContext(module_1.default.wrap(bundleContents), context, { filename: filePath })(exports, require, module, filePath, path_1.default.dirname(filePath));
|
|
226
317
|
}
|
|
227
318
|
else {
|
|
228
|
-
vm_1.default.runInContext(bundleContents, context);
|
|
319
|
+
vm_1.default.runInContext(bundleContents, context, { filename: filePath });
|
|
320
|
+
}
|
|
321
|
+
if (readPrepareStackTraceHook(context) !== installedPrepareStackTrace) {
|
|
322
|
+
log_js_1.default.warn('Bundle replaced Error.prepareStackTrace; source-mapped stack traces are disabled for bundle %s', filePath);
|
|
229
323
|
}
|
|
230
324
|
// Only now, after VM is fully initialized, store the context
|
|
231
325
|
const newVmContext = {
|
|
232
326
|
context,
|
|
233
327
|
sharedConsoleHistory,
|
|
328
|
+
sourceMapRegistration: currentSourceMapRegistration,
|
|
234
329
|
lastUsed: Date.now(),
|
|
235
330
|
};
|
|
236
331
|
vmContexts.set(filePath, newVmContext);
|
|
@@ -247,8 +342,18 @@ async function buildVM(filePath) {
|
|
|
247
342
|
return newVmContext;
|
|
248
343
|
}
|
|
249
344
|
catch (error) {
|
|
345
|
+
// Materialize/remap the stack before reporting so failed bundle builds
|
|
346
|
+
// still include original source locations, then retire the registration:
|
|
347
|
+
// failed builds never enter the VM pool and cannot be reused.
|
|
348
|
+
(0, vmSourceMapSupport_js_1.remapErrorStack)(error, sourceMapRegistration);
|
|
250
349
|
log_js_1.default.error({ error }, 'Caught Error when creating context in buildVM');
|
|
251
350
|
errorReporter.error(error);
|
|
351
|
+
if (sourceMapRegistration) {
|
|
352
|
+
(0, vmSourceMapSupport_js_1.unregisterBundleForSourceMaps)(sourceMapRegistration);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
(0, vmSourceMapSupport_js_1.unregisterBundleForSourceMaps)(filePath);
|
|
356
|
+
}
|
|
252
357
|
throw error;
|
|
253
358
|
}
|
|
254
359
|
})();
|
|
@@ -305,23 +410,53 @@ async function getOrBuildVMContext(bundleFilePath, buildVmsIfNeeded) {
|
|
|
305
410
|
* @see handleIncrementalRenderRequest.ts for how update chunks access the same context
|
|
306
411
|
*/
|
|
307
412
|
async function buildExecutionContext(bundlePaths, buildVmsIfNeeded) {
|
|
413
|
+
const retainedSourceMapRegistrations = new Set();
|
|
414
|
+
const retainSourceMapRegistrationOnce = (sourceMapRegistration) => {
|
|
415
|
+
if (retainedSourceMapRegistrations.has(sourceMapRegistration)) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
retainSourceMapRegistration(sourceMapRegistration);
|
|
419
|
+
retainedSourceMapRegistrations.add(sourceMapRegistration);
|
|
420
|
+
};
|
|
308
421
|
const mapBundleFilePathToVMContext = new Map();
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
422
|
+
let buildRejected = false;
|
|
423
|
+
let firstBuildRejection;
|
|
424
|
+
// Wait for every parallel build callback before releasing retained source-map
|
|
425
|
+
// registrations; otherwise a sibling build can retain after an early rejection.
|
|
426
|
+
await Promise.allSettled(bundlePaths.map(async (bundleFilePath) => {
|
|
427
|
+
try {
|
|
428
|
+
const vmContext = await getOrBuildVMContext(bundleFilePath, buildVmsIfNeeded);
|
|
429
|
+
retainSourceMapRegistrationOnce(vmContext.sourceMapRegistration);
|
|
430
|
+
vmContext.lastUsed = Date.now();
|
|
431
|
+
mapBundleFilePathToVMContext.set(bundleFilePath, vmContext);
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
if (!buildRejected) {
|
|
435
|
+
buildRejected = true;
|
|
436
|
+
firstBuildRejection = error;
|
|
437
|
+
}
|
|
438
|
+
throw error;
|
|
439
|
+
}
|
|
313
440
|
}));
|
|
441
|
+
if (buildRejected) {
|
|
442
|
+
retainedSourceMapRegistrations.forEach(releaseSourceMapRegistration);
|
|
443
|
+
throw firstBuildRejection;
|
|
444
|
+
}
|
|
314
445
|
// This Map persists for the lifetime of this ExecutionContext (one HTTP request).
|
|
315
446
|
// It allows data to be shared between the initial render and subsequent update chunks.
|
|
316
447
|
// Example: asyncPropsManager is stored here during initial render and accessed by update chunks.
|
|
317
448
|
const sharedExecutionContext = new Map();
|
|
449
|
+
let released = false;
|
|
318
450
|
const runInVM = async (renderingRequest, bundleFilePath, vmCluster) => {
|
|
451
|
+
let sourceMapRegistrationForRequest;
|
|
319
452
|
try {
|
|
320
453
|
const { serverBundleCachePath } = (0, configBuilder_js_1.getConfig)();
|
|
321
454
|
const vmContext = mapBundleFilePathToVMContext.get(bundleFilePath);
|
|
322
455
|
if (!vmContext) {
|
|
323
456
|
throw new VMContextNotFoundError(bundleFilePath);
|
|
324
457
|
}
|
|
458
|
+
sourceMapRegistrationForRequest = vmContext.sourceMapRegistration;
|
|
459
|
+
const remapStackTraceForRequest = (stack) => (0, vmSourceMapSupport_js_1.remapStackTrace)(stack, vmContext.sourceMapRegistration);
|
|
325
460
|
// Update last used timestamp
|
|
326
461
|
vmContext.lastUsed = Date.now();
|
|
327
462
|
const { context, sharedConsoleHistory } = vmContext;
|
|
@@ -361,7 +496,7 @@ async function buildExecutionContext(bundlePaths, buildVmsIfNeeded) {
|
|
|
361
496
|
});
|
|
362
497
|
if ((0, utils_js_1.isReadableStream)(result)) {
|
|
363
498
|
const newStreamAfterHandlingError = (0, utils_js_1.handleStreamError)(result, (error) => {
|
|
364
|
-
const msg = (0, utils_js_1.formatExceptionMessage)({ renderingRequest }, error, 'Error in a rendering stream');
|
|
499
|
+
const msg = (0, utils_js_1.formatExceptionMessage)({ renderingRequest }, error, 'Error in a rendering stream', remapStackTraceForRequest);
|
|
365
500
|
errorReporter.message(msg);
|
|
366
501
|
});
|
|
367
502
|
return newStreamAfterHandlingError;
|
|
@@ -382,7 +517,9 @@ async function buildExecutionContext(bundlePaths, buildVmsIfNeeded) {
|
|
|
382
517
|
return result;
|
|
383
518
|
}
|
|
384
519
|
catch (exception) {
|
|
385
|
-
const exceptionMessage = (0, utils_js_1.formatExceptionMessage)({ renderingRequest }, exception
|
|
520
|
+
const exceptionMessage = (0, utils_js_1.formatExceptionMessage)({ renderingRequest }, exception, undefined, sourceMapRegistrationForRequest
|
|
521
|
+
? (stack) => (0, vmSourceMapSupport_js_1.remapStackTrace)(stack, sourceMapRegistrationForRequest)
|
|
522
|
+
: (_stack) => undefined);
|
|
386
523
|
log_js_1.default.debug('Caught exception in rendering request: %s', exceptionMessage);
|
|
387
524
|
return Promise.resolve({ exceptionMessage });
|
|
388
525
|
}
|
|
@@ -390,19 +527,36 @@ async function buildExecutionContext(bundlePaths, buildVmsIfNeeded) {
|
|
|
390
527
|
return {
|
|
391
528
|
getVMContext: (bundleFilePath) => mapBundleFilePathToVMContext.get(bundleFilePath),
|
|
392
529
|
runInVM,
|
|
530
|
+
release: () => {
|
|
531
|
+
if (released) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
released = true;
|
|
535
|
+
retainedSourceMapRegistrations.forEach(releaseSourceMapRegistration);
|
|
536
|
+
},
|
|
393
537
|
};
|
|
394
538
|
}
|
|
395
539
|
/** @internal Used in tests */
|
|
396
540
|
function resetVM() {
|
|
397
541
|
vmContexts.clear();
|
|
398
542
|
vmCreationPromises.clear();
|
|
543
|
+
activeSourceMapRequestCounts.clear();
|
|
544
|
+
evictedSourceMapRegistrations.clear();
|
|
545
|
+
(0, vmSourceMapSupport_js_1.resetSourceMapSupport)();
|
|
399
546
|
}
|
|
400
547
|
// Optional: Add a method to remove a specific VM if needed
|
|
401
548
|
/**
|
|
402
549
|
* @public TODO: Remove the line below when this function is actually used
|
|
403
550
|
*/
|
|
404
551
|
function removeVM(bundlePath) {
|
|
552
|
+
const vmContext = vmContexts.get(bundlePath);
|
|
405
553
|
vmContexts.delete(bundlePath);
|
|
406
554
|
vmCreationPromises.delete(bundlePath);
|
|
555
|
+
if (vmContext) {
|
|
556
|
+
retireSourceMapRegistrationAfterEviction(vmContext.sourceMapRegistration);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
(0, vmSourceMapSupport_js_1.unregisterBundleForSourceMaps)(bundlePath);
|
|
560
|
+
}
|
|
407
561
|
}
|
|
408
562
|
//# sourceMappingURL=vm.js.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export interface BundleSourceMapRegistration {
|
|
2
|
+
bundleFilePath: string;
|
|
3
|
+
/**
|
|
4
|
+
* Column correction for frames on line 1 of the bundle. When the bundle is
|
|
5
|
+
* evaluated wrapped in `Module.wrap` (the `supportModules` /
|
|
6
|
+
* `additionalContext` path), the wrapper prefix shifts every first-line
|
|
7
|
+
* column by its length.
|
|
8
|
+
*/
|
|
9
|
+
firstLineColumnOffset: number;
|
|
10
|
+
/**
|
|
11
|
+
* `undefined` means the bundle text was not available at registration time
|
|
12
|
+
* and must be checked lazily. `null` means it was checked and no
|
|
13
|
+
* sourceMappingURL comment was present.
|
|
14
|
+
*/
|
|
15
|
+
sourceMappingUrl?: string | null;
|
|
16
|
+
/**
|
|
17
|
+
* `undefined` keeps lazy synchronous lookup behavior. `null` means registration
|
|
18
|
+
* time lookup found no usable source map. A string freezes the source-map JSON
|
|
19
|
+
* for this VM generation so same-path rebuilds cannot remap old bytecode with
|
|
20
|
+
* a newer map.
|
|
21
|
+
*/
|
|
22
|
+
sourceMapJson?: string | null;
|
|
23
|
+
realBundleDirectory: string;
|
|
24
|
+
/**
|
|
25
|
+
* External source maps can arrive after a bundle first becomes visible. When
|
|
26
|
+
* true, a missing external map is retried briefly before caching the miss.
|
|
27
|
+
*/
|
|
28
|
+
retryMissingSourceMap?: boolean;
|
|
29
|
+
}
|
|
30
|
+
interface SourceMapLookupAttempt {
|
|
31
|
+
missingSourceMaps: WeakSet<BundleSourceMapRegistration>;
|
|
32
|
+
}
|
|
33
|
+
export declare function retireMissingSourceMapRetry(registration: BundleSourceMapRegistration): void;
|
|
34
|
+
export declare function createSourceMapLookupAttempt(): SourceMapLookupAttempt;
|
|
35
|
+
/**
|
|
36
|
+
* Name of the host-callback global injected into the VM context. Used by
|
|
37
|
+
* {@link PREPARE_STACK_TRACE_INSTALL_SCRIPT}.
|
|
38
|
+
*/
|
|
39
|
+
export declare const SOURCE_MAP_RESOLVER_CONTEXT_KEY = "__reactOnRailsProResolveOriginalSourcePosition";
|
|
40
|
+
/**
|
|
41
|
+
* Name of the host-callback global used to group resolver calls that belong to
|
|
42
|
+
* one `.stack` formatting attempt.
|
|
43
|
+
*/
|
|
44
|
+
export declare const SOURCE_MAP_LOOKUP_ATTEMPT_CONTEXT_KEY = "__reactOnRailsProCreateSourceMapLookupAttempt";
|
|
45
|
+
/**
|
|
46
|
+
* Name of the host-callback global injected into the VM context for stacks
|
|
47
|
+
* serialized by react-on-rails before they can escape to the host formatter.
|
|
48
|
+
*/
|
|
49
|
+
export declare const SOURCE_MAP_STACK_REMAPPER_CONTEXT_KEY = "__reactOnRailsProRemapStackTrace";
|
|
50
|
+
/** @internal Used by VM build to avoid synchronous external-map reads. */
|
|
51
|
+
export declare function preloadSourceMapJsonForBundle(bundleFilePath: string, bundleContents: string): Promise<{
|
|
52
|
+
retryMissingSourceMap: boolean;
|
|
53
|
+
sourceMapJson: string | null;
|
|
54
|
+
} | {
|
|
55
|
+
retryMissingSourceMap: boolean;
|
|
56
|
+
sourceMapJson: string | undefined;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Registers a bundle file so stack frames pointing into it can be remapped.
|
|
60
|
+
* Pass `bundleContents` when available; omitting it makes the first error-path
|
|
61
|
+
* lookup synchronously re-read the bundle to find its `sourceMappingURL`.
|
|
62
|
+
* `preloadedSourceMapJson` supplies known JSON/null for this VM generation;
|
|
63
|
+
* `retryMissingSourceMap` keeps external preload misses retryable until a map is
|
|
64
|
+
* found, the retry cap is reached, or a same-path generation replaces the registration.
|
|
65
|
+
*/
|
|
66
|
+
export declare function registerBundleForSourceMaps(bundleFilePath: string, firstLineColumnOffset?: number, bundleContents?: string, preloadedSourceMapJson?: string | null, retryMissingSourceMap?: boolean): {
|
|
67
|
+
bundleFilePath: string;
|
|
68
|
+
firstLineColumnOffset: number;
|
|
69
|
+
realBundleDirectory: string;
|
|
70
|
+
sourceMappingUrl: string | null | undefined;
|
|
71
|
+
sourceMapJson: string | null | undefined;
|
|
72
|
+
retryMissingSourceMap: boolean;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Drops the registration and any cached source map for a bundle.
|
|
76
|
+
*/
|
|
77
|
+
export declare function unregisterBundleForSourceMaps(bundleOrRegistration: string | BundleSourceMapRegistration): void;
|
|
78
|
+
/** @internal Used in tests */
|
|
79
|
+
export declare function resetSourceMapSupport(): void;
|
|
80
|
+
export interface ResolvedSourcePosition {
|
|
81
|
+
source: string;
|
|
82
|
+
line: number;
|
|
83
|
+
column: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resolves a 1-based generated position in a registered bundle to its original
|
|
87
|
+
* source position. Returns null when the position cannot be mapped.
|
|
88
|
+
*
|
|
89
|
+
* SECURITY: this function is exposed to untrusted bundle code inside the VM
|
|
90
|
+
* context, so it validates its inputs and only operates on registered bundle
|
|
91
|
+
* paths.
|
|
92
|
+
*
|
|
93
|
+
* @param lineNumber 1-based generated line number, matching V8 CallSite values.
|
|
94
|
+
* @param columnNumber 1-based generated column number, matching V8 CallSite values.
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveOriginalPositionForRegistration(registration: BundleSourceMapRegistration, fileName: unknown, lineNumber: unknown, columnNumber: unknown, lookupAttempt?: unknown): ResolvedSourcePosition | null;
|
|
97
|
+
export declare function remapStackTrace(stack: unknown, registration?: BundleSourceMapRegistration): string | undefined;
|
|
98
|
+
export declare function remapErrorStack(error: unknown, registration?: BundleSourceMapRegistration): void;
|
|
99
|
+
/**
|
|
100
|
+
* Script run inside the VM context (before the bundle is evaluated) that
|
|
101
|
+
* installs an `Error.prepareStackTrace` mirroring V8's default format but
|
|
102
|
+
* rewriting `file:line:column` locations through the host-side resolver when a
|
|
103
|
+
* source-mapped position is available. Bundle code can replace
|
|
104
|
+
* `Error.prepareStackTrace` after this script runs; if it does, source mapping
|
|
105
|
+
* is disabled for that VM context. Any failure falls back to the original frame
|
|
106
|
+
* text.
|
|
107
|
+
*/
|
|
108
|
+
export declare const PREPARE_STACK_TRACE_INSTALL_SCRIPT = "\nError.prepareStackTrace = function (error, callSites) {\n var header;\n try {\n header = String(error);\n } catch (formatError) {\n header = '<error>';\n }\n var parts = [header];\n var sourceMapLookupAttempt;\n try {\n if (typeof __reactOnRailsProCreateSourceMapLookupAttempt === 'function') {\n sourceMapLookupAttempt = __reactOnRailsProCreateSourceMapLookupAttempt();\n }\n } catch (lookupAttemptError) {\n sourceMapLookupAttempt = undefined;\n }\n for (var i = 0; i < callSites.length; i++) {\n var frameText;\n try {\n frameText = String(callSites[i]);\n } catch (formatFrameError) {\n frameText = '<frame>';\n }\n try {\n var fileName = callSites[i].getFileName();\n var line = callSites[i].getLineNumber();\n var column = callSites[i].getColumnNumber();\n if (\n fileName != null &&\n line != null &&\n column != null &&\n typeof __reactOnRailsProResolveOriginalSourcePosition === 'function'\n ) {\n var position = __reactOnRailsProResolveOriginalSourcePosition(fileName, line, column, sourceMapLookupAttempt);\n if (position) {\n var generatedLocation = fileName + ':' + line + ':' + column;\n var originalLocation = position.source + ':' + position.line + ':' + position.column;\n if (frameText.indexOf(generatedLocation) !== -1) {\n // String first-arg: replace only the frame's canonical location once.\n // Do not use a /g regex here; a repeated numeric location elsewhere\n // in the frame text should not be rewritten.\n frameText = frameText.replace(generatedLocation, function () { return originalLocation; });\n } else {\n frameText = frameText + ' -> ' + originalLocation;\n }\n }\n }\n } catch (resolveError) {\n // Keep the original frame text.\n }\n parts.push(' at ' + frameText);\n }\n return parts.join('\\n');\n};\n";
|
|
109
|
+
export {};
|
|
110
|
+
//# sourceMappingURL=vmSourceMapSupport.d.ts.map
|