Files
2026-11-14 09:56:45 +00:00

171 lines
6.6 KiB
TypeScript

// Generated SDK adapter. Production dispatch requires a server-prepared request binding.
import { businessAppError, type Client, requestBusinessBinding, toBusinessError } from "@conusai-kit/business"
import type { Container, ProcedureKind } from "@conusai-kit/core"
import {
type Availability,
type Item,
inventoryCreateItem,
inventoryCreateReservation,
inventoryGetItem,
inventoryGetItemAvailability,
inventoryGetReservation,
inventoryListItems,
inventoryListReservations,
inventoryReleaseReservation,
type Reservation,
} from "@conusai-sdk/inventory"
import { createClient } from "@conusai-sdk/inventory/client"
import type { AvailabilityView, ItemView, ReservationView } from "../contract/index.js"
import { INVENTORY_SERVICE, type InventoryReads, type InventoryService, inventoryReadsOf } from "../core/ports.js"
/**
* An int64 the service answered, as the exact number a view carries over JSON. The generated client
* types every int64 as `bigint` (a service may send any i64); a quantity beyond 2^53 cannot be shown
* exactly, so it is refused rather than rounded.
*/
function exact(value: bigint, field: string): number {
if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER))
throw new RangeError(`${field} is outside the range a number holds exactly`)
return Number(value)
}
const itemView = (item: Item): ItemView => ({
itemId: item.id,
sku: item.sku,
name: item.name,
unit: item.unit,
description: item.description ?? null,
active: item.active,
})
const availabilityView = (availability: Availability): AvailabilityView => ({
itemId: availability.item_id,
unit: availability.unit,
onHand: exact(availability.on_hand, "on_hand"),
reserved: exact(availability.reserved, "reserved"),
available: exact(availability.available, "available"),
locations: availability.locations.map((level) => ({
locationId: level.location_id,
onHand: exact(level.on_hand, "on_hand"),
reserved: exact(level.reserved, "reserved"),
available: exact(level.available, "available"),
})),
})
const reservationView = (reservation: Reservation): ReservationView => ({
reservationId: reservation.id,
itemId: reservation.item_id,
locationId: reservation.location_id,
quantity: exact(reservation.quantity, "quantity"),
outstanding: exact(reservation.outstanding, "outstanding"),
released: exact(reservation.released, "released"),
issued: exact(reservation.issued, "issued"),
status: reservation.status,
createdAt: reservation.created_at,
})
/** What a generated operation answered: its data, or the kit's app error for the refusal (`businessAppError`). */
async function answer<T>(
call: Promise<{ data?: T; error?: unknown; response?: Response }>,
): Promise<Exclude<T, undefined>> {
let result: { data?: T; error?: unknown; response?: Response }
try {
result = await call
} catch (error) {
throw businessAppError(toBusinessError(error, undefined))
}
if (result.error !== undefined || result.data === undefined) {
throw businessAppError(toBusinessError(result.error, result.response))
}
return result.data as Exclude<T, undefined>
}
/** The port over one client, in one organization of one platform project. */
export function sdkInventoryService(binding: { client: Client; orgSlug: string; bindingId: string }): InventoryService {
const { client } = binding
const org = { org_slug: binding.orgSlug }
return {
bindingId: binding.bindingId,
async listItems(query) {
const page = await answer(
inventoryListItems({ client, path: org, query: { ...query, limit: BigInt(query.limit) } }),
)
return { rows: page.data.map(itemView), ...(page.next_after ? { after: page.next_after } : {}) }
},
async getItem(itemId) {
return itemView(await answer(inventoryGetItem({ client, path: { ...org, item_id: itemId } })))
},
async availability(itemId) {
return availabilityView(await answer(inventoryGetItemAvailability({ client, path: { ...org, item_id: itemId } })))
},
async listReservations(query) {
const page = await answer(
inventoryListReservations({ client, path: org, query: { ...query, limit: BigInt(query.limit) } }),
)
return { rows: page.data.map(reservationView), ...(page.next_after ? { after: page.next_after } : {}) }
},
async getReservation(reservationId) {
const reservation = await answer(
inventoryGetReservation({ client, path: { ...org, reservation_id: reservationId } }),
)
return { reservation: reservationView(reservation), reference: reservation.reference }
},
async createItem(input) {
return itemView(await answer(inventoryCreateItem({ client, path: org, body: input })))
},
async reserve(input) {
const outcome = await answer(
inventoryCreateReservation({
client,
path: org,
body: {
item_id: input.itemId,
location_id: input.locationId,
quantity: BigInt(input.quantity),
reference: input.reference,
idempotency_key: input.key,
},
}),
)
return reservationView(outcome.reservation)
},
async release(input) {
const outcome = await answer(
inventoryReleaseReservation({
client,
path: { ...org, reservation_id: input.reservationId },
body: {
idempotency_key: input.key,
...(input.quantity === undefined ? {} : { quantity: BigInt(input.quantity) }),
},
}),
)
return reservationView(outcome.reservation)
},
}
}
/**
* The service a handler of `kind` may use: the bound one (the token's view for the kind applies), or
* the current verified request's, narrowed to its reads for a `read` or `write` handler as the token would.
* A deployment without the contract is kit-core's `service_configuration_missing` refusal.
*/
export function inventoryServiceOf(container: Container, kind: "effect"): InventoryService
export function inventoryServiceOf(container: Container, kind: ProcedureKind): InventoryReads
export function inventoryServiceOf(container: Container, kind: ProcedureKind): InventoryReads {
if (container.has(INVENTORY_SERVICE)) return container.resolve(INVENTORY_SERVICE) as InventoryReads
let service: InventoryService
try {
const binding = requestBusinessBinding(container)
service = sdkInventoryService({
client: binding.client(createClient),
orgSlug: binding.orgSlug,
bindingId: binding.bindingId,
})
} catch (error) {
throw businessAppError(error)
}
return kind === "effect" ? service : inventoryReadsOf(service)
}