121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
import { PERMS } from "@/lib/permission-slugs";
|
|
import { authorizeHousekeeping } from "../../foundation/authorization";
|
|
import { satisfiesCapability } from "../../foundation/capability-context";
|
|
import {
|
|
anyCapability,
|
|
type CapabilityRequirement,
|
|
fail,
|
|
type HousekeepingCapabilityContext,
|
|
type HousekeepingSearchProvider,
|
|
ok,
|
|
} from "../../foundation/contracts";
|
|
import { isSafeHousekeepingHref } from "../../foundation/housekeeping-href";
|
|
|
|
export const ECONOMY_SEARCH_PROVIDER_IDS = [
|
|
"economy.catalog-pages",
|
|
"economy.items",
|
|
"economy.transactions",
|
|
] as const;
|
|
|
|
export interface EconomySearchCandidate {
|
|
readonly id: string;
|
|
readonly title: string;
|
|
readonly description?: string;
|
|
readonly href: string;
|
|
readonly capability: CapabilityRequirement;
|
|
}
|
|
|
|
type EconomySearchLoader = (
|
|
context: HousekeepingCapabilityContext,
|
|
term: string,
|
|
limit: number,
|
|
) => Promise<readonly EconomySearchCandidate[]>;
|
|
|
|
export interface EconomySearchAdapters {
|
|
readonly catalogPages: EconomySearchLoader;
|
|
readonly items: EconomySearchLoader;
|
|
readonly transactions: EconomySearchLoader;
|
|
}
|
|
|
|
function createProvider(
|
|
id: (typeof ECONOMY_SEARCH_PROVIDER_IDS)[number],
|
|
capability: CapabilityRequirement,
|
|
load: EconomySearchLoader,
|
|
): HousekeepingSearchProvider {
|
|
return {
|
|
id,
|
|
owner: "economy",
|
|
capability,
|
|
async search(context, input) {
|
|
const authorization = authorizeHousekeeping(context, capability);
|
|
if (!authorization.ok) return authorization;
|
|
const limit = Number.isFinite(input.limit)
|
|
? Math.min(25, Math.max(1, Math.trunc(input.limit)))
|
|
: 25;
|
|
const term = input.term.normalize("NFC").trim().slice(0, 128);
|
|
try {
|
|
const candidates = await load(context, term, limit);
|
|
return ok(
|
|
candidates
|
|
.filter(
|
|
(candidate) =>
|
|
isSafeHousekeepingHref(candidate.href) &&
|
|
satisfiesCapability(context, candidate.capability),
|
|
)
|
|
.slice(0, limit)
|
|
.map((candidate) => ({
|
|
...candidate,
|
|
domain: "economy" as const,
|
|
type: "entity" as const,
|
|
href: candidate.href as `/ase/${string}`,
|
|
})),
|
|
authorization.correlationId,
|
|
);
|
|
} catch {
|
|
return fail(
|
|
"DEPENDENCY_UNAVAILABLE",
|
|
"errors.housekeeping.dependencyUnavailable",
|
|
authorization.correlationId,
|
|
);
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
export function createEconomySearchProviders(
|
|
adapters: EconomySearchAdapters,
|
|
): readonly HousekeepingSearchProvider[] {
|
|
return [
|
|
createProvider(
|
|
"economy.catalog-pages",
|
|
anyCapability(PERMS.CATALOG_VIEW),
|
|
adapters.catalogPages,
|
|
),
|
|
createProvider(
|
|
"economy.items",
|
|
anyCapability(PERMS.CATALOG_VIEW),
|
|
adapters.items,
|
|
),
|
|
createProvider(
|
|
"economy.transactions",
|
|
anyCapability(PERMS.SHOP_VIEW),
|
|
adapters.transactions,
|
|
),
|
|
];
|
|
}
|
|
|
|
export const ECONOMY_SEARCH_PROVIDERS = createEconomySearchProviders({
|
|
async catalogPages(context, term, limit) {
|
|
const { loadEconomySearchCandidates } = await import("./search-production");
|
|
return loadEconomySearchCandidates("catalog-pages", context, term, limit);
|
|
},
|
|
async items(context, term, limit) {
|
|
const { loadEconomySearchCandidates } = await import("./search-production");
|
|
return loadEconomySearchCandidates("items", context, term, limit);
|
|
},
|
|
async transactions(context, term, limit) {
|
|
const { loadEconomySearchCandidates } = await import("./search-production");
|
|
return loadEconomySearchCandidates("transactions", context, term, limit);
|
|
},
|
|
});
|