'use strict';

var chunkSRJEMEVZ_js = require('./chunk-SRJEMEVZ.js');
var chunkGFM5JW45_js = require('./chunk-GFM5JW45.js');
var zod = require('zod');

var cimplifyRegistry = zod.z.registry();
function registerSchema(schema, meta) {
  cimplifyRegistry.add(schema, meta);
  return schema;
}
var MoneySchema = registerSchema(
  zod.z.string().regex(/^-?\d+\.\d{2}$/, 'Money must be a decimal string with exactly 2 decimals (e.g. "12.50")'),
  { id: "Money", description: "Strict 2-decimal money string", version: "1.0.0" }
);
var MoneyLooseSchema = registerSchema(
  zod.z.string().regex(/^-?\d+(\.\d+)?$/, "Must be a decimal string"),
  { id: "MoneyLoose", description: "Decimal string with arbitrary precision", version: "1.0.0" }
);
var IsoDateTimeSchema = registerSchema(zod.z.iso.datetime(), {
  id: "IsoDateTime",
  description: "ISO 8601 date-time",
  version: "1.0.0"
});
var TimestampSchema = registerSchema(
  zod.z.union([zod.z.iso.datetime(), zod.z.string().min(1)]),
  {
    id: "Timestamp",
    description: "ISO 8601 or any non-empty string (boundary use)",
    version: "1.0.0"
  }
);
var CurrencyCodeSchema = registerSchema(
  zod.z.string().length(3, "currency must be a 3-letter ISO 4217 code").regex(/^[A-Z]{3}$/, "currency must be uppercase letters"),
  { id: "CurrencyCode", description: "ISO 4217 currency code", version: "1.0.0" }
);
var CountryCodeSchema = registerSchema(
  zod.z.string().length(2, "countryCode must be ISO 3166-1 alpha-2").regex(/^[A-Z]{2}$/, "countryCode must be uppercase letters"),
  { id: "CountryCode", description: "ISO 3166-1 alpha-2 country code", version: "1.0.0" }
);
var LocaleSchema = registerSchema(
  zod.z.string().regex(/^[a-z]{2}(_[A-Z]{2})?$/, "locale must look like 'en' or 'en_GH'"),
  { id: "Locale", description: "BCP-47-ish locale", version: "1.0.0" }
);
var PhoneE164Schema = registerSchema(
  zod.z.string().regex(/^\+?[0-9]{7,15}$/, "phoneTel should be E.164-ish digits (7\u201315)"),
  { id: "PhoneE164", description: "E.164-ish phone number", version: "1.0.0" }
);
var SeedNameSchema = registerSchema(
  zod.z.enum([
    "default",
    "empty",
    "reesa-storefront",
    "restaurant",
    "retail",
    "services",
    "grocery",
    "fashion"
  ]),
  { id: "SeedName", description: "Mock seed identifier", version: "1.0.0" }
);
var ProductTypeSchema = registerSchema(
  zod.z.enum(["product", "service", "digital", "bundle", "composite"]),
  { id: "ProductType", description: "Product type discriminator", version: "1.0.0" }
);
var LineTypeSchema = registerSchema(
  zod.z.enum(["simple", "service", "digital", "bundle", "composite"]),
  { id: "LineType", description: "Cart line type discriminator", version: "1.0.0" }
);
var ServiceStatusSchema = registerSchema(
  zod.z.enum(["pending", "confirmed", "completed", "cancelled"]),
  { id: "ServiceStatus", description: "Service booking status", version: "1.0.0" }
);
var SchemaViolationError = class extends Error {
  constructor(message, label, issues) {
    super(message);
    this.label = label;
    this.issues = issues;
    this.name = "SchemaViolationError";
  }
  toJSON() {
    return {
      type: "https://cimplify.io/errors/schema-violation",
      title: "Schema Violation",
      status: 422,
      detail: `${this.label} did not match its schema`,
      errors: this.issues
    };
  }
};
function makeAssertion(schema, label) {
  return (value) => {
    const result = schema.safeParse(value);
    if (!result.success) {
      const pretty = zod.z.prettifyError(result.error);
      const issues = result.error.issues.map((i) => ({
        path: i.path.map(String),
        message: i.message,
        code: i.code
      }));
      throw new SchemaViolationError(
        `${label} schema violation:
${pretty}`,
        label,
        issues
      );
    }
  };
}
var BrandSocialSchema = zod.z.object({
  label: zod.z.string().min(1),
  href: zod.z.string().url(),
  icon: zod.z.string().optional()
});
var BrandNavLinkSchema = zod.z.object({
  label: zod.z.string().min(1),
  href: zod.z.string().min(1)
});
var BrandPolicySectionSchema = zod.z.object({
  heading: zod.z.string().min(1),
  body: zod.z.union([
    zod.z.string().min(1),
    zod.z.object({ intro: zod.z.string(), bullets: zod.z.array(zod.z.string()).min(1) })
  ])
});
var BrandPolicyPageSchema = zod.z.object({
  eyebrow: zod.z.string(),
  title: zod.z.string().min(1),
  lastUpdated: zod.z.string().min(1),
  sections: zod.z.array(BrandPolicySectionSchema).min(1)
});
var BrandContactSchema = zod.z.object({
  email: zod.z.string().email(),
  phone: zod.z.string().min(1),
  phoneTel: PhoneE164Schema,
  streetAddress: zod.z.string().min(1),
  city: zod.z.string().min(1),
  countryCode: CountryCodeSchema,
  hours: zod.z.string().optional()
});
var BrandHeroSchema = zod.z.object({
  badge: zod.z.string().optional(),
  title: zod.z.string().min(1),
  subtitle: zod.z.string().min(1),
  primaryCtaLabel: zod.z.string().min(1),
  secondaryCtaLabel: zod.z.string().optional(),
  secondaryCtaHref: zod.z.string().optional()
});
var BrandFaqSchema = zod.z.object({
  eyebrow: zod.z.string().optional(),
  title: zod.z.string().min(1),
  sections: zod.z.array(
    zod.z.object({
      title: zod.z.string(),
      items: zod.z.array(zod.z.object({ q: zod.z.string(), a: zod.z.string() }))
    })
  )
});
var BrandSchema = registerSchema(
  zod.z.object({
    // Identity
    name: zod.z.string().min(1),
    shortName: zod.z.string().min(1),
    microTag: zod.z.string(),
    description: zod.z.string().min(20, "description should be at least 20 characters"),
    schemaType: zod.z.string().min(1),
    currency: CurrencyCodeSchema,
    locale: LocaleSchema,
    contact: BrandContactSchema,
    socials: zod.z.array(BrandSocialSchema),
    header: zod.z.object({ nav: zod.z.array(BrandNavLinkSchema).min(1) }),
    hero: BrandHeroSchema,
    // Optional sections — render conditionally when present
    trustItems: zod.z.array(
      zod.z.object({
        label: zod.z.string().min(1),
        value: zod.z.string().min(1),
        description: zod.z.string().min(1),
        iconKey: zod.z.string().min(1)
      })
    ).optional(),
    brandStrip: zod.z.object({ headline: zod.z.string(), brands: zod.z.array(zod.z.string()).min(1) }).optional(),
    promo: zod.z.object({
      badge: zod.z.string().optional(),
      title: zod.z.string().min(1),
      body: zod.z.string().min(1),
      ctaLabel: zod.z.string().min(1),
      ctaHref: zod.z.string().min(1)
    }).optional(),
    // Standalone policy pages
    terms: BrandPolicyPageSchema,
    privacy: BrandPolicyPageSchema,
    shipping: BrandPolicyPageSchema,
    returns: BrandPolicyPageSchema,
    accessibility: BrandPolicyPageSchema,
    // Misc
    newsletter: zod.z.object({
      eyebrow: zod.z.string().optional(),
      title: zod.z.string().min(1),
      body: zod.z.string()
    }),
    about: zod.z.object({
      eyebrow: zod.z.string().optional(),
      title: zod.z.string().min(1),
      paragraphs: zod.z.array(zod.z.string().min(1)).min(1),
      sections: zod.z.array(zod.z.object({ heading: zod.z.string().min(1), body: zod.z.string().min(1) })).optional()
    }),
    faq: BrandFaqSchema,
    account: zod.z.object({
      loginEyebrow: zod.z.string().min(1),
      loginTitle: zod.z.string().min(1),
      loginSubtitle: zod.z.string().optional(),
      signupEyebrow: zod.z.string().min(1),
      signupTitle: zod.z.string().min(1),
      signupSubtitle: zod.z.string().optional(),
      accountEyebrow: zod.z.string().min(1),
      accountTitle: zod.z.string().min(1)
    }),
    contactPage: zod.z.object({
      eyebrow: zod.z.string().optional(),
      title: zod.z.string().min(1),
      body: zod.z.string()
    }),
    trackOrder: zod.z.object({
      eyebrow: zod.z.string().optional(),
      title: zod.z.string().min(1),
      body: zod.z.string()
    }),
    footer: zod.z.object({
      blurb: zod.z.string().optional(),
      sitemap: zod.z.array(zod.z.unknown()).optional(),
      poweredBy: zod.z.object({ label: zod.z.string().min(1), href: zod.z.string().min(1) }).optional()
    }),
    llms: zod.z.object({ summary: zod.z.string().min(20) }),
    mock: zod.z.object({
      seed: SeedNameSchema,
      businessId: zod.z.string().regex(/^bus_/, "businessId should start with 'bus_'")
    })
  }),
  {
    id: "Brand",
    description: "Single-source-of-truth contract for lib/brand.ts in storefront templates",
    version: "1.0.0"
  }
);
var assertBrand = makeAssertion(BrandSchema, "brand");
var BundleSelectionInputSchema = zod.z.object({
  component_id: zod.z.string(),
  variant_id: zod.z.string().optional(),
  quantity: zod.z.number().int().positive(),
  scheduled_start: zod.z.string().optional(),
  scheduled_end: zod.z.string().optional()
});
var CompositeSelectionInputSchema = zod.z.object({
  component_id: zod.z.string().optional(),
  source_product_id: zod.z.string().optional(),
  source_stock_id: zod.z.string().optional(),
  source_type: zod.z.enum(["product", "stock", "add_on", "standalone"]).optional(),
  quantity: zod.z.number().int().positive().optional()
});
var CustomerInputValueSchema = zod.z.object({
  field_id: zod.z.string(),
  field_name: zod.z.string().optional(),
  field_type: zod.z.string().optional(),
  value: zod.z.unknown()
});
var AddItemPayloadSchema = registerSchema(
  zod.z.object({
    item_id: zod.z.string().min(1),
    quantity: zod.z.number().int().positive(),
    variant_id: zod.z.string().optional(),
    quote_id: zod.z.string().optional(),
    add_on_options: zod.z.array(zod.z.string()).optional(),
    bundle_selections: zod.z.array(BundleSelectionInputSchema).optional(),
    composite_selections: zod.z.array(CompositeSelectionInputSchema).optional(),
    scheduled_start: zod.z.string().optional(),
    scheduled_end: zod.z.string().optional(),
    staff_id: zod.z.string().optional(),
    resource_id: zod.z.string().optional(),
    special_instructions: zod.z.string().optional(),
    customer_inputs: zod.z.array(CustomerInputValueSchema).optional(),
    billing_plan_id: zod.z.string().optional(),
    metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
  }),
  {
    id: "AddItemPayload",
    description: "POST /cart/items request body \u2014 strict on shape, lenient on optional fields",
    version: "1.0.0"
  }
);
var assertAddItemPayload = makeAssertion(AddItemPayloadSchema, "payload");
var VariantDisplayAttributeSchema = zod.z.object({
  axis_id: zod.z.string(),
  axis_name: zod.z.string(),
  value_id: zod.z.string(),
  value_name: zod.z.string()
});
var VariantInfoSchema = registerSchema(
  zod.z.object({
    id: zod.z.string(),
    name: zod.z.string(),
    sku: zod.z.string().optional(),
    price: MoneySchema,
    price_adjustment: MoneySchema,
    is_default: zod.z.boolean(),
    display_attributes: zod.z.array(VariantDisplayAttributeSchema).optional(),
    images: zod.z.array(zod.z.string()).optional()
  }),
  {
    id: "VariantInfo",
    description: "Variant axis values + display attributes \u2014 emitted on cart line items",
    version: "1.1.0"
  }
);
var VariantDetailsSchema = zod.z.object({
  variant_id: zod.z.string(),
  sku: zod.z.string().optional(),
  properties: zod.z.record(zod.z.string(), zod.z.string())
});
var ChosenPriceSchema = zod.z.object({
  base_price: MoneySchema,
  final_price: MoneySchema,
  markup_percentage: MoneySchema,
  markup_amount: MoneySchema,
  markup_discount_percentage: MoneySchema,
  markup_discount_amount: MoneySchema
});
var SelectedAddOnOptionSchema = registerSchema(
  zod.z.object({
    option_id: zod.z.string(),
    add_on_id: zod.z.string(),
    name: zod.z.string(),
    price: MoneySchema,
    quantity: zod.z.number().int().nonnegative(),
    is_required: zod.z.boolean(),
    selected_at: TimestampSchema,
    price_info: zod.z.unknown().optional()
  }),
  {
    id: "SelectedAddOnOption",
    description: "A specific add-on option chosen for a cart line",
    version: "1.0.0"
  }
);
var AddOnInDetailsSchema = registerSchema(
  zod.z.object({
    add_on_id: zod.z.string(),
    name: zod.z.string(),
    min_selections: zod.z.number().int().nonnegative(),
    max_selections: zod.z.number().int().nonnegative(),
    selected_options: zod.z.array(zod.z.string()),
    is_required: zod.z.boolean()
  }),
  {
    id: "AddOnInDetails",
    description: "Add-on group as embedded inside add_on_details \u2014 selected_options is an ID list",
    version: "1.0.0"
  }
);
var AddOnDetailsSchema = registerSchema(
  zod.z.object({
    selected_options: zod.z.array(SelectedAddOnOptionSchema),
    total_add_on_price: MoneySchema,
    add_ons: zod.z.array(AddOnInDetailsSchema)
  }),
  {
    id: "AddOnDetails",
    description: "Combined add-on selection state for a cart line",
    version: "1.0.0"
  }
);
var AddOnOptionDetailsSchema = registerSchema(
  zod.z.object({
    id: zod.z.string(),
    name: zod.z.string(),
    is_required: zod.z.boolean(),
    description: zod.z.string().optional(),
    price: MoneySchema.optional(),
    image_url: zod.z.string().optional()
  }),
  {
    id: "AddOnOptionDetails",
    description: "Option-level details (one per chosen add-on option)",
    version: "1.0.0"
  }
);
var AddOnGroupDetailsSchema = registerSchema(
  zod.z.object({
    id: zod.z.string(),
    name: zod.z.string(),
    is_multiple_allowed: zod.z.boolean(),
    min_selections: zod.z.number().int().nonnegative(),
    max_selections: zod.z.number().int().nonnegative(),
    required: zod.z.boolean(),
    options: zod.z.array(AddOnOptionDetailsSchema)
  }),
  {
    id: "AddOnGroupDetails",
    description: "Group-level details for an add-on; contains its options",
    version: "1.0.0"
  }
);
var BundleResolvedSchema = zod.z.looseObject({
  bundle_id: zod.z.string(),
  selections: zod.z.array(zod.z.unknown())
});
var CompositeResolvedSchema = zod.z.looseObject({
  composite_id: zod.z.string(),
  selections: zod.z.array(zod.z.unknown()),
  breakdown: zod.z.object({
    base_price: MoneySchema,
    components_total: MoneySchema,
    final_price: MoneySchema,
    tier_applied: zod.z.string().optional()
  }).optional()
});
var DiscountDetailsSchema = zod.z.object({
  applied_count: zod.z.number().int().nonnegative(),
  total_amount: MoneySchema,
  discount_ids: zod.z.array(zod.z.string())
});
var CartItemSchema = registerSchema(
  zod.z.looseObject({
    id: zod.z.string(),
    cart_id: zod.z.string(),
    item_id: zod.z.string(),
    quantity: zod.z.number().int().nonnegative(),
    line_key: zod.z.string(),
    line_type: LineTypeSchema,
    name: zod.z.string(),
    description: zod.z.string().optional(),
    image_url: zod.z.string().optional(),
    category_id: zod.z.string().optional(),
    category_name: zod.z.string().optional(),
    is_available: zod.z.boolean(),
    variant_id: zod.z.string().optional(),
    variant_name: zod.z.string().optional(),
    variant_info: VariantInfoSchema.optional(),
    variant_details: VariantDetailsSchema.optional(),
    base_price: MoneySchema,
    add_ons_price: MoneySchema,
    total_price: MoneySchema,
    item_discount_amount: MoneySchema,
    price_info: ChosenPriceSchema,
    add_on_option_ids: zod.z.array(zod.z.string()),
    add_on_ids: zod.z.array(zod.z.string()),
    add_on_details: AddOnDetailsSchema,
    add_on_options: zod.z.array(AddOnOptionDetailsSchema),
    add_ons: zod.z.array(AddOnGroupDetailsSchema),
    scheduled_start: zod.z.string().optional(),
    scheduled_end: zod.z.string().optional(),
    staff_id: zod.z.string().optional(),
    resource_id: zod.z.string().optional(),
    service_status: ServiceStatusSchema.optional(),
    confirmation_code: zod.z.string().optional(),
    special_instructions: zod.z.string().optional(),
    customer_inputs: zod.z.array(CustomerInputValueSchema).optional(),
    bundle_selections: zod.z.unknown().optional(),
    bundle_resolved: BundleResolvedSchema.optional(),
    composite_selections: zod.z.unknown().optional(),
    composite_resolved: CompositeResolvedSchema.optional(),
    billing_plan_id: zod.z.string().optional(),
    applied_discount_ids: zod.z.array(zod.z.string()),
    discount_details: DiscountDetailsSchema.optional(),
    metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
    created_at: TimestampSchema,
    updated_at: TimestampSchema
  }),
  {
    id: "CartItem",
    description: "Cart line item \u2014 loose so backend additions don't break old SDK clients",
    version: "1.0.0"
  }
);
var assertCartItem = makeAssertion(CartItemSchema, "cartItem");
var CartPricingSchema = registerSchema(
  zod.z.object({
    subtotal: MoneySchema,
    total_discounts: MoneySchema,
    tax_amount: MoneySchema,
    service_charge: MoneySchema,
    total_price: MoneySchema,
    currency: CurrencyCodeSchema,
    tax_rate: MoneySchema.optional(),
    service_charge_rate: MoneySchema.optional(),
    tax_inclusive: zod.z.boolean(),
    deposit_required: zod.z.boolean(),
    deposit_amount: MoneySchema
  }),
  {
    id: "CartPricing",
    description: "Cart-level pricing roll-up \u2014 subtotal, tax, service, total",
    version: "1.0.0"
  }
);
var CartSchema = registerSchema(
  zod.z.looseObject({
    id: zod.z.string(),
    business_id: zod.z.string(),
    items: zod.z.array(CartItemSchema),
    pricing: CartPricingSchema,
    customer_info: zod.z.object({
      name: zod.z.string(),
      email: zod.z.string().optional(),
      phone: zod.z.string().optional(),
      address: zod.z.string().optional()
    }),
    created_at: TimestampSchema,
    updated_at: TimestampSchema,
    expires_at: TimestampSchema,
    status: zod.z.string(),
    source: zod.z.string(),
    channel: zod.z.string()
  }),
  {
    id: "Cart",
    description: "Full cart envelope \u2014 loose for forward-compat with backend additions",
    version: "1.0.0"
  }
);
var assertCart = makeAssertion(CartSchema, "cart");
var CheckoutCustomerSchema = zod.z.object({
  name: zod.z.string().min(1),
  email: zod.z.string().email(),
  phone: zod.z.string().min(1),
  notes: zod.z.string().optional(),
  save_details: zod.z.boolean().optional()
});
var CheckoutBodySchema = registerSchema(
  zod.z.object({
    cart_id: zod.z.string().min(1),
    location_id: zod.z.string().optional(),
    customer: CheckoutCustomerSchema,
    order_type: zod.z.string().min(1),
    address_info: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
    payment_method: zod.z.string().min(1),
    mobile_money_details: zod.z.object({ phone_number: zod.z.string(), provider: zod.z.string() }).optional(),
    special_instructions: zod.z.string().optional(),
    metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
    pay_currency: zod.z.string().optional(),
    fx_quote_id: zod.z.string().optional()
  }),
  {
    id: "CheckoutBody",
    description: "POST /checkout request body",
    version: "1.0.0"
  }
);
var assertCheckoutBody = makeAssertion(CheckoutBodySchema, "checkoutBody");
var AuthorizationTypeSchema = zod.z.enum(["otp", "pin", "phone", "birthday", "address"]);
var NextActionSchema = zod.z.discriminatedUnion("type", [
  zod.z.object({ type: zod.z.literal("none") }),
  zod.z.object({ type: zod.z.literal("redirect"), authorization_url: zod.z.string() }),
  zod.z.object({
    type: zod.z.literal("authorization"),
    authorization_type: zod.z.string(),
    display_text: zod.z.string().optional()
  })
]);
var CheckoutResponseSchema = registerSchema(
  zod.z.looseObject({
    order_id: zod.z.string().min(1),
    order_number: zod.z.string().min(1),
    payment_status: zod.z.string().min(1),
    payment_reference: zod.z.string().optional(),
    requires_authorization: zod.z.boolean(),
    authorization_type: AuthorizationTypeSchema.optional(),
    next_action: NextActionSchema,
    provider: zod.z.string().optional(),
    authorization_url: zod.z.string().optional(),
    display_text: zod.z.string().optional(),
    bill_token: zod.z.string().optional(),
    client_secret: zod.z.string().optional(),
    public_key: zod.z.string().optional()
  }),
  {
    id: "CheckoutResponse",
    description: "POST /checkout response \u2014 loose for payment-provider field additions",
    version: "1.0.0"
  }
);
var assertCheckoutResponse = makeAssertion(CheckoutResponseSchema, "checkout");

// src/testing/client.ts
var TEST_BASE_URL = "http://test.local";
async function toForwardedRequest(input, init) {
  if (input instanceof Request) {
    return {
      method: init?.method ?? input.method,
      headers: new Headers(init?.headers ?? input.headers),
      body: init?.body ?? (["GET", "HEAD"].includes(input.method) ? void 0 : await input.text())
    };
  }
  return {
    method: init?.method ?? "GET",
    headers: new Headers(init?.headers ?? {}),
    body: init?.body ?? void 0
  };
}
function createTestClient(options = {}) {
  const mock = chunkSRJEMEVZ_js.createMockApp(options);
  let sessionToken;
  const fetchImpl = async (input, init) => {
    const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
    if (!url.startsWith(TEST_BASE_URL)) {
      return globalThis.fetch(input, init);
    }
    const path = url.slice(TEST_BASE_URL.length);
    const fwd = await toForwardedRequest(input, init);
    if (sessionToken) fwd.headers.set("x-session-token", sessionToken);
    const res = await mock.request(path, {
      method: fwd.method,
      headers: fwd.headers,
      body: fwd.body
    });
    const echoed = res.headers.get("x-session-token");
    if (echoed) sessionToken = echoed;
    return res;
  };
  const client = chunkGFM5JW45_js.createCimplifyClient({
    baseUrl: TEST_BASE_URL,
    // Link mock lives at /v1/link on the same Hono app, so point the SDK's
    // separate Link transport at the same TEST_BASE_URL.
    linkApiUrl: TEST_BASE_URL,
    publicKey: "test-public-key",
    suppressPublicKeyWarning: true,
    fetch: fetchImpl
  });
  return {
    client,
    mock,
    reset: () => {
      mock.deps.resetAll();
      sessionToken = void 0;
    },
    dispose: () => {
      mock.deps.resetAll();
      sessionToken = void 0;
    }
  };
}
async function pickProduct(client, opts) {
  const list = await client.catalogue.getProducts();
  if (!list.ok) throw list.error;
  const body = list.value;
  const products = body.items ?? [];
  if (products.length === 0) throw new Error("No products in catalogue (wrong seed?)");
  if (opts.slug) {
    const found = products.find((p) => p.slug === opts.slug);
    if (!found) throw new Error(`Product slug "${opts.slug}" not found in catalogue`);
    return found;
  }
  return products.find((p) => p.type === "product") ?? products[0];
}
var fixtures = {
  /**
   * Add the first available product (or a named one) to the cart.
   * Picks the first variant if the product has variants.
   */
  async addFirstProduct(client, opts = {}) {
    const product = await pickProduct(client, opts);
    let variantId;
    if (opts.withVariant !== false) {
      const variants = await client.catalogue.getVariants(product.id);
      if (variants.ok && variants.value.length > 0) {
        variantId = variants.value[0]?.id;
      }
    }
    const res = await client.cart.addItem({
      item_id: product.id,
      quantity: 1,
      variant_id: variantId
    });
    if (!res.ok) throw res.error;
    return { product, variantId };
  },
  /** A minimal valid customer object for checkout. */
  customer(overrides = {}) {
    return {
      name: overrides.name ?? "Test Customer",
      email: overrides.email ?? "test@example.com",
      phone: overrides.phone ?? "+233244000000"
    };
  },
  /**
   * Run the full guest auth flow (request OTP → verify) and return
   * the new session token. The mock's permissive auth mode echoes
   * the code in `request_otp`, so we don't need to read it.
   */
  async authenticate(client, contact = "test@example.com", code = "123456") {
    const req = await client.auth.requestOtp(contact);
    if (!req.ok) throw req.error;
    const verify = await client.auth.verifyOtp(contact, code);
    if (!verify.ok) throw verify.error;
    const value = verify.value;
    return { token: value.session_token ?? "", customerId: value.customer_id ?? "" };
  }
};

exports.AddItemPayloadSchema = AddItemPayloadSchema;
exports.AddOnDetailsSchema = AddOnDetailsSchema;
exports.AddOnGroupDetailsSchema = AddOnGroupDetailsSchema;
exports.AddOnInDetailsSchema = AddOnInDetailsSchema;
exports.AddOnOptionDetailsSchema = AddOnOptionDetailsSchema;
exports.AuthorizationTypeSchema = AuthorizationTypeSchema;
exports.BrandContactSchema = BrandContactSchema;
exports.BrandFaqSchema = BrandFaqSchema;
exports.BrandHeroSchema = BrandHeroSchema;
exports.BrandNavLinkSchema = BrandNavLinkSchema;
exports.BrandPolicyPageSchema = BrandPolicyPageSchema;
exports.BrandPolicySectionSchema = BrandPolicySectionSchema;
exports.BrandSchema = BrandSchema;
exports.BrandSocialSchema = BrandSocialSchema;
exports.BundleResolvedSchema = BundleResolvedSchema;
exports.BundleSelectionInputSchema = BundleSelectionInputSchema;
exports.CartItemSchema = CartItemSchema;
exports.CartPricingSchema = CartPricingSchema;
exports.CartSchema = CartSchema;
exports.CheckoutBodySchema = CheckoutBodySchema;
exports.CheckoutCustomerSchema = CheckoutCustomerSchema;
exports.CheckoutResponseSchema = CheckoutResponseSchema;
exports.ChosenPriceSchema = ChosenPriceSchema;
exports.CompositeResolvedSchema = CompositeResolvedSchema;
exports.CompositeSelectionInputSchema = CompositeSelectionInputSchema;
exports.CountryCodeSchema = CountryCodeSchema;
exports.CurrencyCodeSchema = CurrencyCodeSchema;
exports.CustomerInputValueSchema = CustomerInputValueSchema;
exports.DiscountDetailsSchema = DiscountDetailsSchema;
exports.IsoDateTimeSchema = IsoDateTimeSchema;
exports.LineTypeSchema = LineTypeSchema;
exports.LocaleSchema = LocaleSchema;
exports.MoneyLooseSchema = MoneyLooseSchema;
exports.MoneySchema = MoneySchema;
exports.NextActionSchema = NextActionSchema;
exports.PhoneE164Schema = PhoneE164Schema;
exports.ProductTypeSchema = ProductTypeSchema;
exports.SchemaViolationError = SchemaViolationError;
exports.SeedNameSchema = SeedNameSchema;
exports.SelectedAddOnOptionSchema = SelectedAddOnOptionSchema;
exports.ServiceStatusSchema = ServiceStatusSchema;
exports.TimestampSchema = TimestampSchema;
exports.VariantDetailsSchema = VariantDetailsSchema;
exports.VariantDisplayAttributeSchema = VariantDisplayAttributeSchema;
exports.VariantInfoSchema = VariantInfoSchema;
exports.assertAddItemPayload = assertAddItemPayload;
exports.assertBrand = assertBrand;
exports.assertCart = assertCart;
exports.assertCartItem = assertCartItem;
exports.assertCheckoutBody = assertCheckoutBody;
exports.assertCheckoutResponse = assertCheckoutResponse;
exports.cimplifyRegistry = cimplifyRegistry;
exports.createTestClient = createTestClient;
exports.fixtures = fixtures;
exports.makeAssertion = makeAssertion;
exports.registerSchema = registerSchema;
