mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
* fix(environment): match served items whose route repeats a separator
callSegments split the tool path template itself and dropped empty literal
segments, while the held collection is split by segmentsOf, which keeps an
internal empty segment. A captured item route such as /api//books/{id} was
stored under collection /api//books but searched with ["api","books"], so
collectionMatch rejected it and a served item read reported the extracted
entity as missing.
Split the call template through segmentsOf, the same owner used for the held
collection path, so a repeated separator is preserved on both sides and the
item answers from its extracted collection. Single-separator routes stay
distinct and param positions are unchanged.
Fixes #6847
* fix(environment): keep repeated-separator collections writable
Preserving empty literal segments in callSegments made createdCollection's
missing-argument guard reject a create whose route repeats a separator, such
as POST /api//books: the empty literal segment tripped the
segments.some(text.length === 0) check and answerCreate refused the valid
route. Restrict the guard to empty supplied parameter segments, so a missing
path argument is still refused while a repeated-separator collection stays
writable as well as readable.
---------
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
44 lines
1.9 KiB
TypeScript
44 lines
1.9 KiB
TypeScript
import {
|
|
collectionPathOf,
|
|
selectedCollection,
|
|
} from "../../domain/analysisView/environmentStateRules.js";
|
|
import type { StateServing } from "./environmentStateAnswer.js";
|
|
import { callSegments } from "./environmentStateCallLines.js";
|
|
import type { ReplayArguments } from "./environmentReplay.js";
|
|
import type { EnvironmentManifestTool } from "./environmentSource.js";
|
|
|
|
/** The collections a create may go to: those the store knows, held ones first, then those the type's checked list lines read, so an empty one keeps its spelling. */
|
|
const creatableCollectionsOf = (
|
|
serving: StateServing,
|
|
type: string,
|
|
): readonly string[] => {
|
|
const listed: string[] = [];
|
|
for (const view of serving.store.state.views.values())
|
|
if (view.shape === "list" && view.type === type)
|
|
for (const ordinal of view.checked_record_ordinals) {
|
|
const exchange = serving.source.exchanges.get(ordinal);
|
|
if (exchange !== undefined)
|
|
listed.push(collectionPathOf(exchange, false));
|
|
}
|
|
return [...new Set([...serving.store.knownCollectionsOf(type), ...listed])];
|
|
};
|
|
|
|
/** The collection a create writes to: a known collection the call's path matches, else the path as supplied. */
|
|
export const createdCollection = (
|
|
serving: StateServing,
|
|
tool: EnvironmentManifestTool,
|
|
args: ReplayArguments,
|
|
type: string,
|
|
): string | undefined => {
|
|
const segments = callSegments(tool, args);
|
|
// A supplied path argument may not be empty; an empty literal segment, from a
|
|
// repeated separator in the route, is part of the collection's spelling.
|
|
if (
|
|
segments === undefined ||
|
|
segments.some(({ text, supplied }) => supplied && text.length === 0)
|
|
)
|
|
return undefined;
|
|
// The spelling the call supplies verbatim wins, as item calls choose their entity, then the first known: held ones lead.
|
|
return selectedCollection(segments, creatableCollectionsOf(serving, type));
|
|
};
|