diff --git a/src/application/environment/environmentStateAnswer.ts b/src/application/environment/environmentStateAnswer.ts index cd41601dac..7417d6524e 100644 --- a/src/application/environment/environmentStateAnswer.ts +++ b/src/application/environment/environmentStateAnswer.ts @@ -2,8 +2,6 @@ import { stateBodyJson } from "../../domain/analysisView/EnvironmentStateAccumul import { collectionPathOf, listItems, - pageSize, - pagesOnly, missingRequiredFields, renderStateEntity, renderStateList, @@ -17,6 +15,10 @@ import { type StateListView, type StateView, } from "../../domain/analysisView/environmentStateRules.js"; +import { + pageSize, + pagesOnly, +} from "../../domain/analysisView/environmentStatePaging.js"; import { isAbsence } from "../../domain/analysisView/environmentStateWriteChecks.js"; import type { AnalysisError } from "../../domain/analysisErrorBase.js"; import { diff --git a/src/domain/analysisView/environmentStateGate.ts b/src/domain/analysisView/environmentStateGate.ts index c1ba4a7bb3..0e0b175096 100644 --- a/src/domain/analysisView/environmentStateGate.ts +++ b/src/domain/analysisView/environmentStateGate.ts @@ -7,7 +7,6 @@ import { isObject, lastSegmentValue, listItems, - pagesOnly, renderStateEntity, renderStateList, replaceAt, @@ -20,6 +19,7 @@ import { type StateToolFacts, type StateView, } from "./environmentStateRules.js"; +import { pagesOnly } from "./environmentStatePaging.js"; import { ENTRY_BYTES, textBytes } from "./webNetworkRouteTemplates.js"; /** A read tool whose recordings locate entities of one type at one pointer. */ diff --git a/src/domain/analysisView/environmentStatePaging.ts b/src/domain/analysisView/environmentStatePaging.ts new file mode 100644 index 0000000000..237e94f50a --- /dev/null +++ b/src/domain/analysisView/environmentStatePaging.ts @@ -0,0 +1,53 @@ +import { + iterateUrlQueryParameters, + parseUrlQueryParameterNames, +} from "../recordedTransportUrl.js"; + +/** Query parameters that page or bound a list without filtering it. */ +const PAGING_PARAMETERS = new Set([ + "limit", + "offset", + "page", + "per_page", + "perpage", + "page_size", + "pagesize", + "size", + "start", + "_start", + "_end", +]); + +/** Whether a query only pages its list: no parameters, or paging parameters alone. */ +export const pagesOnly = (query: string | undefined): boolean => + query === undefined || + [...parseUrlQueryParameterNames(query)].every((name) => + PAGING_PARAMETERS.has(name.toLowerCase()), + ); + +/** Paging parameters that bound how many items a page holds. */ +const PAGE_SIZE_PARAMETERS = new Set([ + "limit", + "per_page", + "perpage", + "page_size", + "pagesize", + "size", +]); + +/** The most items a query's page holds: its smallest whole-number page-size value or `_start`–`_end` range width, or undefined without one. */ +export const pageSize = (query: string | undefined): number | undefined => { + let size = Infinity; + const range = { start: 0, end: Infinity }; + for (const [name, value] of iterateUrlQueryParameters(query ?? "")) { + const key = name.toLowerCase(); + if (!/^\d+$/u.test(value)) continue; + // A size of zero bounds nothing: applications read it as no limit, as a whole line naming it shows. + if (PAGE_SIZE_PARAMETERS.has(key)) { + if (Number(value) > 0) size = Math.min(size, Number(value)); + } else if (key === "_start") range.start = Number(value); + else if (key === "_end") range.end = Number(value); + } + const width = Math.min(size, Math.max(range.end - range.start, 0)); + return Number.isFinite(width) ? width : undefined; +}; diff --git a/src/domain/analysisView/environmentStateRules.ts b/src/domain/analysisView/environmentStateRules.ts index 99a43ec22d..c9875117a9 100644 --- a/src/domain/analysisView/environmentStateRules.ts +++ b/src/domain/analysisView/environmentStateRules.ts @@ -1,8 +1,4 @@ import { canonicalJson } from "../comparisonSemantics.js"; -import { - iterateUrlQueryParameters, - parseUrlQueryParameterNames, -} from "../recordedTransportUrl.js"; import type { JsonValue } from "../jsonValue.js"; import type { StateWritePlan } from "./environmentStateWriteRules.js"; import { ENTRY_BYTES, textBytes } from "./webNetworkRouteTemplates.js"; @@ -147,55 +143,6 @@ const QUERY_SEGMENTS = new Set([ "_count", "_msearch", ]); -/** Query parameters that page or bound a list without filtering it. */ -const PAGING_PARAMETERS = new Set([ - "limit", - "offset", - "page", - "per_page", - "perpage", - "page_size", - "pagesize", - "size", - "start", - "_start", - "_end", -]); - -/** Whether a query only pages its list: no parameters, or paging parameters alone. */ -export const pagesOnly = (query: string | undefined): boolean => - query === undefined || - [...parseUrlQueryParameterNames(query)].every((name) => - PAGING_PARAMETERS.has(name.toLowerCase()), - ); - -/** Paging parameters that bound how many items a page holds. */ -const PAGE_SIZE_PARAMETERS = new Set([ - "limit", - "per_page", - "perpage", - "page_size", - "pagesize", - "size", -]); - -/** The most items a query's page holds: its smallest whole-number page-size value or `_start`–`_end` range width, or undefined without one. */ -export const pageSize = (query: string | undefined): number | undefined => { - let size = Infinity; - const range = { start: 0, end: Infinity }; - for (const [name, value] of iterateUrlQueryParameters(query ?? "")) { - const key = name.toLowerCase(); - if (!/^\d+$/u.test(value)) continue; - // A size of zero bounds nothing: applications read it as no limit, as a whole line naming it shows. - if (PAGE_SIZE_PARAMETERS.has(key)) { - if (Number(value) > 0) size = Math.min(size, Number(value)); - } else if (key === "_start") range.start = Number(value); - else if (key === "_end") range.end = Number(value); - } - const width = Math.min(size, Math.max(range.end - range.start, 0)); - return Number.isFinite(width) ? width : undefined; -}; - /** Identity field names tried, in order, when no path parameter proves one. */ export const IDENTITY_NAMES = [ "id", diff --git a/src/domain/analysisView/environmentStateWriteChecks.ts b/src/domain/analysisView/environmentStateWriteChecks.ts index f0b6cf3bad..8efdf0d90f 100644 --- a/src/domain/analysisView/environmentStateWriteChecks.ts +++ b/src/domain/analysisView/environmentStateWriteChecks.ts @@ -13,8 +13,6 @@ import { lastSegmentValue, listItems, missingRequiredFields, - pagesOnly, - pageSize, renderStateEntity, renderStateList, replaceAt, @@ -30,6 +28,7 @@ import { type StateToolFacts, type StateView, } from "./environmentStateRules.js"; +import { pagesOnly, pageSize } from "./environmentStatePaging.js"; /** A query as replay compares a call's: decoded values by name, in any order; undefined when it repeats a name with another value, which no call's arguments can carry. */ const queryKey = (query: string | undefined): string | undefined => {