.
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
import type { Segment as FlightRouterStateSegment } from '../app-router-types';
|
||||
type Opaque<K, T> = T & {
|
||||
__brand: K;
|
||||
};
|
||||
export type SegmentRequestKeyPart = Opaque<'SegmentRequestKeyPart', string>;
|
||||
export type SegmentRequestKey = Opaque<'SegmentRequestKey', string>;
|
||||
export declare const ROOT_SEGMENT_REQUEST_KEY: SegmentRequestKey;
|
||||
export declare const HEAD_REQUEST_KEY: SegmentRequestKey;
|
||||
export declare function createSegmentRequestKeyPart(segment: FlightRouterStateSegment): SegmentRequestKeyPart;
|
||||
export declare function appendSegmentRequestKeyPart(parentRequestKey: SegmentRequestKey, parallelRouteKey: string, childRequestKeyPart: SegmentRequestKeyPart): SegmentRequestKey;
|
||||
export declare function convertSegmentPathToStaticExportFilename(segmentPath: string): string;
|
||||
export {};
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
0 && (module.exports = {
|
||||
HEAD_REQUEST_KEY: null,
|
||||
ROOT_SEGMENT_REQUEST_KEY: null,
|
||||
appendSegmentRequestKeyPart: null,
|
||||
convertSegmentPathToStaticExportFilename: null,
|
||||
createSegmentRequestKeyPart: null
|
||||
});
|
||||
function _export(target, all) {
|
||||
for(var name in all)Object.defineProperty(target, name, {
|
||||
enumerable: true,
|
||||
get: all[name]
|
||||
});
|
||||
}
|
||||
_export(exports, {
|
||||
HEAD_REQUEST_KEY: function() {
|
||||
return HEAD_REQUEST_KEY;
|
||||
},
|
||||
ROOT_SEGMENT_REQUEST_KEY: function() {
|
||||
return ROOT_SEGMENT_REQUEST_KEY;
|
||||
},
|
||||
appendSegmentRequestKeyPart: function() {
|
||||
return appendSegmentRequestKeyPart;
|
||||
},
|
||||
convertSegmentPathToStaticExportFilename: function() {
|
||||
return convertSegmentPathToStaticExportFilename;
|
||||
},
|
||||
createSegmentRequestKeyPart: function() {
|
||||
return createSegmentRequestKeyPart;
|
||||
}
|
||||
});
|
||||
const _segment = require("../segment");
|
||||
const ROOT_SEGMENT_REQUEST_KEY = '';
|
||||
const HEAD_REQUEST_KEY = '/_head';
|
||||
function createSegmentRequestKeyPart(segment) {
|
||||
if (typeof segment === 'string') {
|
||||
if (segment.startsWith(_segment.PAGE_SEGMENT_KEY)) {
|
||||
// The Flight Router State type sometimes includes the search params in
|
||||
// the page segment. However, the Segment Cache tracks this as a separate
|
||||
// key. So, we strip the search params here, and then add them back when
|
||||
// the cache entry is turned back into a FlightRouterState. This is an
|
||||
// unfortunate consequence of the FlightRouteState being used both as a
|
||||
// transport type and as a cache key; we'll address this once more of the
|
||||
// Segment Cache implementation has settled.
|
||||
// TODO: We should hoist the search params out of the FlightRouterState
|
||||
// type entirely, This is our plan for dynamic route params, too.
|
||||
return _segment.PAGE_SEGMENT_KEY;
|
||||
}
|
||||
const safeName = // TODO: FlightRouterState encodes Not Found routes as "/_not-found".
|
||||
// But params typically don't include the leading slash. We should use
|
||||
// a different encoding to avoid this special case.
|
||||
segment === '/_not-found' ? '_not-found' : encodeToFilesystemAndURLSafeString(segment);
|
||||
// Since this is not a dynamic segment, it's fully encoded. It does not
|
||||
// need to be "hydrated" with a param value.
|
||||
return safeName;
|
||||
}
|
||||
const name = segment[0];
|
||||
const paramType = segment[2];
|
||||
const safeName = encodeToFilesystemAndURLSafeString(name);
|
||||
const encodedName = '$' + paramType + '$' + safeName;
|
||||
return encodedName;
|
||||
}
|
||||
function appendSegmentRequestKeyPart(parentRequestKey, parallelRouteKey, childRequestKeyPart) {
|
||||
// Aside from being filesystem safe, segment keys are also designed so that
|
||||
// each segment and parallel route creates its own subdirectory. Roughly in
|
||||
// the same shape as the source app directory. This is mostly just for easier
|
||||
// debugging (you can open up the build folder and navigate the output); if
|
||||
// we wanted to do we could just use a flat structure.
|
||||
// Omit the parallel route key for children, since this is the most
|
||||
// common case. Saves some bytes (and it's what the app directory does).
|
||||
const slotKey = parallelRouteKey === 'children' ? childRequestKeyPart : `@${encodeToFilesystemAndURLSafeString(parallelRouteKey)}/${childRequestKeyPart}`;
|
||||
return parentRequestKey + '/' + slotKey;
|
||||
}
|
||||
// Define a regex pattern to match the most common characters found in a route
|
||||
// param. It excludes anything that might not be cross-platform filesystem
|
||||
// compatible, like |. It does not need to be precise because the fallback is to
|
||||
// just base64url-encode the whole parameter, which is fine; we just don't do it
|
||||
// by default for compactness, and for easier debugging.
|
||||
const simpleParamValueRegex = /^[a-zA-Z0-9\-_@]+$/;
|
||||
function encodeToFilesystemAndURLSafeString(value) {
|
||||
if (simpleParamValueRegex.test(value)) {
|
||||
return value;
|
||||
}
|
||||
// If there are any unsafe characters, base64url-encode the entire value.
|
||||
// We also add a ! prefix so it doesn't collide with the simple case.
|
||||
const base64url = btoa(value).replace(/\+/g, '-') // Replace '+' with '-'
|
||||
.replace(/\//g, '_') // Replace '/' with '_'
|
||||
.replace(/=+$/, '') // Remove trailing '='
|
||||
;
|
||||
return '!' + base64url;
|
||||
}
|
||||
function convertSegmentPathToStaticExportFilename(segmentPath) {
|
||||
return `__next${segmentPath.replace(/\//g, '.')}.txt`;
|
||||
}
|
||||
|
||||
//# sourceMappingURL=segment-value-encoding.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Vary Params Decoding
|
||||
*
|
||||
* This module is shared between server and client.
|
||||
*/
|
||||
export type VaryParams = Set<string>;
|
||||
type FulfilledVaryParamsThenable = {
|
||||
status: 'fulfilled';
|
||||
value: VaryParams;
|
||||
} & PromiseLike<VaryParams>;
|
||||
type PendingVaryParamsThenable = {
|
||||
status: 'pending' | 'resolved_model';
|
||||
value: unknown;
|
||||
} & PromiseLike<VaryParams>;
|
||||
export type VaryParamsThenable = FulfilledVaryParamsThenable | PendingVaryParamsThenable;
|
||||
/**
|
||||
* Synchronously reads vary params from a thenable.
|
||||
*
|
||||
* By the time this is called (client-side or in collectSegmentData), the
|
||||
* thenable should already be fulfilled because the Flight stream has been
|
||||
* fully received. We check the status synchronously to avoid unnecessary
|
||||
* microtasks.
|
||||
*
|
||||
* Returns null if the thenable is still pending (which shouldn't happen in
|
||||
* normal operation - it indicates the server failed to track vary params).
|
||||
*/
|
||||
export declare function readVaryParams(thenable: VaryParamsThenable): VaryParams | null;
|
||||
export {};
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Vary Params Decoding
|
||||
*
|
||||
* This module is shared between server and client.
|
||||
*/ "use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "readVaryParams", {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return readVaryParams;
|
||||
}
|
||||
});
|
||||
function readVaryParams(thenable) {
|
||||
// Attach a no-op listener to force Flight to synchronously resolve the
|
||||
// thenable. When a thenable arrives from the Flight stream, it may be in an
|
||||
// intermediate 'resolved_model' state (data received but not unwrapped).
|
||||
// Calling .then() triggers Flight to transition it to 'fulfilled', making
|
||||
// the value available synchronously. React uses this same optimization
|
||||
// internally to avoid unnecessary microtasks.
|
||||
thenable.then(noop);
|
||||
// If the thenable is still not 'fulfilled' after calling .then(), the server
|
||||
// failed to resolve it before the stream ended. Treat as unknown.
|
||||
if (thenable.status !== 'fulfilled') {
|
||||
return null;
|
||||
}
|
||||
return thenable.value;
|
||||
}
|
||||
const noop = ()=>{};
|
||||
|
||||
//# sourceMappingURL=vary-params-decoding.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../../../src/shared/lib/segment-cache/vary-params-decoding.ts"],"sourcesContent":["/**\n * Vary Params Decoding\n *\n * This module is shared between server and client.\n */\n\nexport type VaryParams = Set<string>\n\ntype FulfilledVaryParamsThenable = {\n status: 'fulfilled'\n value: VaryParams\n} & PromiseLike<VaryParams>\n\ntype PendingVaryParamsThenable = {\n // 'resolved_model' is an internal React Flight state: the underlying model\n // data has arrived but the thenable hasn't been \"unwrapped\" yet. Calling\n // .then() triggers Flight to synchronously transition to 'fulfilled'.\n status: 'pending' | 'resolved_model'\n value: unknown\n} & PromiseLike<VaryParams>\n\nexport type VaryParamsThenable =\n | FulfilledVaryParamsThenable\n | PendingVaryParamsThenable\n\n/**\n * Synchronously reads vary params from a thenable.\n *\n * By the time this is called (client-side or in collectSegmentData), the\n * thenable should already be fulfilled because the Flight stream has been\n * fully received. We check the status synchronously to avoid unnecessary\n * microtasks.\n *\n * Returns null if the thenable is still pending (which shouldn't happen in\n * normal operation - it indicates the server failed to track vary params).\n */\nexport function readVaryParams(\n thenable: VaryParamsThenable\n): VaryParams | null {\n // Attach a no-op listener to force Flight to synchronously resolve the\n // thenable. When a thenable arrives from the Flight stream, it may be in an\n // intermediate 'resolved_model' state (data received but not unwrapped).\n // Calling .then() triggers Flight to transition it to 'fulfilled', making\n // the value available synchronously. React uses this same optimization\n // internally to avoid unnecessary microtasks.\n thenable.then(noop)\n // If the thenable is still not 'fulfilled' after calling .then(), the server\n // failed to resolve it before the stream ended. Treat as unknown.\n if (thenable.status !== 'fulfilled') {\n return null\n }\n return thenable.value\n}\n\nconst noop = () => {}\n"],"names":["readVaryParams","thenable","then","noop","status","value"],"mappings":"AAAA;;;;CAIC;;;;+BAgCeA;;;eAAAA;;;AAAT,SAASA,eACdC,QAA4B;IAE5B,uEAAuE;IACvE,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,8CAA8C;IAC9CA,SAASC,IAAI,CAACC;IACd,6EAA6E;IAC7E,kEAAkE;IAClE,IAAIF,SAASG,MAAM,KAAK,aAAa;QACnC,OAAO;IACT;IACA,OAAOH,SAASI,KAAK;AACvB;AAEA,MAAMF,OAAO,KAAO","ignoreList":[0]}
|
||||
Reference in New Issue
Block a user