Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | 103x 103x 482012x 269493x 269493x 486941x 486941x 486941x 486941x 268507x 268511x 268511x 268511x 94x 12452x 12993x 12997x 7807x 94x 12997x 12993x 8348x 4645x 4645x 4645x 4645x 4645x 220881x 4810x 255514x 255514x | import { defaultSerializeQueryArgs } from "@reduxjs/toolkit/query";
const SAFE_REQUEST_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
export type RevisionRouteScope = {
isPresent: boolean;
rawValue: string | null;
revisionStateId: number | null;
};
export const CURRENT_REVISION_ROUTE_SCOPE: RevisionRouteScope = {
isPresent: false,
rawValue: null,
revisionStateId: null,
};
/** Normalize an optional route revision into one positive state identifier. */
export function normalizeRevisionStateId(
value: string | null | undefined,
): number | null {
if (!value || !/^\d+$/.test(value)) {
return null;
}
const revisionStateId = Number(value);
return Number.isSafeInteger(revisionStateId) && revisionStateId > 0
? revisionStateId
: null;
}
/** Read a normalized revision identifier from an absolute or relative URL. */
export function getRevisionRouteScopeFromUrl(url: string): RevisionRouteScope {
try {
const parsedUrl = new URL(url, "http://localhost");
const isPresent = parsedUrl.searchParams.has("revision");
const rawValue = isPresent ? parsedUrl.searchParams.get("revision") : null;
return {
isPresent,
rawValue,
revisionStateId: normalizeRevisionStateId(rawValue),
};
} catch {
return CURRENT_REVISION_ROUTE_SCOPE;
}
}
/** Read a normalized revision identifier from an absolute or relative URL. */
export function getRevisionStateIdFromUrl(url: string): number | null {
return getRevisionRouteScopeFromUrl(url).revisionStateId;
}
/** Read the active route scope without retaining mutable browser state. */
export function getCurrentRevisionRouteScope(): RevisionRouteScope {
return typeof document === "undefined"
? CURRENT_REVISION_ROUTE_SCOPE
: getRevisionRouteScopeFromUrl(document.URL);
}
/** Resolve the stable flowsheet identity only on a flowsheet project route. */
export function getFlowsheetIdFromUrl(url: string): number | null {
try {
const pathname = new URL(url, "http://localhost").pathname;
const match = pathname.match(/^\/project\/(\d+)(?:\/|$)/);
return match ? normalizeRevisionStateId(match[1]) : null;
} catch {
return null;
}
}
export function isSafeRequestMethod(method: string): boolean {
return SAFE_REQUEST_METHODS.has(method.toUpperCase());
}
/** Return whether a client request must be stopped before leaving the browser. */
export function isHistoricalRevisionMutation(
method: string,
revisionScope: RevisionRouteScope,
): boolean {
return revisionScope.isPresent && !isSafeRequestMethod(method);
}
/** Identify the explicit lifecycle action that restores Current from history. */
export function isRevisionRestoreRequest(
method: string,
pathname: string,
): boolean {
return (
method.toUpperCase() === "POST" &&
/^\/api\/core\/flowsheets\/[^/]+\/revisions\/[^/]+\/restore\/?$/.test(
pathname,
)
);
}
/** Add the stable flowsheet and immutable read-state scope to a request URL. */
export function scopeFlowsheetRequestUrl({
url,
flowsheetId,
revisionScope,
}: {
url: URL;
flowsheetId: number | null;
revisionScope: RevisionRouteScope;
}): URL {
const scopedUrl = new URL(url.toString());
if (flowsheetId !== null && !scopedUrl.searchParams.has("flowsheet")) {
scopedUrl.searchParams.set("flowsheet", flowsheetId.toString());
}
if (flowsheetId !== null && revisionScope.isPresent) {
scopedUrl.searchParams.set(
"revision",
revisionScope.revisionStateId?.toString() ?? revisionScope.rawValue ?? "",
);
}
return scopedUrl;
}
/**
* Move an explicitly supplied flowsheet parameter into the URL before route
* scope is applied. RTK Query otherwise appends `params` after the base query
* has injected the active flowsheet, producing two conflicting parameters.
*/
export function canonicalizeFlowsheetQueryParam(
url: URL,
params: Record<string, unknown> | undefined,
): { url: URL; params: Record<string, unknown> | undefined } {
const explicitFlowsheet = params?.flowsheet;
if (
explicitFlowsheet === undefined ||
explicitFlowsheet === null ||
explicitFlowsheet === ""
) {
return { url, params };
}
const canonicalUrl = new URL(url.toString());
canonicalUrl.searchParams.set("flowsheet", String(explicitFlowsheet));
const remainingParams = { ...params };
delete remainingParams.flowsheet;
return { url: canonicalUrl, params: remainingParams };
}
export function getRevisionReadIdentity(
revisionScope: RevisionRouteScope,
): string {
if (!revisionScope.isPresent) return "current";
return revisionScope.revisionStateId === null
? "revision:invalid"
: `revision:${revisionScope.revisionStateId}`;
}
/** Keep otherwise-identical RTK Query results separate for each read state. */
export function serializeQueryArgsWithRevision(
query: Parameters<typeof defaultSerializeQueryArgs>[0],
revisionScope = getCurrentRevisionRouteScope(),
flowsheetId = typeof document === "undefined"
? null
: getFlowsheetIdFromUrl(document.URL),
): string {
const defaultKey = defaultSerializeQueryArgs(query);
return flowsheetId === null || !revisionScope.isPresent
? defaultKey
: `${defaultKey}|readState=${getRevisionReadIdentity(revisionScope)}`;
}
|