// What the inventory module does, as pure TypeScript over the port in ports.ts. // // Items belong to the credential-bound app tenant and environment. Reservations // additionally belong to the resolved app project; another project's reservation // is not found. Platform project IDs never substitute for immutable binding IDs. import { projectRef } from "@conusai-kit/business" import { notFound, type ProjectScope } from "@conusai-kit/core" import { type AvailabilityView, type ItemView, PAGE_LIMIT, type ReservationView } from "../contract/index.js" import type { InventoryReads, InventoryService, Page } from "./ports.js" /** Canonical partition and resolved-project reference, shared with the service parser. */ export const projectReference = (bindingId: string, projectId: string): string => projectRef(bindingId, projectId) /** The service's idempotency key of a request: one project's request id never answers another's. */ export const requestKey = (scope: Pick, requestId: string): string => `app-project:${scope.projectId}:${requestId}` export function listItems( reads: InventoryReads, query: { q?: string; after?: string; limit?: number }, ): Promise> { const q = query.q?.trim() return reads.listItems({ ...(q ? { q } : {}), ...(query.after === undefined ? {} : { after: query.after }), limit: query.limit ?? PAGE_LIMIT, }) } export function createItem( service: InventoryService, input: { sku: string; name: string; unit: string; description?: string }, ): Promise { const description = input.description?.trim() return service.createItem({ sku: input.sku, name: input.name.trim(), unit: input.unit, ...(description ? { description } : {}), }) } export async function itemAvailability( reads: InventoryReads, itemId: string, ): Promise<{ item: ItemView; availability: AvailabilityView }> { const [item, availability] = await Promise.all([reads.getItem(itemId), reads.availability(itemId)]) return { item, availability } } export function listProjectReservations( reads: InventoryReads, scope: ProjectScope, query: { status?: "open" | "closed"; after?: string; limit?: number }, ): Promise> { return reads.listReservations({ reference: projectReference(reads.bindingId, scope.projectId), ...(query.status === undefined ? {} : { status: query.status }), ...(query.after === undefined ? {} : { after: query.after }), limit: query.limit ?? PAGE_LIMIT, }) } export function reserveStock( service: InventoryService, scope: ProjectScope, input: { itemId: string; locationId: string; quantity: number; requestId: string }, ): Promise { return service.reserve({ itemId: input.itemId, locationId: input.locationId, quantity: input.quantity, reference: projectReference(service.bindingId, scope.projectId), key: requestKey(scope, input.requestId), }) } export async function releaseStock( service: InventoryService, scope: ProjectScope, input: { reservationId: string; quantity?: number; requestId: string }, ): Promise { const { reference } = await service.getReservation(input.reservationId) // Another project's reservation is not found, never forbidden: its existence does not leak. if (reference !== projectReference(service.bindingId, scope.projectId)) throw notFound("no such reservation") return service.release({ reservationId: input.reservationId, ...(input.quantity === undefined ? {} : { quantity: input.quantity }), key: requestKey(scope, input.requestId), }) }