import {z} from 'zod' export type GetApiStreamDspV1MenuFileS3UrlOptions = {baseUrl: string, fetch?: typeof fetch | undefined}; export const schedule = z.object({monday: z.array(z.string()), tuesday: z.array(z.string()), wednesday: z.array(z.string()), thursday: z.array(z.string()), friday: z.array(z.string()), saturday: z.array(z.string()), sunday: z.array(z.string())}); export const image = z.object({url: z.string()}); export const itemVariation = z.object({stream_id: z.string(), price: z.number(), currency: z.string(), name: z.string(), is_active: z.boolean().optional(), sku: z.string().optional(), upc: z.string().optional(), stock_count: z.number().optional()}); export const defaultModifierQuantities = z.object({}); export const modifier = z.object({stream_id: z.string(), name: z.string(), price: z.number(), currency: z.string(), is_active: z.boolean().optional(), nested_modifier_groups: z.array(z.lazy(() => modifierGroup)), images: z.array(image).optional()}); export const modifierRules = z.object({minimum_unique_modifiers_allowed: z.number().optional(), maximum_unique_modifiers_allowed: z.number().optional(), amount_of_modifiers_free: z.number().optional(), selection_type: z.enum(["single", "single_proportion", "multiple"]).optional()}); export const modifierGroup: z.ZodType = z.object({stream_id: z.string(), name: z.string(), default_modifier_quantities: defaultModifierQuantities.optional(), modifiers: z.array(modifier), rules: modifierRules.optional()}); export const item = z.object({stream_id: z.string(), is_alcohol: z.boolean().optional(), tax_ids: z.array(z.string()), default_item_id: z.string().optional(), name: z.string(), description: z.string().optional(), images: z.array(image).optional(), variations: z.array(itemVariation), modifier_groups: z.array(modifierGroup), is_active: z.boolean().optional(), allergens: z.array(z.enum(["eggs", "soybeans", "dairy", "sesame", "lupin", "celery", "mustard", "gluten", "wheat", "rye", "barley", "oats", "spelt", "kamut", "shellfish", "crustaceans", "molluscs", "fish", "almonds", "hazelnuts", "walnuts", "cashews", "pecan", "brazil", "pistachio", "macadamia", "peanuts", "tree_nuts", "sulphur_dioxide_sulphites"])).optional()}); export const category = z.object({stream_id: z.string(), name: z.string(), items: z.array(item), description: z.string().optional()}); export const menu = z.object({schedule: schedule, stream_id: z.string(), categories: z.array(category), name: z.string()}); export const holiday = z.object({date: z.string(), hours: z.array(z.string()).optional(), recurring_yearly: z.boolean(), specific_years: z.array(z.number()).optional(), name: z.string().optional()}); export const tax = z.object({stream_id: z.string(), is_inclusive: z.boolean().optional(), rate: z.number(), name: z.string(), is_default: z.boolean().optional()}); export const dspLocationMenuEventDataDto = z.object({prep_time_minutes: z.number(), menus: z.array(menu), holidays: z.array(holiday), taxes: z.array(tax)}); export const menuV2 = z.object({stream_id: z.string(), name: z.string(), schedule: schedule, category_ids: z.array(z.string())}); export const categoryV2 = z.object({stream_id: z.string(), name: z.string(), item_family_ids: z.array(z.string()), description: z.string().optional()}); export const modifierRulesV2 = z.object({minimum_unique_modifiers_allowed: z.number().optional(), maximum_unique_modifiers_allowed: z.number().optional(), amount_of_modifiers_free: z.number().optional(), selection_type: z.enum(["single", "single_proportion", "multiple"]).optional(), default_modifier_quantities: defaultModifierQuantities.optional()}); export const overrideRules = z.object({parent_id: z.string(), parent_type: z.object({}), override_rules: modifierRulesV2}); export const modifierGroupV2 = z.object({stream_id: z.string(), name: z.string(), modifier_ids: z.array(z.string()), rules: modifierRulesV2.optional(), overrides: z.array(overrideRules).optional()}); export const itemFamilyV2 = z.object({stream_id: z.string(), name: z.string(), description: z.string().optional(), images: image.optional(), item_ids: z.array(z.string()), modifier_group_ids: z.array(z.string()), is_alcohol: z.boolean().optional(), tax_ids: z.array(z.string()), allergens: z.array(z.enum(["eggs", "soybeans", "dairy", "sesame", "lupin", "celery", "mustard", "gluten", "wheat", "rye", "barley", "oats", "spelt", "kamut", "shellfish", "crustaceans", "molluscs", "fish", "almonds", "hazelnuts", "walnuts", "cashews", "pecan", "brazil", "pistachio", "macadamia", "peanuts", "tree_nuts", "sulphur_dioxide_sulphites"])).optional(), default_item_id: z.string().optional()}); export const itemModifierOverride = z.object({modifier_id: z.string(), price_amount: z.number()}); export const menuSpecificItemOverride = z.object({menu_id: z.string(), price_amount: z.number(), is_active: z.boolean().optional()}); export const itemV2 = z.object({price: z.number(), currency: z.string(), stream_id: z.string(), name: z.string(), modifier_overrides: z.array(itemModifierOverride).optional(), menu_specific_overrides: z.array(menuSpecificItemOverride).optional(), is_active: z.boolean().optional(), sku: z.string().optional(), upc: z.string().optional(), stock_count: z.number().optional()}); export const modifierV2 = z.object({stream_id: z.string(), name: z.string(), price: z.number(), currency: z.string(), is_active: z.boolean().optional(), nested_modifier_group_ids: z.array(z.string()).optional(), images: z.array(image).optional()}); export const dspLocationMenuEventDataV2Dto = z.object({prep_time_minutes: z.number(), menus: z.array(menuV2), holidays: z.array(holiday), taxes: z.array(tax), categories: z.array(categoryV2), modifier_groups: z.array(modifierGroupV2), item_families: z.array(itemFamilyV2), items: z.array(itemV2), modifiers: z.array(modifierV2)}); export const getApiStreamDspV1MenuFileS3UrlResponse = z.union([dspLocationMenuEventDataDto, dspLocationMenuEventDataV2Dto]); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Menu Ingestion File (DSP -> URL) * * GET /stream-dsp/v1/menuFileS3Url */ export const getApiStreamDspV1MenuFileS3Url = async (options: GetApiStreamDspV1MenuFileS3UrlOptions) => { const {baseUrl, fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/stream-dsp/v1/menuFileS3Url${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(getApiStreamDspV1MenuFileS3UrlResponse, await res.json()) };