@nymbalhq/types 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1889,6 +1889,8 @@ type ChatStreamEvent = {
1889
1889
  };
1890
1890
  interface AiAdapter extends CommerceAdapter {
1891
1891
  readonly kind: 'ai';
1892
+ /** The base URL its requests go to, for `nymbal doctor`; never a key. Absent when the adapter does not say. */
1893
+ readonly endpoint?: string;
1892
1894
  embed(texts: string[]): Promise<number[][]>;
1893
1895
  /** The text `chat` answers to a single user turn. */
1894
1896
  complete(prompt: string, opts?: {
@@ -2133,8 +2135,14 @@ interface AsOfRead<T = Record<string, unknown>> {
2133
2135
  */
2134
2136
  type AssistantFrame = {
2135
2137
  event: 'text';
2138
+ /** `revealed`: where in `delta` a customer's value stands that the provider saw only as a placeholder. */
2136
2139
  data: {
2137
2140
  delta: string;
2141
+ revealed?: Array<{
2142
+ start: number;
2143
+ end: number;
2144
+ kind: 'email' | 'phone' | 'address' | 'postcode' | 'name';
2145
+ }>;
2138
2146
  };
2139
2147
  } | {
2140
2148
  event: 'tool';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/domain/money.ts","../src/domain/product.ts","../src/domain/cart.ts","../src/domain/order.ts","../src/domain/order-timeline.ts","../src/events/constants.ts","../src/adapters/adapter-base.ts","../src/adapters/document-order.ts","../src/adapters/event-bus.ts","../src/adapters/http.ts","../src/adapters/security.ts","../src/adapters/commerce.ts","../src/adapters/webhook-delivery.ts","../src/adapters/email-feedback.ts","../src/adapters/shipping-tracking.ts","../src/adapters/email-templates.ts","../src/stock-level.ts","../src/audit.ts","../src/proposals.ts"],"sourcesContent":["export type ErrorContext = Record<string, unknown>\n\nexport class NymbalError extends Error {\n readonly code: string\n readonly context: ErrorContext\n\n constructor(\n code: string,\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined)\n this.name = this.constructor.name\n this.code = code\n this.context = options.context ?? {}\n }\n\n toJSON(): { name: string; code: string; message: string; context: ErrorContext } {\n return { name: this.name, code: this.code, message: this.message, context: this.context }\n }\n}\n\nexport class ConfigError extends NymbalError {\n constructor(\n message: string,\n options: {\n cause?: unknown\n context?: ErrorContext\n code?: `config.${string}` | `adapter.${string}`\n } = {},\n ) {\n super(options.code ?? 'config.invalid', message, options)\n }\n}\n\nexport class ValidationError extends NymbalError {\n constructor(\n message: string,\n options: { cause?: unknown; context?: ErrorContext; code?: string } = {},\n ) {\n super(options.code ?? 'validation.failed', message, options)\n }\n}\n\nexport class NotFoundError extends NymbalError {\n constructor(\n resource: string,\n identifier: unknown,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super('not_found', `${resource} not found: ${String(identifier)}`, {\n ...options,\n context: { resource, identifier, ...(options.context ?? {}) },\n })\n }\n}\n\nexport class AdapterError extends NymbalError {\n constructor(\n code: string,\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`adapter.${code}`, message, options)\n }\n}\n\nexport class HttpError extends NymbalError {\n readonly status: number\n\n constructor(\n status: number,\n message: string,\n options: { cause?: unknown; context?: ErrorContext; code?: string } = {},\n ) {\n super(options.code ?? `http.${status}`, message, options)\n this.status = status\n }\n}\n\nexport class AuthError extends HttpError {\n constructor(\n code: 'unauthorized' | 'forbidden' | 'invalid_credentials' | 'token_expired' | 'token_invalid',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n const status = code === 'forbidden' ? 403 : 401\n super(status, message, { ...options, code: `auth.${code}` })\n }\n}\n\nexport class PaymentError extends NymbalError {\n constructor(\n code:\n | 'intent_failed'\n | 'capture_failed'\n // A definite refusal: no money moved.\n | 'refund_rejected'\n // An outcome the shop cannot know (a timeout, a provider fault): money may have moved.\n | 'refund_unknown'\n | 'cancel_refused'\n | 'webhook_invalid'\n | 'not_configured',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`payment.${code}`, message, options)\n }\n}\n\n/** A command the order's money does not allow (confirm unpaid, cancel paid, refund beyond the capture). */\nexport class OrderConflictError extends NymbalError {\n constructor(\n code:\n | 'order.unpaid'\n | 'order.paid'\n | 'order.stock_unavailable'\n | 'order.payment_in_progress'\n | 'refund.not_captured'\n | 'refund.exceeds_captured'\n | 'refund.request_reused',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(code, message, options)\n }\n}\n\nexport class CheckoutError extends NymbalError {\n constructor(\n code:\n | 'cart_empty'\n | 'in_progress'\n | 'payment_in_progress'\n | 'variant_missing'\n | 'out_of_stock'\n | 'reservation_failed'\n | 'finalize_failed',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`checkout.${code}`, message, options)\n }\n}\n\n/** A cart command that would take a line above `max`; `onCart` is what the line held before it. */\nexport class CartLimitError extends NymbalError {\n constructor(variantId: string, max: number, onCart: number) {\n super('cart.qty_limit', `a cart line holds at most ${max}`, {\n context: { variantId, max, onCart },\n })\n }\n}\n\n/** Why a review was not taken. `not_a_buyer` is reserved: any signed-in customer may review. */\nexport type ReviewRefusal = 'submit_unsupported' | 'already_reviewed' | 'not_a_buyer' | 'rejected'\n\nconst REVIEW_REFUSAL_MESSAGES: Record<ReviewRefusal, string> = {\n submit_unsupported: 'This store does not take reviews on its site.',\n already_reviewed: 'You have already reviewed this product.',\n not_a_buyer: 'Only a customer who bought this product can review it.',\n rejected: 'The review was not accepted.',\n}\n\nexport class ReviewRefusedError extends NymbalError {\n readonly refusal: ReviewRefusal\n\n constructor(\n refusal: ReviewRefusal,\n message: string = REVIEW_REFUSAL_MESSAGES[refusal],\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`reviews.${refusal}`, message, options)\n this.refusal = refusal\n }\n}\n\nexport class StatusTransitionError extends NymbalError {\n constructor(\n entity: string,\n fromStatus: string,\n toStatus: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(\n 'status.invalid_transition',\n `Invalid ${entity} status transition: ${fromStatus} → ${toStatus}`,\n {\n ...options,\n context: { entity, fromStatus, toStatus, ...(options.context ?? {}) },\n },\n )\n }\n}\n","import { ValidationError } from '../errors.js'\n\nexport interface Money {\n amount: number\n currency: string\n}\n\nexport function money(amount: number, currency: string): Money {\n assertMinorUnits(amount)\n return { amount, currency }\n}\n\nexport function assertMinorUnits(amount: number): void {\n if (!Number.isInteger(amount)) {\n throw new ValidationError(`Monetary amount must be an integer (minor units), got ${amount}`)\n }\n}\n\nfunction assertSameCurrency(a: Money, b: Money): void {\n if (a.currency !== b.currency) {\n throw new ValidationError(\n `Currency mismatch: ${a.currency} vs ${b.currency}`,\n )\n }\n}\n\nexport function add(a: Money, b: Money): Money {\n assertSameCurrency(a, b)\n return { amount: a.amount + b.amount, currency: a.currency }\n}\n\nexport function sub(a: Money, b: Money): Money {\n assertSameCurrency(a, b)\n return { amount: a.amount - b.amount, currency: a.currency }\n}\n\nexport function mulBy(m: Money, scalar: number): Money {\n if (!Number.isFinite(scalar)) {\n throw new ValidationError(`Scalar must be finite, got ${scalar}`)\n }\n return { amount: Math.round(m.amount * scalar), currency: m.currency }\n}\n\nexport function sumMoney(values: Money[], currency: string): Money {\n const total = values.reduce((acc, m) => {\n assertSameCurrency(m, { amount: 0, currency })\n return acc + m.amount\n }, 0)\n return { amount: total, currency }\n}\n\nexport function allocate(m: Money, weights: number[]): Money[] {\n if (weights.length === 0) return []\n const totalWeight = weights.reduce((s, w) => s + w, 0)\n if (totalWeight <= 0) {\n throw new ValidationError('allocate: sum of weights must be > 0')\n }\n const portions = weights.map((w) => Math.floor((m.amount * w) / totalWeight))\n let remainder = m.amount - portions.reduce((s, p) => s + p, 0)\n const result = portions.slice()\n let idx = 0\n while (remainder !== 0 && idx < result.length) {\n const delta = remainder > 0 ? 1 : -1\n result[idx] = (result[idx] ?? 0) + delta\n remainder -= delta\n idx += 1\n }\n return result.map((amount) => ({ amount, currency: m.currency }))\n}\n","export type ProductStatus = 'draft' | 'active' | 'archived'\nexport type ProductType = 'simple' | 'variable'\nexport type VariantStatus = 'active' | 'inactive'\n\nexport interface ProductMedia {\n url: string\n altText: string\n position: number\n}\n\nexport interface VariantOption {\n name: string\n value: string\n}\n\nexport interface VariantDimensions {\n lengthMm?: number\n widthMm?: number\n heightMm?: number\n}\n\nexport interface Variant {\n id: string\n productId: string\n sku: string\n name: string\n priceMinor: number\n compareAtPriceMinor: number | null\n weightGrams: number | null\n dimensions: VariantDimensions | null\n stock: number\n lowStockThreshold: number\n options: VariantOption[]\n status: VariantStatus\n createdAt: string\n updatedAt: string\n}\n\nexport interface Product {\n id: string\n slug: string\n name: string\n description: string\n shortDescription: string\n status: ProductStatus\n type: ProductType\n seoTitle: string\n seoDescription: string\n media: ProductMedia[]\n metadata: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\n/** The orders a product listing can be requested in; ties break by slug, ascending. */\nexport const PRODUCT_SORT_KEYS = [\n 'newest',\n 'price-asc',\n 'price-desc',\n 'name-asc',\n 'name-desc',\n] as const\nexport type ProductSortKey = (typeof PRODUCT_SORT_KEYS)[number]\nexport const DEFAULT_PRODUCT_SORT: ProductSortKey = 'newest'\n/** What a storefront shows for each sort. */\nexport const PRODUCT_SORT_LABELS: Readonly<Record<ProductSortKey, string>> = {\n newest: 'Newest',\n 'price-asc': 'Price: Low to High',\n 'price-desc': 'Price: High to Low',\n 'name-asc': 'Name: A to Z',\n 'name-desc': 'Name: Z to A',\n}\n","import type { OrderSource } from './order.js'\n\n/** The most of one variant a cart line holds. */\nexport const CART_LINE_MAX_QTY = 99\n\nexport interface CartItem {\n variantId: string\n productId: string\n /** The product page's slug; lines stored before lines carried one have none. */\n productSlug?: string\n productName: string\n variantName: string\n priceMinor: number\n qty: number\n imageUrl: string\n}\n\n/** Who opened a cart: the storefront or a shopping agent. An import opens none. */\nexport type CartSource = Exclude<OrderSource, 'import'>\n\nexport interface Cart {\n token: string\n customerId: string | null\n items: CartItem[]\n subtotalMinor: number\n currency: string\n source: CartSource\n expiresAt: string\n createdAt: string\n updatedAt: string\n}\n","import { StatusTransitionError } from '../errors.js'\n\nexport type OrderStatus =\n | 'pending'\n | 'confirmed'\n | 'processing'\n | 'shipped'\n | 'delivered'\n | 'cancelled'\n | 'refunded'\n | 'partially_refunded'\n\n// A captured order is refunded from any status between confirmed and delivered; a partial refund leaves room for more.\nexport const ORDER_STATUS_TRANSITIONS: Record<OrderStatus, readonly OrderStatus[]> = {\n pending: ['confirmed', 'cancelled'],\n confirmed: ['processing', 'cancelled', 'refunded', 'partially_refunded'],\n processing: ['shipped', 'cancelled', 'refunded', 'partially_refunded'],\n shipped: ['delivered', 'refunded', 'partially_refunded'],\n delivered: ['refunded', 'partially_refunded'],\n cancelled: [],\n refunded: [],\n partially_refunded: ['refunded'],\n}\n\nexport function canTransitionOrder(from: OrderStatus, to: OrderStatus): boolean {\n return ORDER_STATUS_TRANSITIONS[from].includes(to)\n}\n\nexport function assertOrderTransition(from: OrderStatus, to: OrderStatus): void {\n if (!canTransitionOrder(from, to)) {\n throw new StatusTransitionError('order', from, to)\n }\n}\n\nexport interface Address {\n firstName: string\n lastName: string\n company?: string\n addressLine1: string\n addressLine2?: string\n city: string\n region: string\n postalCode: string\n country: string\n phone?: string\n}\n\nexport interface OrderLineItem {\n variantId: string\n productId: string\n productName: string\n variantName: string\n sku: string\n qty: number\n unitPriceMinor: number\n lineSubtotalMinor: number\n lineTaxMinor: number\n lineTotalMinor: number\n imageUrl: string\n}\n\n/** Where an order came from: the storefront, an import, or the shopping agent that opened its cart. */\nexport type OrderSource = 'storefront' | 'import' | `agent:${string}`\n\n/** The source a cart or order opened by the named agent carries. */\nexport function agentSource(name: string): `agent:${string}` {\n return `agent:${name}`\n}\n\nexport interface Order {\n id: string\n orderNumber: string\n sequence: number\n customerId: string | null\n status: OrderStatus\n email: string\n billingAddress: Address\n shippingAddress: Address\n lineItems: OrderLineItem[]\n subtotalMinor: number\n taxTotalMinor: number\n shippingTotalMinor: number\n discountTotalMinor: number\n totalMinor: number\n currency: string\n notes: string\n metadata: Record<string, unknown>\n paymentIntentId: string | null\n /** Money taken for the order, in minor units of its currency; 0 while nothing is captured. */\n capturedMinor: number\n capturedAt: string | null\n /** Σ of the order's succeeded refunds (or, for an import or a pre-refund-table order, what was refunded then). */\n refundedMinor: number\n captureMethod: CaptureMethod | null\n source: OrderSource\n createdAt: string\n updatedAt: string\n}\n\n/**\n * The request header a shopper's order key travels in to `GET /api/orders/:orderNumber`. Never a query parameter, so\n * the key stays out of access logs that record request URLs.\n */\nexport const ORDER_KEY_HEADER = 'x-nymbal-order-key'\n\n/** The request header that carries the order's email beside an expired order key. Never a query parameter either. */\nexport const ORDER_EMAIL_HEADER = 'x-nymbal-order-email'\n\n/**\n * The API's attestation of a shopper's address on a page request it forwards to the storefront. The storefront server\n * echoes it on the API calls it makes for that shopper, so they count against the shopper's address, not its own.\n */\nexport const SHOPPER_ATTESTATION_HEADER = 'x-nymbal-shopper'\n\n/** Who took the money: the payments provider, the merchant recording an offline payment, or an imported history. */\nexport type CaptureMethod = 'provider' | 'manual' | 'import'\n\nexport type OrderRefundStatus = 'pending' | 'succeeded' | 'failed'\n\n/** One refund of an order. `pending` until the provider's answer settles it, and counted against what is refundable. */\nexport interface OrderRefund {\n id: string\n orderId: string\n amountMinor: number\n currency: string\n providerRefundId: string | null\n status: OrderRefundStatus\n reason: string | null\n actor: string\n createdAt: string\n}\n\nexport type PaymentState = 'unpaid' | 'paid' | 'partially_refunded' | 'refunded'\n\n/** What the admin shows about an order's money: derived from the stored amounts and refunds alone. */\nexport interface OrderPayment {\n state: PaymentState\n capturedMinor: number\n refundedMinor: number\n refundableMinor: number\n currency: string\n refunds: OrderRefund[]\n}\n\nexport function paymentState(order: Pick<Order, 'capturedMinor' | 'refundedMinor'>): PaymentState {\n if (order.capturedMinor <= 0) return 'unpaid'\n if (order.refundedMinor <= 0) return 'paid'\n return order.refundedMinor < order.capturedMinor ? 'partially_refunded' : 'refunded'\n}\n\n/** What can still be refunded: the capture less what was refunded and what is still pending with the provider. */\nexport function refundableMinor(\n order: Pick<Order, 'capturedMinor' | 'refundedMinor'>,\n refunds: ReadonlyArray<Pick<OrderRefund, 'amountMinor' | 'status'>>,\n): number {\n const pending = refunds\n .filter((r) => r.status === 'pending')\n .reduce((sum, r) => sum + r.amountMinor, 0)\n return Math.max(0, order.capturedMinor - order.refundedMinor - pending)\n}\n\nexport interface OrderHistoryEntry {\n id: string\n orderId: string\n fromStatus: OrderStatus | null\n toStatus: OrderStatus\n actor: string\n note: string\n timestamp: string\n}\n","import type { Address, Order } from './order.js'\n\n/** The lifecycle events an order's timeline shows, each one from an `order_history` row. */\nexport const ORDER_TIMELINE_KINDS = [\n 'placed',\n 'paid',\n 'shipped',\n 'delivered',\n 'cancelled',\n 'partially_refunded',\n 'refunded',\n] as const\n\nexport type OrderTimelineKind = (typeof ORDER_TIMELINE_KINDS)[number]\n\nexport interface OrderTimelineEntry {\n kind: OrderTimelineKind\n /** ISO 8601, UTC. */\n at: string\n}\n\n/** A shipping address as an order's key holder reads it: no phone, no company. */\nexport type ShopperAddress = Omit<Address, 'phone' | 'company'>\n\n/** What only the order's signed-in owner reads, or the store's own records, beside what its key holder reads. */\ntype OwnerOrStoreField =\n | 'id'\n | 'email'\n | 'billingAddress'\n | 'customerId'\n | 'sequence'\n | 'capturedMinor'\n | 'capturedAt'\n | 'refundedMinor'\n | 'captureMethod'\n | 'notes'\n | 'metadata'\n | 'paymentIntentId'\n | 'source'\n | 'updatedAt'\n\n/**\n * An order as a storefront reads it. A key holder reads the confirmation page's fields; its owner's session adds the\n * email, both full addresses and the order's id. Every other field is optional, so an adapter that returns a plain\n * `Order` still fits.\n */\nexport interface ShopperOrder\n extends\n Omit<Order, OwnerOrStoreField | 'shippingAddress'>,\n Partial<Pick<Order, OwnerOrStoreField>> {\n shippingAddress: ShopperAddress\n timeline?: OrderTimelineEntry[]\n}\n","// Product\nexport const EVT_PRODUCT_CREATED = 'product.created.v1'\nexport const EVT_PRODUCT_UPDATED = 'product.updated.v1'\nexport const EVT_PRODUCT_DELETED = 'product.deleted.v1'\nexport const EVT_PRODUCT_PUBLISHED = 'product.published.v1'\nexport const EVT_PRODUCT_UNPUBLISHED = 'product.unpublished.v1'\nexport const EVT_PRODUCT_VIEWED = 'product.viewed.v1'\n/** A variant's stock went from 0 to above 0, in the transaction of the inventory.changed.v1 it accompanies. */\nexport const EVT_PRODUCT_RESTOCKED = 'product.restocked.v1'\n/** A variant's price or compare-at price changed, in the transaction of the product.updated.v1 it accompanies. */\nexport const EVT_PRODUCT_VARIANT_PRICE_CHANGED = 'product.variant_price_changed.v1'\n\n// Category\nexport const EVT_CATEGORY_CREATED = 'category.created.v1'\nexport const EVT_CATEGORY_UPDATED = 'category.updated.v1'\nexport const EVT_CATEGORY_DELETED = 'category.deleted.v1'\n\n// Order\nexport const EVT_ORDER_PLACED = 'order.placed.v1'\nexport const EVT_ORDER_PAID = 'order.paid.v1'\nexport const EVT_ORDER_FULFILLED = 'order.fulfilled.v1'\nexport const EVT_ORDER_SHIPPED = 'order.shipped.v1'\nexport const EVT_ORDER_DELIVERED = 'order.delivered.v1'\nexport const EVT_ORDER_CANCELLED = 'order.cancelled.v1'\nexport const EVT_ORDER_REFUNDED = 'order.refunded.v1'\nexport const EVT_ORDER_PARTIALLY_REFUNDED = 'order.partially_refunded.v1'\nexport const EVT_ORDER_STATUS_CHANGED = 'order.status_changed.v1'\nexport const EVT_ORDER_CONFIRMATION_RESEND_REQUESTED = 'order.confirmation_resend_requested.v1'\n/** An imported order taken back out of the store, with every row it owned. */\nexport const EVT_ORDER_DELETED = 'order.deleted.v1'\n\n// Inventory\nexport const EVT_INVENTORY_CHANGED = 'inventory.changed.v1'\nexport const EVT_INVENTORY_RESERVED = 'inventory.reserved.v1'\nexport const EVT_INVENTORY_RELEASED = 'inventory.released.v1'\nexport const EVT_INVENTORY_LOW_STOCK = 'inventory.low_stock.v1'\nexport const EVT_INVENTORY_OUT_OF_STOCK = 'inventory.out_of_stock.v1'\n\n// Customer\nexport const EVT_CUSTOMER_CREATED = 'customer.created.v1'\nexport const EVT_CUSTOMER_UPDATED = 'customer.updated.v1'\nexport const EVT_CUSTOMER_DELETED = 'customer.deleted.v1'\n// Legacy alias (kept so Prompt 1 seed code continues to compile).\nexport const EVT_CUSTOMER_REGISTERED = EVT_CUSTOMER_CREATED\n\n// Visitor\nexport const EVT_VISITOR_MERGED = 'visitor.merged.v1'\n\n// Cart\nexport const EVT_CART_CREATED = 'cart.created.v1'\nexport const EVT_CART_ITEM_ADDED = 'cart.item_added.v1'\nexport const EVT_CART_ITEM_REMOVED = 'cart.item_removed.v1'\nexport const EVT_CART_UPDATED = 'cart.updated.v1'\nexport const EVT_CART_ABANDONED = 'cart.abandoned.v1'\nexport const EVT_CART_CONVERTED = 'cart.converted.v1'\n\n// Payment\nexport const EVT_PAYMENT_INTENT_CREATED = 'payment.intent_created.v1'\nexport const EVT_PAYMENT_CAPTURED = 'payment.captured.v1'\nexport const EVT_PAYMENT_FAILED = 'payment.failed.v1'\nexport const EVT_PAYMENT_REFUNDED = 'payment.refunded.v1'\n\n// Alerts\nexport const EVT_ALERT_REQUESTED = 'alert.requested.v1'\n/** A subscribe for an alert already active: the subscription stands and nothing is sent. */\nexport const EVT_ALERT_REPEATED = 'alert.repeated.v1'\nexport const EVT_ALERT_CONFIRMED = 'alert.confirmed.v1'\nexport const EVT_ALERT_UNSUBSCRIBED = 'alert.unsubscribed.v1'\nexport const EVT_ALERT_SENT = 'alert.sent.v1'\n/** A pending subscription's confirmation was sent; another goes only once the confirmation interval has passed. */\nexport const EVT_ALERT_CONFIRMATION_SENT = 'alert.confirmation_sent.v1'\n\n// Reviews\n/** A review the reviews adapter took for the store; the kernel resolved its reviewer and the order it is verified by. */\nexport const EVT_REVIEW_SUBMITTED = 'review.submitted.v1'\n\n// Proposals: an agent's write a person or a policy decides\nexport const EVT_PROPOSAL_CREATED = 'proposal.created.v1'\nexport const EVT_PROPOSAL_APPROVED = 'proposal.approved.v1'\nexport const EVT_PROPOSAL_REJECTED = 'proposal.rejected.v1'\nexport const EVT_PROPOSAL_APPLIED = 'proposal.applied.v1'\nexport const EVT_PROPOSAL_FAILED = 'proposal.failed.v1'\n\n// System\nexport const EVT_ADAPTER_ERROR = 'adapter.error.v1'\nexport const EVT_SETTINGS_CHANGED = 'settings.changed.v1'\n\nexport const STANDARD_EVENT_TYPES = [\n EVT_PRODUCT_CREATED,\n EVT_PRODUCT_UPDATED,\n EVT_PRODUCT_DELETED,\n EVT_PRODUCT_PUBLISHED,\n EVT_PRODUCT_UNPUBLISHED,\n EVT_PRODUCT_VIEWED,\n EVT_PRODUCT_RESTOCKED,\n EVT_PRODUCT_VARIANT_PRICE_CHANGED,\n EVT_CATEGORY_CREATED,\n EVT_CATEGORY_UPDATED,\n EVT_CATEGORY_DELETED,\n EVT_ORDER_PLACED,\n EVT_ORDER_PAID,\n EVT_ORDER_FULFILLED,\n EVT_ORDER_SHIPPED,\n EVT_ORDER_DELIVERED,\n EVT_ORDER_CANCELLED,\n EVT_ORDER_REFUNDED,\n EVT_ORDER_PARTIALLY_REFUNDED,\n EVT_ORDER_STATUS_CHANGED,\n EVT_ORDER_CONFIRMATION_RESEND_REQUESTED,\n EVT_ORDER_DELETED,\n EVT_INVENTORY_CHANGED,\n EVT_INVENTORY_RESERVED,\n EVT_INVENTORY_RELEASED,\n EVT_INVENTORY_LOW_STOCK,\n EVT_INVENTORY_OUT_OF_STOCK,\n EVT_CUSTOMER_CREATED,\n EVT_CUSTOMER_UPDATED,\n EVT_CUSTOMER_DELETED,\n EVT_VISITOR_MERGED,\n EVT_CART_CREATED,\n EVT_CART_ITEM_ADDED,\n EVT_CART_ITEM_REMOVED,\n EVT_CART_UPDATED,\n EVT_CART_ABANDONED,\n EVT_CART_CONVERTED,\n EVT_PAYMENT_INTENT_CREATED,\n EVT_PAYMENT_CAPTURED,\n EVT_PAYMENT_FAILED,\n EVT_PAYMENT_REFUNDED,\n EVT_ALERT_REQUESTED,\n EVT_ALERT_REPEATED,\n EVT_ALERT_CONFIRMED,\n EVT_ALERT_UNSUBSCRIBED,\n EVT_ALERT_SENT,\n EVT_ALERT_CONFIRMATION_SENT,\n EVT_REVIEW_SUBMITTED,\n EVT_PROPOSAL_CREATED,\n EVT_PROPOSAL_APPROVED,\n EVT_PROPOSAL_REJECTED,\n EVT_PROPOSAL_APPLIED,\n EVT_PROPOSAL_FAILED,\n EVT_ADAPTER_ERROR,\n EVT_SETTINGS_CHANGED,\n] as const\n","import type { Logger } from './http.js'\n\nexport type HealthStatus = 'healthy' | 'degraded' | 'down'\n\nexport interface HealthReport {\n status: HealthStatus\n detail?: string\n}\n\n/** How long a health check may run before it reports `down`. */\nexport const HEALTH_CHECK_TIMEOUT_MS = 5000\n\nexport interface BoundedHealthCheckOptions {\n /** `HEALTH_CHECK_TIMEOUT_MS` when not given. */\n timeoutMs?: number\n /** Values an error's message must never show (a URL's user, password or query values); each becomes `***`. */\n redact?: readonly string[]\n}\n\n/** An error's message for a report; an AggregateError (a refused connection to every address of a name) lists its own. */\nexport function errorText(error: unknown): string {\n if (!(error instanceof Error)) return String(error)\n if (error.message !== '') return error.message\n const inner = (error as { errors?: unknown }).errors\n if (Array.isArray(inner) && inner.length > 0) return inner.map(errorText).join('; ')\n const code = (error as { code?: unknown }).code\n return typeof code === 'string' ? code : error.name\n}\n\n/**\n * The shortest value worth removing from text as a secret. A shorter one is too likely to be an ordinary word (a local\n * password of `nymbal`), whose removal garbles the text; user info and query strings go with the URL rewrite anyway.\n */\nexport const SECRET_MIN_LENGTH = 8\n\n/**\n * The values in a connection URL that are credentials or may carry one: its password and its query values, and its\n * user unless `user` is false, each as written and decoded, if at least `SECRET_MIN_LENGTH` long. A user is worth\n * hiding in a driver's error, not in our own text.\n */\nexport function connectionSecrets(url: string, options: { user?: boolean } = {}): string[] {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return []\n }\n const raw = [\n ...(options.user === false ? [] : [parsed.username]),\n parsed.password,\n ...parsed.searchParams.values(),\n ]\n const decoded = raw.map((value) => {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n })\n return [...new Set([...raw, ...decoded])].filter((value) => value.length >= SECRET_MIN_LENGTH)\n}\n\n/**\n * A connection URL as it may be printed: `scheme://host:port/path`, with no user, password, query or fragment. Text\n * that is not a URL comes back as `<unparsable url>`, since it may be a URL with a secret the parser could not read.\n */\nexport function displayUrl(url: string): string {\n try {\n const parsed = new URL(url)\n return `${parsed.protocol}//${parsed.host}${parsed.pathname === '/' ? '' : parsed.pathname}`\n } catch {\n return '<unparsable url>'\n }\n}\n\n/**\n * Runs `check` within the bound. A check that throws is `down` with the error's message, every `redact` value in it\n * replaced; one still running when the bound passes is `down` with `timed out after <n> ms`, and its signal aborted.\n */\nexport async function boundedHealthCheck(\n check: (signal: AbortSignal) => Promise<HealthReport> | HealthReport,\n options: BoundedHealthCheckOptions = {},\n): Promise<HealthReport> {\n const timeoutMs = options.timeoutMs ?? HEALTH_CHECK_TIMEOUT_MS\n const controller = new AbortController()\n let timer: ReturnType<typeof setTimeout> | undefined\n const timedOut = new Promise<HealthReport>((resolve) => {\n timer = setTimeout(() => {\n controller.abort()\n resolve({ status: 'down', detail: `timed out after ${timeoutMs} ms` })\n }, timeoutMs)\n })\n const failed = (error: unknown): HealthReport => {\n let detail = errorText(error)\n for (const secret of options.redact ?? []) {\n if (secret !== '') detail = detail.split(secret).join('***')\n }\n return { status: 'down', detail }\n }\n try {\n // Started inside a promise so a check that throws before returning one, or returns a plain report, is still\n // mapped rather than escaping the caller.\n const checked = Promise.resolve()\n .then(() => check(controller.signal))\n .catch(failed)\n return await Promise.race([checked, timedOut])\n } finally {\n clearTimeout(timer)\n }\n}\n\nexport interface AdapterBase {\n /** What this adapter offers, for the app's per-slot report; each adapter answers for itself. */\n capabilities(): readonly string[]\n readonly producesEvents: readonly string[]\n readonly consumesEvents: readonly string[]\n initialize(config: unknown): Promise<void>\n healthCheck(): Promise<HealthReport>\n}\n\n/** The commerce slots a store's config names a provider for. */\nexport type CommerceSlot =\n | 'payments'\n | 'email'\n | 'reviews'\n | 'search'\n | 'analytics'\n | 'shipping'\n | 'tax'\n | 'ai'\n\n/**\n * Every slot an adapter package can fill: the commerce slots, the infrastructure primitives and `security`, whose\n * choice is `security.adapter`.\n */\nexport type Slot = CommerceSlot | 'documentStore' | 'eventBus' | 'media' | 'security'\n\n/**\n * What the kernel hands an adapter package's `createAdapter`. A package that serves more than one slot switches on\n * `slot`; `config` is a commerce slot's `config` block from nymbal.config.ts, a primitive's connection block (or a\n * third-party primitive's `config`), or for `security` the block named after the adapter (`security.cloudflare`) or a\n * third-party adapter's `security.config`.\n */\nexport interface AdapterContext {\n slot: Slot\n config: Record<string, unknown>\n logger: Logger\n env: NodeJS.ProcessEnv\n}\n\n/**\n * One field of a slot's config block that a store owner may fill from the admin's Settings, as a provider package\n * declares it in its `settings` export. The config block wins, then `env`, then a saved value.\n */\nexport interface AdapterSetting {\n /** A dot path inside the slot's config block: `secretKey`, `smtp.pass`. */\n path: string\n label: string\n /** Stored encrypted and never shown again once saved. */\n secret: boolean\n /** The environment variable that sets the field when the config block does not. */\n env?: string\n /**\n * The adapter works without it (SMTP without authentication), so a secret left unset does not leave the slot\n * unconfigured.\n */\n optional?: boolean\n}\n\n/**\n * What a provider package exports: `createAdapter`, the slot and provider it serves (checked when it is loaded), and\n * the fields it lets Settings fill.\n */\nexport interface ProviderPackageModule {\n createAdapter(ctx: AdapterContext): unknown\n slot: Slot\n provider: string\n settings?: readonly AdapterSetting[]\n}\n\n/**\n * How `nymbal.config.ts` names an infrastructure slot's adapter (`documentStore`, `eventBus`, `media`): the provider,\n * the npm package it comes from when not `@nymbalhq/<slot>-<provider>`, and a third-party adapter's `ctx.config`.\n */\nexport interface InfrastructureBlock {\n provider: string\n package?: string\n config?: Record<string, unknown>\n}\n","import { ValidationError } from '../errors.js'\nimport type { QueryParams, QueryResult } from './document-store.js'\n\n// The one order and filter every document store is held to: the in-memory store runs it, and stores that cannot\n// order or filter the same way on their server fetch candidates and run it in the process.\n\n// Made on first use: a top-level `new` is a side effect bundlers keep, so every storefront bundle that imports\n// these types would carry it.\nlet collator: Intl.Collator | undefined\n\n/** Number, then string, then any other type, then null and missing: so values of different types never tie. */\nfunction rankOf(value: unknown): number {\n if (typeof value === 'number') return 0\n if (typeof value === 'string') return 1\n if (value === null || value === undefined) return 3\n return 2\n}\n\nfunction sign(n: number): number {\n return n > 0 ? 1 : n < 0 ? -1 : 0\n}\n\nfunction compareOther(a: unknown, b: unknown): number {\n const ta = typeof a\n const tb = typeof b\n if (ta !== tb) return ta < tb ? -1 : 1\n if (ta === 'boolean') return Number(a) - Number(b)\n const ja = JSON.stringify(a) ?? ''\n const jb = JSON.stringify(b) ?? ''\n return ja < jb ? -1 : ja > jb ? 1 : 0\n}\n\n/**\n * A total order over document values: numbers numerically, strings by `Intl.Collator` at base sensitivity (case and\n * accents tie), other types after strings, and null or missing last. Values of different types rank by type first.\n */\nexport function compareDocumentValues(a: unknown, b: unknown): number {\n const ra = rankOf(a)\n const rb = rankOf(b)\n if (ra !== rb) return ra - rb\n switch (ra) {\n case 0:\n return sign((a as number) - (b as number))\n case 1:\n collator ??= new Intl.Collator(undefined, { sensitivity: 'base' })\n return sign(collator.compare(a as string, b as string))\n case 2:\n return sign(compareOther(a, b))\n default:\n return 0\n }\n}\n\nexport type SortKeyCondition = Pick<NonNullable<QueryParams['sortKey']>, 'operator' | 'value'>\n\n/** `eq` is `===`; `gt`, `lt` and `between` (inclusive, `[low, high]`) are `compareDocumentValues`. */\nexport function matchesSortKey(value: unknown, condition: SortKeyCondition): boolean {\n const target = condition.value\n switch (condition.operator) {\n case 'eq':\n return value === target\n case 'gt':\n return compareDocumentValues(value, target) > 0\n case 'lt':\n return compareDocumentValues(value, target) < 0\n case 'between': {\n if (!Array.isArray(target) || target.length !== 2) return false\n const [low, high] = target as [unknown, unknown]\n return compareDocumentValues(value, low) >= 0 && compareDocumentValues(value, high) <= 0\n }\n default:\n return false\n }\n}\n\n/** The value at a dotted path (`priceRange.minMinor`); missing anywhere along it is `undefined`. */\nexport function documentValueAt(doc: unknown, path: string): unknown {\n let value: unknown = doc\n for (const part of path.split('.')) {\n if (value === null || typeof value !== 'object') return undefined\n value = (value as Record<string, unknown>)[part]\n }\n return value\n}\n\nexport const DEFAULT_QUERY_LIMIT = 50\n\ninterface Order {\n /** The field ordered by, or null for the document key alone. */\n field: string | null\n direction: 'asc' | 'desc'\n}\n\ninterface Position {\n key: string\n value: unknown\n}\n\nfunction orderOf(params: QueryParams): Order {\n if (params.orderBy !== undefined) {\n return { field: params.orderBy.field, direction: params.orderBy.direction }\n }\n const direction = (params.ascending ?? true) ? 'asc' : 'desc'\n return { field: params.sortKey?.field ?? null, direction }\n}\n\nconst isNullish = (value: unknown): boolean => value === null || value === undefined\n\n/** Nulls and missing values last in either direction; ties by key ascending, so no two documents tie. */\nfunction comparePositions(a: Position, b: Position, order: Order): number {\n if (order.field !== null) {\n const an = isNullish(a.value)\n const bn = isNullish(b.value)\n if (an !== bn) return an ? 1 : -1\n let c = an ? 0 : compareDocumentValues(a.value, b.value)\n if (order.direction === 'desc') c = -c\n if (c !== 0) return c\n return a.key < b.key ? -1 : a.key > b.key ? 1 : 0\n }\n const c = a.key < b.key ? -1 : a.key > b.key ? 1 : 0\n return order.direction === 'desc' ? -c : c\n}\n\n/** What a cursor is bound to: the order and the partition, so a cursor reused under another is refused. */\nfunction cursorScope(order: Order, params: QueryParams): string {\n return JSON.stringify([order.field, order.direction, params.partitionKey])\n}\n\nfunction decodeCursor(cursor: string, scope: string): Position {\n let decoded: unknown\n try {\n decoded = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'))\n } catch {\n decoded = null\n }\n const c = decoded as { o?: unknown; k?: unknown; v?: unknown } | null\n if (c === null || typeof c !== 'object' || typeof c.o !== 'string' || typeof c.k !== 'string') {\n throw new ValidationError('The cursor is not valid', { context: { cursor } })\n }\n if (c.o !== scope) {\n throw new ValidationError('The cursor was issued under another order', { context: { cursor } })\n }\n return { key: c.k, value: c.v ?? null }\n}\n\n/**\n * One page of `params` over the documents of a collection: filter by partition (`'*'` for all) and sort key, order,\n * then seek past the cursor by value, so a document deleted between pages neither restarts nor repeats the walk.\n */\nexport function queryDocuments<T>(\n documents: Iterable<readonly [string, unknown]>,\n params: QueryParams,\n): QueryResult<T> {\n const limit = params.limit ?? DEFAULT_QUERY_LIMIT\n const order = orderOf(params)\n const scope = cursorScope(order, params)\n const after = params.cursor ? decodeCursor(params.cursor, scope) : null\n const { partitionKey, sortKey } = params\n\n const matches: Array<{ position: Position; doc: unknown }> = []\n for (const [key, doc] of documents) {\n const record = (doc ?? {}) as Record<string, unknown>\n if (partitionKey.value !== '*' && record[partitionKey.field] !== partitionKey.value) continue\n if (sortKey !== undefined && !matchesSortKey(record[sortKey.field], sortKey)) continue\n const value = order.field === null ? null : (documentValueAt(record, order.field) ?? null)\n const position = { key, value }\n if (after !== null && comparePositions(position, after, order) <= 0) continue\n matches.push({ position, doc })\n }\n matches.sort((a, b) => comparePositions(a.position, b.position, order))\n\n const page = matches.slice(0, limit)\n const last = page[page.length - 1]\n const nextCursor =\n matches.length > limit && last !== undefined\n ? Buffer.from(\n JSON.stringify({ o: scope, k: last.position.key, v: last.position.value }),\n 'utf8',\n ).toString('base64url')\n : null\n return { items: page.map((m) => m.doc as T), nextCursor }\n}\n","import { AdapterError } from '../errors.js'\nimport type { NymbalEvent, EventHandler } from '../events/envelope.js'\nimport type { HealthReport } from './adapter-base.js'\n\nexport interface RetryPolicy {\n maxAttempts: number\n backoff: 'exponential' | 'linear' | 'fixed'\n initialDelayMs?: number\n}\n\nexport interface SubscriptionConfig {\n retryPolicy?: RetryPolicy\n deadLetterQueue?: string\n batchSize?: number\n /**\n * A named subscription is handled once per name across every bus instance (one consumer group); an unnamed one is\n * handled by every subscriber on every instance. On a single-process bus the two are the same.\n */\n name?: string\n /** The subscriber only maintains derived state (a projection, a search index): it also receives `replay`. */\n derived?: boolean\n}\n\nexport interface Subscription {\n id: string\n patterns: string[]\n unsubscribe(): Promise<void>\n}\n\n/** A named subscription whose handler threw on an event. */\nexport interface HandlerFailure {\n subscription: string\n message: string\n}\n\n/** What `publish` rejects with when a named subscription's handler threw: code `adapter.handler_failed`. */\nexport function handlerFailedError(\n event: NymbalEvent<unknown>,\n failed: HandlerFailure[],\n): AdapterError {\n return new AdapterError(\n 'handler_failed',\n `${event.type} (${event.id}) failed in ${failed.map((f) => f.subscription).join(', ')}`,\n { context: { eventId: event.id, eventType: event.type, failed } },\n )\n}\n\n/**\n * Delivery is at least once: a failed delivery is published again, to every matching subscription, so every handler\n * is idempotent (projections overwrite their documents, side effects use `withIdempotency`). The bus never retries a\n * failed handler by itself.\n */\nexport interface EventBusAdapter {\n capabilities(): readonly string[]\n /**\n * Resolves only after every matching subscription this instance holds has handled the event, so a read straight\n * after a command sees its projections. When a named subscription's handler throws, it rejects with\n * `handlerFailedError` naming each one that threw, once every matching handler has settled. An unnamed subscription's\n * throw is logged and does not reject.\n */\n publish<T>(event: NymbalEvent<T>): Promise<void>\n /** Hands every event out in order, then rejects as `publish` does for the first event a named handler failed. */\n publishBatch(events: NymbalEvent<unknown>[]): Promise<void>\n /**\n * Delivers to `derived` subscriptions only, so rebuilding derived state never triggers side effects. A handler's\n * throw is logged and does not reject.\n */\n replay(events: NymbalEvent<unknown>[]): Promise<void>\n subscribe(\n eventPattern: string | string[],\n handler: EventHandler,\n config?: SubscriptionConfig,\n ): Promise<Subscription>\n unsubscribe(subscription: Subscription): Promise<void>\n close(): Promise<void>\n /**\n * Whether the bus can deliver, within `HEALTH_CHECK_TIMEOUT_MS`. Optional, so a bus written before it existed still\n * type-checks; `nymbal doctor` reports one without it as `no healthCheck`.\n */\n healthCheck?(): Promise<HealthReport>\n}\n","export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n\n/** The request header a caller may name its own correlation id in, and the response header that echoes the one used. */\nexport const CORRELATION_ID_HEADER = 'x-correlation-id'\n\nconst CORRELATION_ID = /^[A-Za-z0-9._:-]{1,128}$/\n\n/**\n * Whether `s` may be used as a correlation id: it reaches logs and the event log, so an inbound value that fails is\n * replaced, never cleaned. The id is a label only; nothing reads it to decide access.\n */\nexport function isValidCorrelationId(s: string): boolean {\n return CORRELATION_ID.test(s)\n}\n\nexport interface RequestAuth {\n userId?: string\n roles?: string[]\n token?: string\n}\n\nexport interface Logger {\n trace(obj: unknown, msg?: string): void\n debug(obj: unknown, msg?: string): void\n info(obj: unknown, msg?: string): void\n warn(obj: unknown, msg?: string): void\n error(obj: unknown, msg?: string): void\n child(bindings: Record<string, unknown>): Logger\n}\n\nexport interface RequestContext<TBody = unknown, TQuery = Record<string, string | string[] | undefined>, TParams = Record<string, string>> {\n method: HttpMethod\n path: string\n url: string\n params: TParams\n query: TQuery\n body: TBody\n rawBody?: Buffer\n headers: Record<string, string | string[] | undefined>\n cookies: Record<string, string>\n auth?: RequestAuth\n requestId: string\n /** The inbound `x-correlation-id` when `isValidCorrelationId` accepts it, else the request id. */\n correlationId: string\n logger: Logger\n ip: string\n /** The agent a tool call is made for. Only the tool invoker sets it; no HTTP adapter does, so no header can claim it. */\n agent?: { name: string }\n}\n\nexport interface ResponseCookie {\n name: string\n value: string\n path?: string\n domain?: string\n maxAge?: number\n httpOnly?: boolean\n secure?: boolean\n sameSite?: 'strict' | 'lax' | 'none'\n}\n\nexport interface ResponseEnvelope<T = unknown> {\n status: number\n headers?: Record<string, string>\n body?: T\n cookies?: ResponseCookie[]\n}\n\nexport type RouteHandler<TBody = unknown, TQuery = Record<string, string | string[] | undefined>, TParams = Record<string, string>, TResponse = unknown> = (\n ctx: RequestContext<TBody, TQuery, TParams>,\n) => Promise<ResponseEnvelope<TResponse>> | ResponseEnvelope<TResponse>\n\nexport interface RouteOptions {\n name?: string\n csrf?: boolean\n rateLimit?: string\n rawBody?: boolean\n}\n\nexport type MiddlewareHandler = (\n ctx: RequestContext,\n) => Promise<ResponseEnvelope | void> | ResponseEnvelope | void\n\nexport interface SseEvent {\n id?: string\n event?: string\n data: unknown\n}\n\nexport interface StreamContext {\n send(event: SseEvent): void\n close(): void\n onClose(callback: () => void): void\n}\n\nexport type StreamHandler = (\n ctx: RequestContext,\n stream: StreamContext,\n) => Promise<void> | void\n\nexport interface HttpAdapter {\n registerRoute(\n method: HttpMethod,\n path: string,\n handler: RouteHandler,\n options?: RouteOptions,\n ): void\n registerStreamRoute(\n method: 'GET',\n path: string,\n handler: StreamHandler,\n options?: RouteOptions,\n ): void\n registerMiddleware(middleware: MiddlewareHandler): void\n setDefaultHeaders(headers: Record<string, string>): void\n start(port: number, host?: string): Promise<void>\n stop(): Promise<void>\n}\n\n/** A JSON Schema whose root is an object, as an MCP tool's `inputSchema` must be. */\nexport interface JsonSchemaObject {\n type: 'object'\n properties?: Record<string, unknown>\n required?: string[]\n [keyword: string]: unknown\n}\n\n/** Who a tool runs for: the verified bearer's user (none for an anonymous agent) and the agent's client info. */\nexport interface Caller {\n auth?: RequestAuth\n agent: { name: string; version?: string }\n /** The address the agent's request came from, for the route's rate rule. */\n ip?: string\n}\n\n/**\n * One route of the API as an agent tool. `scope` is the least caller the route's gate admits; `kind` is `read` for a\n * GET, `session` for a write that changes nothing audited (the cart), `command` for an audited command.\n */\nexport interface ToolDefinition {\n name: string\n title: string\n description: string\n inputSchema: JsonSchemaObject\n scope: 'public' | 'customer' | 'admin'\n kind: 'read' | 'session' | 'command'\n route: { method: HttpMethod; path: string; command: string | null | undefined }\n}\n\nexport interface ToolResult {\n content: Array<{ type: 'text'; text: string }>\n structuredContent?: Record<string, unknown>\n isError?: boolean\n}\n\n/** What a caller of the tool surface may ask of one call beside its arguments. */\nexport interface ToolCallOptions {\n /** The correlation id the call's request, its proposal and its audit entries carry; a fresh one when absent. */\n correlationId?: string\n /** Keep a command as a pending proposal whatever the policy says (`agents.autoApprove`, `agents.mcp.writes`). */\n proposeWrites?: boolean\n}\n\n/** The tools a store serves to agents; the transports (HTTP, stdio) bind it. */\nexport interface ToolSurface {\n list(caller: Caller): ToolDefinition[]\n call(name: string, args: unknown, caller: Caller, options?: ToolCallOptions): Promise<ToolResult>\n}\n","import type { AdapterBase } from './adapter-base.js'\nimport type { HttpAdapter } from './http.js'\n\nexport type RateLimitAction = 'block' | 'challenge' | 'throttle'\n\nexport interface RateLimitRule {\n requests: number\n window: string\n action: RateLimitAction\n}\n\nexport interface RateLimitDecision {\n allowed: boolean\n retryAfterSeconds?: number\n action: RateLimitAction\n}\n\nexport interface SecurityConfigLike {\n rateLimit: Record<string, RateLimitRule>\n csrf: boolean\n headers: {\n hsts: boolean\n contentSecurityPolicy: string\n referrerPolicy: string\n xFrameOptions: string\n }\n [key: string]: unknown\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: Array<{ code: string; message: string; path?: string }>\n}\n\nexport interface DriftReport {\n drift: boolean\n differences: Array<{ path: string; configured: unknown; actual: unknown }>\n}\n\nexport interface SecurityState {\n rulesApplied: string[]\n lastValidatedAt: string\n}\n\n/**\n * What a security adapter tells the HTTP layer about the network in front of the store. Each value counts only when the\n * store sets neither its variable nor its config key.\n */\nexport interface SecurityNetwork {\n /** The proxies whose `X-Forwarded-For` (and client-address header) the API believes: presets, addresses or CIDRs. */\n trustedProxies?: string[]\n /** One header a listed proxy sets to the client's address, such as `CF-Connecting-IP`. */\n clientIpHeader?: string\n /** Whether `clientIpHeader` may be honoured only on a request the origin lock admitted. */\n clientIpRequiresLock?: boolean\n}\n\n/** Where a resolved network value came from: the variable, the config key, the adapter's name, or `default`. */\nexport interface NetworkSource {\n kind: 'env' | 'config' | 'adapter' | 'default'\n name: string\n}\n\n/** The network block the HTTP server is built from, resolved once at boot. */\nexport interface ResolvedNetwork {\n trustedProxies: string[]\n trustedProxiesSource: NetworkSource\n clientIpHeader?: string\n clientIpHeaderSource?: NetworkSource\n clientIpRequiresLock: boolean\n /** Present when `NYMBAL_ORIGIN_SECRET` is set: the header the CDN proves itself in, and the secrets it may send. */\n originLock?: ResolvedOriginLock\n}\n\n/** The origin lock, resolved once at boot: every entry is accepted, so a secret can be rotated without downtime. */\nexport interface ResolvedOriginLock {\n header: string\n secrets: readonly string[]\n}\n\n/**\n * Whether the CDN sends the origin secret: `missing` when it has no enabled rule; `present` with the header the rule\n * sets and `entry`, the 0-based index of the secret it sends, or null when it sends none of them.\n */\nexport type OriginLockRule =\n | { state: 'missing' }\n | { state: 'present'; header: string; entry: number | null }\n\nexport interface SecurityAdapter extends AdapterBase {\n applyMiddleware(app: HttpAdapter, config: SecurityConfigLike): Promise<void>\n validate(config: SecurityConfigLike): Promise<ValidationResult>\n drift?(config: SecurityConfigLike): Promise<DriftReport>\n inspect?(): Promise<SecurityState>\n /** The adapter's network settings, offered at boot; synchronous and without I/O. */\n network?(): SecurityNetwork\n /** Whether the CDN in front sends one of the lock's secrets, read from the CDN itself. */\n originLockRule?(lock: ResolvedOriginLock): Promise<OriginLockRule>\n}\n\n/** The response headers every security adapter sets in process, from the config's `headers` block. */\nexport function buildSecurityHeaders(security: SecurityConfigLike): Record<string, string> {\n const headers: Record<string, string> = {}\n if (security.headers.hsts) {\n headers['strict-transport-security'] = 'max-age=31536000; includeSubDomains'\n }\n const csp = security.headers.contentSecurityPolicy\n const cspValue =\n csp === 'strict'\n ? \"default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'\"\n : csp === 'relaxed'\n ? \"default-src 'self' https:; img-src * data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'\"\n : csp === 'off'\n ? null\n : csp\n if (cspValue) headers['content-security-policy'] = cspValue\n headers['referrer-policy'] = security.headers.referrerPolicy\n headers['x-frame-options'] = security.headers.xFrameOptions === 'deny' ? 'DENY' : 'SAMEORIGIN'\n headers['x-content-type-options'] = 'nosniff'\n return headers\n}\n","import type { AdapterBase } from './adapter-base.js'\nimport type {\n PaymentIntent,\n PaymentIntentParams,\n PaymentCapture,\n Refund,\n PaymentStatus,\n WebhookResult,\n} from '../domain/payment.js'\nimport type { CustomerProfile } from '../domain/customer.js'\nimport type { Address } from '../domain/order.js'\nimport type { Review, ReviewModerationStatus, AggregateRating } from '../domain/review.js'\nimport type { JsonSchemaObject } from './http.js'\n\n/** A webhook request's headers, names lower-cased. */\nexport type WebhookHeaders = Record<string, string | string[] | undefined>\n\n/** One event a webhook delivery carries; the kernel builds its envelope (id, source, correlation). */\nexport interface WebhookEventDraft {\n type: string\n payload: Record<string, unknown>\n}\n\nexport interface WebhookDelivery {\n verified: boolean\n /** The provider's id for this delivery: what makes a retry of it a replay. */\n deliveryId: string\n events: WebhookEventDraft[]\n}\n\nexport interface CommerceAdapter extends AdapterBase {\n readonly providerName: string\n /**\n * Takes a delivery to `POST /api/webhooks/<providerName>`: verifies its signature over `rawBody` (from whichever\n * header the provider signs in) and returns its events. The same bytes must give the same `deliveryId` and the same\n * events, so a provider's retry is processed once. Each event's type is `<providerName>.<event>.v<n>` (lower-case\n * words, `_` allowed), is listed in `producesEvents`, and its payload is a plain JSON object; a delivery breaking\n * this is refused whole. A kernel namespace (`order`, `payment`, ...) is never an adapter's. Not called on a\n * payments adapter, whose deliveries go through `parseWebhook`.\n */\n receiveWebhook?(rawBody: Buffer, headers: WebhookHeaders): Promise<WebhookDelivery>\n}\n\n// --- Payments ---------------------------------------------------------------\n\nexport interface RefundOptions {\n /** The same key on a retry makes the provider refund at most once. */\n idempotencyKey?: string\n /** The order's currency: what a provider that no longer holds the payment answers in. */\n currency?: string\n}\n\n/**\n * The storefront half of a payments adapter: which client-side driver collects the payment and the public values it\n * needs. GET /api/capabilities serves `options` to every shopper, so it holds public values only.\n */\nexport interface PaymentClient {\n driver: string\n options: Record<string, string>\n}\n\nexport interface PaymentsAdapter extends CommerceAdapter {\n readonly kind: 'payments'\n /** How a storefront collects a payment; absent or `null` when there is no client step. */\n client?(): PaymentClient | null\n createPaymentIntent(params: PaymentIntentParams): Promise<PaymentIntent>\n capturePayment(intentId: string, amountMinor?: number): Promise<PaymentCapture>\n /** Throws `PaymentError('refund_rejected')` when no money moved, `PaymentError('refund_unknown')` when it may have. */\n refund(\n paymentId: string,\n amountMinor?: number,\n reason?: string,\n opts?: RefundOptions,\n ): Promise<Refund>\n getPaymentStatus(intentId: string): Promise<PaymentStatus>\n /** The intent as the provider holds it now, client secret included: what a shopper needs to pay it again. */\n getPaymentIntent(intentId: string): Promise<PaymentIntent>\n /** Cancels an intent that has not been paid; throws `PaymentError('cancel_refused')` when the provider will not\n * (the intent is processing or already captured). */\n cancelPaymentIntent(intentId: string): Promise<void>\n parseWebhook(rawBody: Buffer | string, signature: string): Promise<WebhookResult>\n}\n\n// --- Email -----------------------------------------------------------------\n\nexport interface TransactionalEmail {\n template: string\n to: string\n vars: Record<string, unknown>\n locale?: string\n}\n\nexport interface EmailSendResult {\n messageId: string | null\n accepted: boolean\n}\n\nexport interface EmailAdapter extends CommerceAdapter {\n readonly kind: 'email'\n sendTransactional(params: TransactionalEmail): Promise<EmailSendResult>\n syncCustomerToList(customer: CustomerProfile): Promise<void>\n removeCustomerFromList(customerId: string): Promise<void>\n}\n\n// --- Reviews ---------------------------------------------------------------\n\nexport interface PaginationParams {\n limit?: number\n cursor?: string\n}\n\nexport interface ReviewsResult {\n items: Review[]\n nextCursor: string | null\n}\n\n/** What a reviews adapter can do; `capabilities()` of a reviews adapter answers a subset. */\nexport type ReviewsCapability = 'submit' | 'moderate' | 'aggregate' | 'invite' | 'widget'\n\n/**\n * The kernel resolves `customerId`, `author` and `orderId` (the reviewer's paid order holding the product) before the\n * adapter sees a submission; an adapter never looks up customers or orders. An adapter without `submit` refuses it\n * with `ReviewRefusedError('submit_unsupported')`.\n */\nexport interface ReviewSubmission {\n productId: string\n customerId: string | null\n orderId: string | null\n author: { name: string; email: string } | null\n rating: number\n title: string\n body: string\n}\n\nexport interface ReviewResult {\n review: Review\n}\n\nexport interface ReviewsAdapter extends CommerceAdapter {\n readonly kind: 'reviews'\n getProductReviews(\n productId: string,\n params?: PaginationParams & { status?: ReviewModerationStatus },\n ): Promise<ReviewsResult>\n getAggregateRating(productId: string): Promise<AggregateRating>\n submitReview(review: ReviewSubmission): Promise<ReviewResult>\n requestReview(orderId: string): Promise<void>\n}\n\n// --- Search ----------------------------------------------------------------\n\nexport interface SearchDocument {\n id: string\n type: string\n fields: Record<string, unknown>\n}\n\nexport interface IndexResult {\n indexed: number\n}\n\nexport interface SearchParams {\n limit?: number\n cursor?: string\n filters?: Record<string, unknown>\n sort?: Array<{ field: string; direction: 'asc' | 'desc' }>\n}\n\nexport interface SearchResult {\n items: SearchDocument[]\n total: number\n nextCursor: string | null\n}\n\nexport interface FacetParams {\n /** Count only the documents this query matches. */\n query?: string\n limit?: number\n filters?: Record<string, unknown>\n}\n\nexport interface Facet {\n field: string\n label: string\n values: Array<{ value: string; label?: string; count?: number }>\n}\n\nexport interface SearchAdapter extends CommerceAdapter {\n readonly kind: 'search'\n index(documents: SearchDocument[]): Promise<IndexResult>\n remove(documentIds: string[]): Promise<void>\n /**\n * Replaces the whole index with `documents`, so nothing outside them survives. Optional: without it a rebuild can\n * only `index` every document, and cannot remove ids the index holds beyond them (it has no way to list them).\n */\n replaceAll?(documents: Iterable<SearchDocument> | AsyncIterable<SearchDocument>): Promise<void>\n search(query: string, params?: SearchParams): Promise<SearchResult>\n facet(field: string, params?: FacetParams): Promise<Facet>\n}\n\n// --- Analytics -------------------------------------------------------------\n\nexport interface AnalyticsAdapter extends CommerceAdapter {\n readonly kind: 'analytics'\n track(event: string, properties: Record<string, unknown>): Promise<void>\n identify(userId: string, traits: Record<string, unknown>): Promise<void>\n}\n\n// --- Shipping --------------------------------------------------------------\n\n/** What a shipping adapter does; each word is declared exactly when the method it names is present. */\nexport const SHIPPING_CAPABILITIES = ['rates', 'labels', 'tracking'] as const\n\n/** `rates` has `rate`, `labels` has `createLabel` (and needs `rates`), `tracking` has `startTracking` and `receiveWebhook`. */\nexport type ShippingCapability = (typeof SHIPPING_CAPABILITIES)[number]\n\n/** A parcel has all three dimensions or none. Grams and millimetres, each positive. */\nexport type Parcel =\n | { weightGrams: number; lengthMm?: never; widthMm?: never; heightMm?: never }\n | { weightGrams: number; lengthMm: number; widthMm: number; heightMm: number }\n\nexport interface ShippingRateParams {\n from: Address\n to: Address\n parcel: Parcel\n}\n\nexport interface ShippingRate {\n /** Opaque to the kernel: handed back unchanged as `ShippingLabelParams.rateId`. */\n id: string\n carrier: string\n service: string\n amountMinor: number\n currency: string\n estimatedDays: number | null\n}\n\nexport interface ShippingLabelParams {\n rateId: string\n}\n\nexport interface ShippingLabel {\n labelUrl: string\n trackingNumber: string\n carrier: string\n service: string\n amountMinor: number\n currency: string\n}\n\nexport interface StartTrackingParams {\n trackingNumber: string\n /** As the merchant typed it; null leaves the carrier to the provider. */\n carrier: string | null\n}\n\n/**\n * `tracking`: the provider holds the number, now or from before. `refused`: it never will (an invalid number, an\n * unknown carrier, a refused key). An outage rejects instead, so the caller may try again.\n */\nexport type StartTrackingResult = { status: 'tracking' } | { status: 'refused'; reason: string }\n\nexport interface ShippingAdapter extends CommerceAdapter {\n readonly kind: 'shipping'\n capabilities(): readonly ShippingCapability[]\n rate?(params: ShippingRateParams): Promise<ShippingRate[]>\n createLabel?(params: ShippingLabelParams): Promise<ShippingLabel>\n startTracking?(params: StartTrackingParams): Promise<StartTrackingResult>\n}\n\n// --- Tax -------------------------------------------------------------------\n\nexport interface TaxLine {\n amountMinor: number\n qty: number\n}\n\nexport interface TaxCalculationParams {\n currency: string\n shippingCountry: string\n shippingRegion: string\n lines: TaxLine[]\n}\n\nexport interface TaxCalculationResult {\n totalTaxMinor: number\n ratePerLine: Array<{ taxMinor: number }>\n}\n\nexport interface TaxAdapter extends CommerceAdapter {\n readonly kind: 'tax'\n calculate(params: TaxCalculationParams): Promise<TaxCalculationResult>\n}\n\n// --- AI --------------------------------------------------------------------\n\n/** A tool the model may call. Its name matches `^[A-Za-z0-9_-]{1,64}$` and is unique among the tools of one request. */\nexport interface ChatTool {\n name: string\n description: string\n inputSchema: JsonSchemaObject\n}\n\n/** A call the model asked for, its arguments a JSON object. */\nexport interface ChatToolCall {\n id: string\n name: string\n args: Record<string, unknown>\n}\n\n/**\n * One turn of a conversation. The first turn is the user's. An assistant turn's `content` may be empty only when it\n * has calls; each call is answered by one `tool` turn before the next user or assistant turn.\n */\nexport type ChatMessage =\n | { role: 'user'; content: string }\n | { role: 'assistant'; content: string; toolCalls?: readonly ChatToolCall[] }\n | { role: 'tool'; toolCallId: string; content: string; isError?: boolean }\n\nexport interface ChatOptions {\n /** Instructions for the whole conversation, sent in the provider's own system field. */\n system?: string\n /** The most tokens the answer may take; 1024 when omitted. */\n maxTokens?: number\n tools?: readonly ChatTool[]\n /** Aborting it closes the request and rejects `adapter.cancelled`. */\n signal?: AbortSignal\n}\n\n/** Why the answer ended: complete, cut at `maxTokens`, waiting on its tool calls, or anything else reported. */\nexport type ChatStopReason = 'end' | 'max_tokens' | 'tool_use' | 'other'\n\nexport interface ChatResult {\n /** '' when the answer is calls only. */\n text: string\n stopReason: ChatStopReason\n /** 0 where the provider reports none. */\n usage: { inputTokens: number; outputTokens: number }\n /** The model as the provider reports it. */\n model: string\n /** The calls the model asked for, in its order; [] when none. */\n toolCalls: ChatToolCall[]\n}\n\n/** What `chatStream` yields: text as it arrives, each call once its arguments are complete, and one `done` last. */\nexport type ChatStreamEvent =\n | { type: 'text'; delta: string }\n | { type: 'tool_call'; call: ChatToolCall }\n | { type: 'done'; result: ChatResult }\n\nexport interface AiAdapter extends CommerceAdapter {\n readonly kind: 'ai'\n embed(texts: string[]): Promise<number[][]>\n /** The text `chat` answers to a single user turn. */\n complete(prompt: string, opts?: { maxTokens?: number }): Promise<string>\n /**\n * One request to the provider. Refused input, a provider failure and a timeout reject with an `AdapterError` whose\n * context is `{ provider, status? }` and which never carries the key.\n */\n chat(messages: readonly ChatMessage[], opts?: ChatOptions): Promise<ChatResult>\n /**\n * `chat` as it is generated: the text deltas concatenate to the `done` result's text and the `tool_call` events are\n * its calls in order. A stream broken after it started rejects `adapter.unavailable`; the whole stream is bounded as\n * one `chat`.\n */\n chatStream?(messages: readonly ChatMessage[], opts?: ChatOptions): AsyncIterable<ChatStreamEvent>\n}\n\n/** The adapter interface each commerce slot takes: what a project injects for a slot whose provider is `custom`. */\nexport interface CommerceSlotAdapters {\n payments: PaymentsAdapter\n email: EmailAdapter\n reviews: ReviewsAdapter\n search: SearchAdapter\n analytics: AnalyticsAdapter\n shipping: ShippingAdapter\n tax: TaxAdapter\n ai: AiAdapter\n}\n","import { STANDARD_EVENT_TYPES } from '../events/constants.js'\nimport type { WebhookDelivery } from './commerce.js'\n\n/** Why a delivery's events may not be published; `type` is the offending event's, when one is to blame. */\nexport interface WebhookDeliveryProblem {\n type?: unknown\n reason: string\n}\n\nconst VERSIONED_EVENT = /^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*\\.v[1-9][0-9]*$/\n\n/** Plain JSON objects only: a class instance would not survive the event log's serialisation as itself. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false\n const proto = Object.getPrototypeOf(value) as unknown\n return proto === Object.prototype || proto === null\n}\n\n/**\n * The first thing that makes a verified delivery from the adapter named `providerName` unpublishable, or null when\n * every event may be published: the rule of `receiveWebhook`, which the kernel refuses a delivery for and the\n * webhook contract suite certifies.\n */\nexport function webhookDeliveryProblem(\n providerName: string,\n producesEvents: readonly string[],\n delivery: WebhookDelivery,\n): WebhookDeliveryProblem | null {\n if (typeof delivery.deliveryId !== 'string' || delivery.deliveryId === '') {\n return { reason: 'the delivery id is empty' }\n }\n if (!Array.isArray(delivery.events)) return { reason: 'events is not an array' }\n for (const event of delivery.events as unknown[]) {\n const { type, payload } = (isPlainObject(event) ? event : {}) as {\n type?: unknown\n payload?: unknown\n }\n if (typeof type !== 'string') return { type, reason: 'an event has no type' }\n const dot = type.indexOf('.')\n // A provider name holding a dot never equals the text before the first dot, so it can publish nothing.\n if (dot === -1 || type.slice(0, dot) !== providerName) {\n return { type, reason: `the type is not in the ${providerName} namespace` }\n }\n // Computed here, not at module level: a top-level Set would keep every event type in each storefront bundle.\n if (STANDARD_EVENT_TYPES.some((standard) => standard.split('.')[0] === providerName)) {\n return { type, reason: 'the type is in a kernel namespace' }\n }\n if (!VERSIONED_EVENT.test(type.slice(dot + 1))) {\n return { type, reason: 'the type is not <provider>.<event>.v<n>' }\n }\n if (!producesEvents.includes(type)) {\n return { type, reason: 'the type is not listed in producesEvents' }\n }\n if (!isPlainObject(payload)) return { type, reason: 'the payload is not a JSON object' }\n }\n return null\n}\n","import type { WebhookDelivery } from './commerce.js'\nimport type { WebhookDeliveryProblem } from './webhook-delivery.js'\n\n/**\n * `<providerName>.bounced.v1`: a message to `recipient` bounced. `hard` is the provider's permanent failure, `soft`\n * its transient one; a classification the adapter cannot map is `soft`, so a doubtful bounce never suppresses.\n */\nexport interface EmailBouncedV1Payload {\n recipient: string\n bounce: 'hard' | 'soft'\n /** The `EmailSendResult.messageId` the bounce answers, when the provider gives one. */\n messageId: string | null\n /** ISO 8601. */\n occurredAt: string\n}\n\n/** `<providerName>.complained.v1`: `recipient` marked a message as spam. */\nexport interface EmailComplainedV1Payload {\n recipient: string\n messageId: string | null\n /** ISO 8601. */\n occurredAt: string\n}\n\nexport type EmailFeedbackKind = 'bounced' | 'complained'\n\n/** The event type an email adapter publishes a bounce or a complaint under. */\nexport function emailFeedbackType(providerName: string, what: EmailFeedbackKind): string {\n return `${providerName}.${what}.v1`\n}\n\nfunction payloadOf(event: unknown): Record<string, unknown> {\n const payload = (event as { payload?: unknown } | null)?.payload\n return typeof payload === 'object' && payload !== null ? (payload as Record<string, unknown>) : {}\n}\n\n/**\n * The first bounce or complaint in a verified delivery from the email adapter `providerName` that the kernel cannot\n * act on, or null when there is none. The route refuses such a delivery whole, before claiming it, so the provider's\n * retry after the adapter is fixed is still received; the email feedback contract suite certifies the same rule.\n */\nexport function emailFeedbackProblem(\n providerName: string,\n delivery: WebhookDelivery,\n): WebhookDeliveryProblem | null {\n const bounced = emailFeedbackType(providerName, 'bounced')\n const complained = emailFeedbackType(providerName, 'complained')\n if (!Array.isArray(delivery.events)) return null\n for (const event of delivery.events as unknown[]) {\n const type = (event as { type?: unknown } | null)?.type\n if (type !== bounced && type !== complained) continue\n const payload = payloadOf(event)\n const { recipient, messageId, occurredAt } = payload\n if (typeof recipient !== 'string' || recipient.trim() === '') {\n return { type, reason: 'the recipient is missing or empty' }\n }\n if (type === bounced && payload['bounce'] !== 'hard' && payload['bounce'] !== 'soft') {\n return { type, reason: \"the bounce is neither 'hard' nor 'soft'\" }\n }\n if (messageId !== null && typeof messageId !== 'string') {\n return { type, reason: 'the messageId is neither a string nor null' }\n }\n if (typeof occurredAt !== 'string' || Number.isNaN(Date.parse(occurredAt))) {\n return { type, reason: 'occurredAt is not a readable date' }\n }\n }\n return null\n}\n","import type { WebhookDelivery } from './commerce.js'\nimport type { WebhookDeliveryProblem } from './webhook-delivery.js'\n\n/** The statuses a shipping adapter maps its carrier's onto; one it cannot map is `unknown`. */\nexport const SHIPPING_TRACKING_STATUSES = [\n 'pre_transit',\n 'in_transit',\n 'out_for_delivery',\n 'delivered',\n 'exception',\n 'returned',\n 'unknown',\n] as const\n\nexport type ShippingTrackingStatus = (typeof SHIPPING_TRACKING_STATUSES)[number]\n\n/** `<providerName>.tracking.v1`: the carrier reported `status` for the parcel `trackingNumber`. */\nexport interface ShippingTrackingV1Payload {\n trackingNumber: string\n /** The carrier's code as the adapter's provider gives it, which need not match the merchant's own. */\n carrier: string | null\n status: ShippingTrackingStatus\n /** ISO 8601. */\n occurredAt: string\n}\n\n/** The event type a shipping adapter publishes a tracking update under. */\nexport function shippingTrackingType(providerName: string): string {\n return `${providerName}.tracking.v1`\n}\n\n/** A tracking number as stored and looked up: no whitespace, upper case, so a carrier's spelling finds the merchant's. */\nexport function normaliseTrackingNumber(value: string): string {\n return value.replace(/\\s+/g, '').toUpperCase()\n}\n\nfunction payloadOf(event: unknown): Record<string, unknown> {\n const payload = (event as { payload?: unknown } | null)?.payload\n return typeof payload === 'object' && payload !== null ? (payload as Record<string, unknown>) : {}\n}\n\n/**\n * The first tracking update in a verified delivery from the shipping adapter `providerName` that the kernel cannot act\n * on, or null when there is none. The route refuses such a delivery whole, before claiming it, so the provider's retry\n * after the adapter is fixed is still received; the shipping tracking contract suite certifies the same rule.\n */\nexport function shippingTrackingProblem(\n providerName: string,\n delivery: WebhookDelivery,\n): WebhookDeliveryProblem | null {\n const tracking = shippingTrackingType(providerName)\n if (!Array.isArray(delivery.events)) return null\n for (const event of delivery.events as unknown[]) {\n const type = (event as { type?: unknown } | null)?.type\n if (type !== tracking) continue\n const { trackingNumber, carrier, status, occurredAt } = payloadOf(event)\n if (typeof trackingNumber !== 'string' || normaliseTrackingNumber(trackingNumber) === '') {\n return { type, reason: 'the trackingNumber is missing or empty' }\n }\n if (carrier !== null && typeof carrier !== 'string') {\n return { type, reason: 'the carrier is neither a string nor null' }\n }\n if (!(SHIPPING_TRACKING_STATUSES as readonly unknown[]).includes(status)) {\n return { type, reason: 'the status is not one of the tracking statuses' }\n }\n if (typeof occurredAt !== 'string' || Number.isNaN(Date.parse(occurredAt))) {\n return { type, reason: 'occurredAt is not a readable date' }\n }\n }\n return null\n}\n","// The kernel's transactional email templates, shared by every email adapter (the console one and the SMTP package).\nexport interface RenderedEmail {\n subject: string\n text: string\n html: string\n}\n\nfunction render(template: string, vars: Record<string, unknown>): string {\n return template.replace(/\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g, (_, path: string) => {\n const parts = path.split('.')\n let cur: unknown = vars\n for (const p of parts) {\n if (cur && typeof cur === 'object' && p in (cur as Record<string, unknown>)) {\n cur = (cur as Record<string, unknown>)[p]\n } else {\n return ''\n }\n }\n return cur == null ? '' : String(cur)\n })\n}\n\nconst TEMPLATES: Record<string, { subject: string; body: string }> = {\n 'order-placed': {\n subject: 'Order {{orderNumber}} confirmed',\n // `orderLink` is the whole \"View your order\" paragraph, absent when the store has no URL to link from.\n body: `Hi {{customerName}},\\n\\nThank you for your order {{orderNumber}}. Total: {{totalFormatted}}.\\n\\nWe'll let you know when it ships.{{orderLink}}`,\n },\n 'order-shipped': {\n subject: 'Order {{orderNumber}} is on its way',\n // `trackingLink` and `orderLink` are whole paragraphs, absent for an unknown carrier or a store with no URL.\n body: `Hi {{customerName}},\\n\\nYour order {{orderNumber}} has shipped via {{carrier}}.\\nTracking: {{trackingNumber}}{{trackingLink}}\\n\\nIn this parcel:\\n{{itemsList}}{{orderLink}}`,\n },\n welcome: {\n subject: 'Welcome to {{storeName}}',\n body: `Welcome {{customerName}}! Your account is ready.`,\n },\n 'cart-abandoned': {\n subject: \"Don't forget your cart at {{storeName}}\",\n body: `Hi,\\n\\nYou left items in your cart. Pick up where you left off: {{resumeUrl}}`,\n },\n // Each link on a line of its own, so a mail client never runs the next word into it.\n 'alert-confirm': {\n subject: 'Confirm your alert for {{productName}}',\n body: `Hi,\\n\\nYou asked {{storeName}} to email you when {{productName}} is back in stock. Confirm by following this link:\\n\\n{{confirmUrl}}\\n\\nIf you did not ask, ignore this email and you will hear nothing more.\\n\\nStop this alert:\\n{{unsubscribeUrl}}`,\n },\n 'alert-back-in-stock': {\n subject: '{{productName}} is back in stock',\n body: `Hi,\\n\\n{{productName}} is back in stock at {{storeName}}:\\n\\n{{productUrl}}\\n\\nThis was a one-time alert. Stop it:\\n{{unsubscribeUrl}}`,\n },\n 'alert-confirm-price-drop': {\n subject: 'Confirm your price alert for {{productName}}',\n body: `Hi,\\n\\nYou asked {{storeName}} to email you when the price of {{productName}} drops, each time it falls below the last price we told you. Confirm by following this link:\\n\\n{{confirmUrl}}\\n\\nIf you did not ask, ignore this email and you will hear nothing more.\\n\\nStop this alert:\\n{{unsubscribeUrl}}`,\n },\n 'alert-price-drop': {\n subject: 'Price drop: {{productName}} is now {{priceFormatted}}',\n body: `Hi,\\n\\nThe price of {{productName}} at {{storeName}} has dropped from {{previousPriceFormatted}} to {{priceFormatted}}:\\n\\n{{productUrl}}\\n\\nWe'll email you again if it drops below {{priceFormatted}}. Stop this alert:\\n{{unsubscribeUrl}}`,\n },\n}\n\nexport function renderEmailTemplate(\n template: string,\n vars: Record<string, unknown>,\n): RenderedEmail {\n const t = TEMPLATES[template]\n if (!t) {\n return {\n subject: `[nymbal] ${template}`,\n text: JSON.stringify(vars),\n html: `<pre>${JSON.stringify(vars, null, 2)}</pre>`,\n }\n }\n const subject = render(t.subject, vars)\n const text = render(t.body, vars)\n const html = `<pre>${text.replace(/</g, '&lt;')}</pre>`\n return { subject, text, html }\n}\n","export type StockLevel = 'out' | 'low' | 'in'\n\n/** Below this many units a variant is low on stock. */\nconst LOW_STOCK_BELOW = 5\n\n/** The one stock rule every storefront renders from. */\nexport function stockLevel(stock: number): StockLevel {\n if (stock <= 0) return 'out'\n return stock < LOW_STOCK_BELOW ? 'low' : 'in'\n}\n\nexport const STOCK_LABELS: Record<StockLevel, string> = {\n out: 'Out of Stock',\n low: 'Low Stock',\n in: 'In Stock',\n}\n","/** Who a command ran for. A guest is `guest:anonymous`: a cart's token is a bearer credential and is never an id. */\nexport interface AuditActor {\n kind: 'admin' | 'customer' | 'guest' | 'system' | 'import'\n id: string\n}\n\n/** `applied`: the command ran and committed. `refused`: its service refused it. `replayed`: a keyed retry answered. */\nexport type AuditOutcome = 'applied' | 'refused' | 'replayed'\n\n/** A record a command touched, by id only. */\nexport interface AuditSubject {\n type: string\n id: string\n}\n\n/**\n * One audit entry as `GET /api/admin/activity` lists it and the exports print it. A stored response is shown by its\n * status only; its body never leaves the store.\n */\nexport interface AuditEntry {\n id: string\n at: string\n actor: AuditActor\n command: string\n subjects: AuditSubject[]\n correlationId: string | null\n idempotencyKey: string | null\n outcome: AuditOutcome\n responseStatus: number | null\n}\n\n/** The request header an admin write carries to be applied once, and the response header a replay carries. */\nexport const IDEMPOTENCY_KEY_HEADER = 'idempotency-key'\nexport const IDEMPOTENT_REPLAYED_HEADER = 'idempotent-replayed'\n\n/** 1-128 characters of `[A-Za-z0-9._:-]`. */\nexport function isValidIdempotencyKey(value: string): boolean {\n return /^[A-Za-z0-9._:-]{1,128}$/.test(value)\n}\n","/** Where an agent's write stands: waiting, decided, then run. */\nexport type ProposalStatus = 'pending' | 'approved' | 'rejected' | 'applied' | 'failed'\n\n/** Who decided a proposal: an admin, or a policy the store's config sets. */\nexport type ProposalDecider =\n | { kind: 'admin'; id: string }\n | { kind: 'policy'; rule: 'agents.mcp.writes' | 'agents.autoApprove' }\n\n/** One proposal as the admin reads it. A handle's value (a cart id) is never served: only its argument's name. */\nexport interface ProposalView {\n id: string\n tool: string\n title: string\n command: string\n /** The arguments the agent sent, less its idempotency key and its handles. */\n args: Record<string, unknown>\n handles: string[]\n idempotencyKey: string | null\n status: ProposalStatus\n proposer: { userId: string | null; agent: { name: string; version?: string } }\n correlationId: string\n createdAt: string\n decision?: { by: ProposalDecider; at: string; reason?: string }\n /** What the route answered when the proposal ran. */\n outcome?: { status: number; code?: string; message?: string }\n /** What a pending write would change, read from the store when the proposal is read on its own. */\n preview?: ProposalPreview\n}\n\n/** The commands whose pending proposal, read on its own, carries a preview. */\nexport const PREVIEWED_COMMANDS: readonly string[] = [\n 'product.update',\n 'inventory.adjust',\n 'order.refund',\n 'order.cancel',\n]\n\n/** One value a pending write changes, as it is now beside what the write would make it. */\nexport interface ProposalChange {\n /** What changes, as the admin reads it: \"Name\", \"Price of Large\", \"Stock\", \"Refunded\", \"Status\", \"Media\". */\n label: string\n /** `money`: minor units of `ProposalPreview.currency`; `collection`: item or key counts, with `changed`. */\n kind: 'value' | 'money' | 'collection'\n current: string | number | null\n proposed: string | number | null\n changed?: boolean\n}\n\n/** What a pending write acts on and what it changes there. */\nexport interface ProposalPreview {\n /** What the write acts on, read from the store now; null when the id names nothing. */\n target: { kind: 'product' | 'variant' | 'order'; id: string; label: string } | null\n changes: ProposalChange[]\n currency: string\n}\n\nexport interface ProposalPage {\n items: ProposalView[]\n nextCursor: string | null\n /** Every proposal still waiting for a decision, whatever the page holds. */\n pending: number\n}\n"],"mappings":"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","names":["c","payloadOf"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/domain/money.ts","../src/domain/product.ts","../src/domain/cart.ts","../src/domain/order.ts","../src/domain/order-timeline.ts","../src/events/constants.ts","../src/adapters/adapter-base.ts","../src/adapters/document-order.ts","../src/adapters/event-bus.ts","../src/adapters/http.ts","../src/adapters/security.ts","../src/adapters/commerce.ts","../src/adapters/webhook-delivery.ts","../src/adapters/email-feedback.ts","../src/adapters/shipping-tracking.ts","../src/adapters/email-templates.ts","../src/stock-level.ts","../src/audit.ts","../src/proposals.ts"],"sourcesContent":["export type ErrorContext = Record<string, unknown>\n\nexport class NymbalError extends Error {\n readonly code: string\n readonly context: ErrorContext\n\n constructor(\n code: string,\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined)\n this.name = this.constructor.name\n this.code = code\n this.context = options.context ?? {}\n }\n\n toJSON(): { name: string; code: string; message: string; context: ErrorContext } {\n return { name: this.name, code: this.code, message: this.message, context: this.context }\n }\n}\n\nexport class ConfigError extends NymbalError {\n constructor(\n message: string,\n options: {\n cause?: unknown\n context?: ErrorContext\n code?: `config.${string}` | `adapter.${string}`\n } = {},\n ) {\n super(options.code ?? 'config.invalid', message, options)\n }\n}\n\nexport class ValidationError extends NymbalError {\n constructor(\n message: string,\n options: { cause?: unknown; context?: ErrorContext; code?: string } = {},\n ) {\n super(options.code ?? 'validation.failed', message, options)\n }\n}\n\nexport class NotFoundError extends NymbalError {\n constructor(\n resource: string,\n identifier: unknown,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super('not_found', `${resource} not found: ${String(identifier)}`, {\n ...options,\n context: { resource, identifier, ...(options.context ?? {}) },\n })\n }\n}\n\nexport class AdapterError extends NymbalError {\n constructor(\n code: string,\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`adapter.${code}`, message, options)\n }\n}\n\nexport class HttpError extends NymbalError {\n readonly status: number\n\n constructor(\n status: number,\n message: string,\n options: { cause?: unknown; context?: ErrorContext; code?: string } = {},\n ) {\n super(options.code ?? `http.${status}`, message, options)\n this.status = status\n }\n}\n\nexport class AuthError extends HttpError {\n constructor(\n code: 'unauthorized' | 'forbidden' | 'invalid_credentials' | 'token_expired' | 'token_invalid',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n const status = code === 'forbidden' ? 403 : 401\n super(status, message, { ...options, code: `auth.${code}` })\n }\n}\n\nexport class PaymentError extends NymbalError {\n constructor(\n code:\n | 'intent_failed'\n | 'capture_failed'\n // A definite refusal: no money moved.\n | 'refund_rejected'\n // An outcome the shop cannot know (a timeout, a provider fault): money may have moved.\n | 'refund_unknown'\n | 'cancel_refused'\n | 'webhook_invalid'\n | 'not_configured',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`payment.${code}`, message, options)\n }\n}\n\n/** A command the order's money does not allow (confirm unpaid, cancel paid, refund beyond the capture). */\nexport class OrderConflictError extends NymbalError {\n constructor(\n code:\n | 'order.unpaid'\n | 'order.paid'\n | 'order.stock_unavailable'\n | 'order.payment_in_progress'\n | 'refund.not_captured'\n | 'refund.exceeds_captured'\n | 'refund.request_reused',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(code, message, options)\n }\n}\n\nexport class CheckoutError extends NymbalError {\n constructor(\n code:\n | 'cart_empty'\n | 'in_progress'\n | 'payment_in_progress'\n | 'variant_missing'\n | 'out_of_stock'\n | 'reservation_failed'\n | 'finalize_failed',\n message: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`checkout.${code}`, message, options)\n }\n}\n\n/** A cart command that would take a line above `max`; `onCart` is what the line held before it. */\nexport class CartLimitError extends NymbalError {\n constructor(variantId: string, max: number, onCart: number) {\n super('cart.qty_limit', `a cart line holds at most ${max}`, {\n context: { variantId, max, onCart },\n })\n }\n}\n\n/** Why a review was not taken. `not_a_buyer` is reserved: any signed-in customer may review. */\nexport type ReviewRefusal = 'submit_unsupported' | 'already_reviewed' | 'not_a_buyer' | 'rejected'\n\nconst REVIEW_REFUSAL_MESSAGES: Record<ReviewRefusal, string> = {\n submit_unsupported: 'This store does not take reviews on its site.',\n already_reviewed: 'You have already reviewed this product.',\n not_a_buyer: 'Only a customer who bought this product can review it.',\n rejected: 'The review was not accepted.',\n}\n\nexport class ReviewRefusedError extends NymbalError {\n readonly refusal: ReviewRefusal\n\n constructor(\n refusal: ReviewRefusal,\n message: string = REVIEW_REFUSAL_MESSAGES[refusal],\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(`reviews.${refusal}`, message, options)\n this.refusal = refusal\n }\n}\n\nexport class StatusTransitionError extends NymbalError {\n constructor(\n entity: string,\n fromStatus: string,\n toStatus: string,\n options: { cause?: unknown; context?: ErrorContext } = {},\n ) {\n super(\n 'status.invalid_transition',\n `Invalid ${entity} status transition: ${fromStatus} → ${toStatus}`,\n {\n ...options,\n context: { entity, fromStatus, toStatus, ...(options.context ?? {}) },\n },\n )\n }\n}\n","import { ValidationError } from '../errors.js'\n\nexport interface Money {\n amount: number\n currency: string\n}\n\nexport function money(amount: number, currency: string): Money {\n assertMinorUnits(amount)\n return { amount, currency }\n}\n\nexport function assertMinorUnits(amount: number): void {\n if (!Number.isInteger(amount)) {\n throw new ValidationError(`Monetary amount must be an integer (minor units), got ${amount}`)\n }\n}\n\nfunction assertSameCurrency(a: Money, b: Money): void {\n if (a.currency !== b.currency) {\n throw new ValidationError(\n `Currency mismatch: ${a.currency} vs ${b.currency}`,\n )\n }\n}\n\nexport function add(a: Money, b: Money): Money {\n assertSameCurrency(a, b)\n return { amount: a.amount + b.amount, currency: a.currency }\n}\n\nexport function sub(a: Money, b: Money): Money {\n assertSameCurrency(a, b)\n return { amount: a.amount - b.amount, currency: a.currency }\n}\n\nexport function mulBy(m: Money, scalar: number): Money {\n if (!Number.isFinite(scalar)) {\n throw new ValidationError(`Scalar must be finite, got ${scalar}`)\n }\n return { amount: Math.round(m.amount * scalar), currency: m.currency }\n}\n\nexport function sumMoney(values: Money[], currency: string): Money {\n const total = values.reduce((acc, m) => {\n assertSameCurrency(m, { amount: 0, currency })\n return acc + m.amount\n }, 0)\n return { amount: total, currency }\n}\n\nexport function allocate(m: Money, weights: number[]): Money[] {\n if (weights.length === 0) return []\n const totalWeight = weights.reduce((s, w) => s + w, 0)\n if (totalWeight <= 0) {\n throw new ValidationError('allocate: sum of weights must be > 0')\n }\n const portions = weights.map((w) => Math.floor((m.amount * w) / totalWeight))\n let remainder = m.amount - portions.reduce((s, p) => s + p, 0)\n const result = portions.slice()\n let idx = 0\n while (remainder !== 0 && idx < result.length) {\n const delta = remainder > 0 ? 1 : -1\n result[idx] = (result[idx] ?? 0) + delta\n remainder -= delta\n idx += 1\n }\n return result.map((amount) => ({ amount, currency: m.currency }))\n}\n","export type ProductStatus = 'draft' | 'active' | 'archived'\nexport type ProductType = 'simple' | 'variable'\nexport type VariantStatus = 'active' | 'inactive'\n\nexport interface ProductMedia {\n url: string\n altText: string\n position: number\n}\n\nexport interface VariantOption {\n name: string\n value: string\n}\n\nexport interface VariantDimensions {\n lengthMm?: number\n widthMm?: number\n heightMm?: number\n}\n\nexport interface Variant {\n id: string\n productId: string\n sku: string\n name: string\n priceMinor: number\n compareAtPriceMinor: number | null\n weightGrams: number | null\n dimensions: VariantDimensions | null\n stock: number\n lowStockThreshold: number\n options: VariantOption[]\n status: VariantStatus\n createdAt: string\n updatedAt: string\n}\n\nexport interface Product {\n id: string\n slug: string\n name: string\n description: string\n shortDescription: string\n status: ProductStatus\n type: ProductType\n seoTitle: string\n seoDescription: string\n media: ProductMedia[]\n metadata: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\n/** The orders a product listing can be requested in; ties break by slug, ascending. */\nexport const PRODUCT_SORT_KEYS = [\n 'newest',\n 'price-asc',\n 'price-desc',\n 'name-asc',\n 'name-desc',\n] as const\nexport type ProductSortKey = (typeof PRODUCT_SORT_KEYS)[number]\nexport const DEFAULT_PRODUCT_SORT: ProductSortKey = 'newest'\n/** What a storefront shows for each sort. */\nexport const PRODUCT_SORT_LABELS: Readonly<Record<ProductSortKey, string>> = {\n newest: 'Newest',\n 'price-asc': 'Price: Low to High',\n 'price-desc': 'Price: High to Low',\n 'name-asc': 'Name: A to Z',\n 'name-desc': 'Name: Z to A',\n}\n","import type { OrderSource } from './order.js'\n\n/** The most of one variant a cart line holds. */\nexport const CART_LINE_MAX_QTY = 99\n\nexport interface CartItem {\n variantId: string\n productId: string\n /** The product page's slug; lines stored before lines carried one have none. */\n productSlug?: string\n productName: string\n variantName: string\n priceMinor: number\n qty: number\n imageUrl: string\n}\n\n/** Who opened a cart: the storefront or a shopping agent. An import opens none. */\nexport type CartSource = Exclude<OrderSource, 'import'>\n\nexport interface Cart {\n token: string\n customerId: string | null\n items: CartItem[]\n subtotalMinor: number\n currency: string\n source: CartSource\n expiresAt: string\n createdAt: string\n updatedAt: string\n}\n","import { StatusTransitionError } from '../errors.js'\n\nexport type OrderStatus =\n | 'pending'\n | 'confirmed'\n | 'processing'\n | 'shipped'\n | 'delivered'\n | 'cancelled'\n | 'refunded'\n | 'partially_refunded'\n\n// A captured order is refunded from any status between confirmed and delivered; a partial refund leaves room for more.\nexport const ORDER_STATUS_TRANSITIONS: Record<OrderStatus, readonly OrderStatus[]> = {\n pending: ['confirmed', 'cancelled'],\n confirmed: ['processing', 'cancelled', 'refunded', 'partially_refunded'],\n processing: ['shipped', 'cancelled', 'refunded', 'partially_refunded'],\n shipped: ['delivered', 'refunded', 'partially_refunded'],\n delivered: ['refunded', 'partially_refunded'],\n cancelled: [],\n refunded: [],\n partially_refunded: ['refunded'],\n}\n\nexport function canTransitionOrder(from: OrderStatus, to: OrderStatus): boolean {\n return ORDER_STATUS_TRANSITIONS[from].includes(to)\n}\n\nexport function assertOrderTransition(from: OrderStatus, to: OrderStatus): void {\n if (!canTransitionOrder(from, to)) {\n throw new StatusTransitionError('order', from, to)\n }\n}\n\nexport interface Address {\n firstName: string\n lastName: string\n company?: string\n addressLine1: string\n addressLine2?: string\n city: string\n region: string\n postalCode: string\n country: string\n phone?: string\n}\n\nexport interface OrderLineItem {\n variantId: string\n productId: string\n productName: string\n variantName: string\n sku: string\n qty: number\n unitPriceMinor: number\n lineSubtotalMinor: number\n lineTaxMinor: number\n lineTotalMinor: number\n imageUrl: string\n}\n\n/** Where an order came from: the storefront, an import, or the shopping agent that opened its cart. */\nexport type OrderSource = 'storefront' | 'import' | `agent:${string}`\n\n/** The source a cart or order opened by the named agent carries. */\nexport function agentSource(name: string): `agent:${string}` {\n return `agent:${name}`\n}\n\nexport interface Order {\n id: string\n orderNumber: string\n sequence: number\n customerId: string | null\n status: OrderStatus\n email: string\n billingAddress: Address\n shippingAddress: Address\n lineItems: OrderLineItem[]\n subtotalMinor: number\n taxTotalMinor: number\n shippingTotalMinor: number\n discountTotalMinor: number\n totalMinor: number\n currency: string\n notes: string\n metadata: Record<string, unknown>\n paymentIntentId: string | null\n /** Money taken for the order, in minor units of its currency; 0 while nothing is captured. */\n capturedMinor: number\n capturedAt: string | null\n /** Σ of the order's succeeded refunds (or, for an import or a pre-refund-table order, what was refunded then). */\n refundedMinor: number\n captureMethod: CaptureMethod | null\n source: OrderSource\n createdAt: string\n updatedAt: string\n}\n\n/**\n * The request header a shopper's order key travels in to `GET /api/orders/:orderNumber`. Never a query parameter, so\n * the key stays out of access logs that record request URLs.\n */\nexport const ORDER_KEY_HEADER = 'x-nymbal-order-key'\n\n/** The request header that carries the order's email beside an expired order key. Never a query parameter either. */\nexport const ORDER_EMAIL_HEADER = 'x-nymbal-order-email'\n\n/**\n * The API's attestation of a shopper's address on a page request it forwards to the storefront. The storefront server\n * echoes it on the API calls it makes for that shopper, so they count against the shopper's address, not its own.\n */\nexport const SHOPPER_ATTESTATION_HEADER = 'x-nymbal-shopper'\n\n/** Who took the money: the payments provider, the merchant recording an offline payment, or an imported history. */\nexport type CaptureMethod = 'provider' | 'manual' | 'import'\n\nexport type OrderRefundStatus = 'pending' | 'succeeded' | 'failed'\n\n/** One refund of an order. `pending` until the provider's answer settles it, and counted against what is refundable. */\nexport interface OrderRefund {\n id: string\n orderId: string\n amountMinor: number\n currency: string\n providerRefundId: string | null\n status: OrderRefundStatus\n reason: string | null\n actor: string\n createdAt: string\n}\n\nexport type PaymentState = 'unpaid' | 'paid' | 'partially_refunded' | 'refunded'\n\n/** What the admin shows about an order's money: derived from the stored amounts and refunds alone. */\nexport interface OrderPayment {\n state: PaymentState\n capturedMinor: number\n refundedMinor: number\n refundableMinor: number\n currency: string\n refunds: OrderRefund[]\n}\n\nexport function paymentState(order: Pick<Order, 'capturedMinor' | 'refundedMinor'>): PaymentState {\n if (order.capturedMinor <= 0) return 'unpaid'\n if (order.refundedMinor <= 0) return 'paid'\n return order.refundedMinor < order.capturedMinor ? 'partially_refunded' : 'refunded'\n}\n\n/** What can still be refunded: the capture less what was refunded and what is still pending with the provider. */\nexport function refundableMinor(\n order: Pick<Order, 'capturedMinor' | 'refundedMinor'>,\n refunds: ReadonlyArray<Pick<OrderRefund, 'amountMinor' | 'status'>>,\n): number {\n const pending = refunds\n .filter((r) => r.status === 'pending')\n .reduce((sum, r) => sum + r.amountMinor, 0)\n return Math.max(0, order.capturedMinor - order.refundedMinor - pending)\n}\n\nexport interface OrderHistoryEntry {\n id: string\n orderId: string\n fromStatus: OrderStatus | null\n toStatus: OrderStatus\n actor: string\n note: string\n timestamp: string\n}\n","import type { Address, Order } from './order.js'\n\n/** The lifecycle events an order's timeline shows, each one from an `order_history` row. */\nexport const ORDER_TIMELINE_KINDS = [\n 'placed',\n 'paid',\n 'shipped',\n 'delivered',\n 'cancelled',\n 'partially_refunded',\n 'refunded',\n] as const\n\nexport type OrderTimelineKind = (typeof ORDER_TIMELINE_KINDS)[number]\n\nexport interface OrderTimelineEntry {\n kind: OrderTimelineKind\n /** ISO 8601, UTC. */\n at: string\n}\n\n/** A shipping address as an order's key holder reads it: no phone, no company. */\nexport type ShopperAddress = Omit<Address, 'phone' | 'company'>\n\n/** What only the order's signed-in owner reads, or the store's own records, beside what its key holder reads. */\ntype OwnerOrStoreField =\n | 'id'\n | 'email'\n | 'billingAddress'\n | 'customerId'\n | 'sequence'\n | 'capturedMinor'\n | 'capturedAt'\n | 'refundedMinor'\n | 'captureMethod'\n | 'notes'\n | 'metadata'\n | 'paymentIntentId'\n | 'source'\n | 'updatedAt'\n\n/**\n * An order as a storefront reads it. A key holder reads the confirmation page's fields; its owner's session adds the\n * email, both full addresses and the order's id. Every other field is optional, so an adapter that returns a plain\n * `Order` still fits.\n */\nexport interface ShopperOrder\n extends\n Omit<Order, OwnerOrStoreField | 'shippingAddress'>,\n Partial<Pick<Order, OwnerOrStoreField>> {\n shippingAddress: ShopperAddress\n timeline?: OrderTimelineEntry[]\n}\n","// Product\nexport const EVT_PRODUCT_CREATED = 'product.created.v1'\nexport const EVT_PRODUCT_UPDATED = 'product.updated.v1'\nexport const EVT_PRODUCT_DELETED = 'product.deleted.v1'\nexport const EVT_PRODUCT_PUBLISHED = 'product.published.v1'\nexport const EVT_PRODUCT_UNPUBLISHED = 'product.unpublished.v1'\nexport const EVT_PRODUCT_VIEWED = 'product.viewed.v1'\n/** A variant's stock went from 0 to above 0, in the transaction of the inventory.changed.v1 it accompanies. */\nexport const EVT_PRODUCT_RESTOCKED = 'product.restocked.v1'\n/** A variant's price or compare-at price changed, in the transaction of the product.updated.v1 it accompanies. */\nexport const EVT_PRODUCT_VARIANT_PRICE_CHANGED = 'product.variant_price_changed.v1'\n\n// Category\nexport const EVT_CATEGORY_CREATED = 'category.created.v1'\nexport const EVT_CATEGORY_UPDATED = 'category.updated.v1'\nexport const EVT_CATEGORY_DELETED = 'category.deleted.v1'\n\n// Order\nexport const EVT_ORDER_PLACED = 'order.placed.v1'\nexport const EVT_ORDER_PAID = 'order.paid.v1'\nexport const EVT_ORDER_FULFILLED = 'order.fulfilled.v1'\nexport const EVT_ORDER_SHIPPED = 'order.shipped.v1'\nexport const EVT_ORDER_DELIVERED = 'order.delivered.v1'\nexport const EVT_ORDER_CANCELLED = 'order.cancelled.v1'\nexport const EVT_ORDER_REFUNDED = 'order.refunded.v1'\nexport const EVT_ORDER_PARTIALLY_REFUNDED = 'order.partially_refunded.v1'\nexport const EVT_ORDER_STATUS_CHANGED = 'order.status_changed.v1'\nexport const EVT_ORDER_CONFIRMATION_RESEND_REQUESTED = 'order.confirmation_resend_requested.v1'\n/** An imported order taken back out of the store, with every row it owned. */\nexport const EVT_ORDER_DELETED = 'order.deleted.v1'\n\n// Inventory\nexport const EVT_INVENTORY_CHANGED = 'inventory.changed.v1'\nexport const EVT_INVENTORY_RESERVED = 'inventory.reserved.v1'\nexport const EVT_INVENTORY_RELEASED = 'inventory.released.v1'\nexport const EVT_INVENTORY_LOW_STOCK = 'inventory.low_stock.v1'\nexport const EVT_INVENTORY_OUT_OF_STOCK = 'inventory.out_of_stock.v1'\n\n// Customer\nexport const EVT_CUSTOMER_CREATED = 'customer.created.v1'\nexport const EVT_CUSTOMER_UPDATED = 'customer.updated.v1'\nexport const EVT_CUSTOMER_DELETED = 'customer.deleted.v1'\n// Legacy alias (kept so Prompt 1 seed code continues to compile).\nexport const EVT_CUSTOMER_REGISTERED = EVT_CUSTOMER_CREATED\n\n// Visitor\nexport const EVT_VISITOR_MERGED = 'visitor.merged.v1'\n\n// Cart\nexport const EVT_CART_CREATED = 'cart.created.v1'\nexport const EVT_CART_ITEM_ADDED = 'cart.item_added.v1'\nexport const EVT_CART_ITEM_REMOVED = 'cart.item_removed.v1'\nexport const EVT_CART_UPDATED = 'cart.updated.v1'\nexport const EVT_CART_ABANDONED = 'cart.abandoned.v1'\nexport const EVT_CART_CONVERTED = 'cart.converted.v1'\n\n// Payment\nexport const EVT_PAYMENT_INTENT_CREATED = 'payment.intent_created.v1'\nexport const EVT_PAYMENT_CAPTURED = 'payment.captured.v1'\nexport const EVT_PAYMENT_FAILED = 'payment.failed.v1'\nexport const EVT_PAYMENT_REFUNDED = 'payment.refunded.v1'\n\n// Alerts\nexport const EVT_ALERT_REQUESTED = 'alert.requested.v1'\n/** A subscribe for an alert already active: the subscription stands and nothing is sent. */\nexport const EVT_ALERT_REPEATED = 'alert.repeated.v1'\nexport const EVT_ALERT_CONFIRMED = 'alert.confirmed.v1'\nexport const EVT_ALERT_UNSUBSCRIBED = 'alert.unsubscribed.v1'\nexport const EVT_ALERT_SENT = 'alert.sent.v1'\n/** A pending subscription's confirmation was sent; another goes only once the confirmation interval has passed. */\nexport const EVT_ALERT_CONFIRMATION_SENT = 'alert.confirmation_sent.v1'\n\n// Reviews\n/** A review the reviews adapter took for the store; the kernel resolved its reviewer and the order it is verified by. */\nexport const EVT_REVIEW_SUBMITTED = 'review.submitted.v1'\n\n// Proposals: an agent's write a person or a policy decides\nexport const EVT_PROPOSAL_CREATED = 'proposal.created.v1'\nexport const EVT_PROPOSAL_APPROVED = 'proposal.approved.v1'\nexport const EVT_PROPOSAL_REJECTED = 'proposal.rejected.v1'\nexport const EVT_PROPOSAL_APPLIED = 'proposal.applied.v1'\nexport const EVT_PROPOSAL_FAILED = 'proposal.failed.v1'\n\n// System\nexport const EVT_ADAPTER_ERROR = 'adapter.error.v1'\nexport const EVT_SETTINGS_CHANGED = 'settings.changed.v1'\n\nexport const STANDARD_EVENT_TYPES = [\n EVT_PRODUCT_CREATED,\n EVT_PRODUCT_UPDATED,\n EVT_PRODUCT_DELETED,\n EVT_PRODUCT_PUBLISHED,\n EVT_PRODUCT_UNPUBLISHED,\n EVT_PRODUCT_VIEWED,\n EVT_PRODUCT_RESTOCKED,\n EVT_PRODUCT_VARIANT_PRICE_CHANGED,\n EVT_CATEGORY_CREATED,\n EVT_CATEGORY_UPDATED,\n EVT_CATEGORY_DELETED,\n EVT_ORDER_PLACED,\n EVT_ORDER_PAID,\n EVT_ORDER_FULFILLED,\n EVT_ORDER_SHIPPED,\n EVT_ORDER_DELIVERED,\n EVT_ORDER_CANCELLED,\n EVT_ORDER_REFUNDED,\n EVT_ORDER_PARTIALLY_REFUNDED,\n EVT_ORDER_STATUS_CHANGED,\n EVT_ORDER_CONFIRMATION_RESEND_REQUESTED,\n EVT_ORDER_DELETED,\n EVT_INVENTORY_CHANGED,\n EVT_INVENTORY_RESERVED,\n EVT_INVENTORY_RELEASED,\n EVT_INVENTORY_LOW_STOCK,\n EVT_INVENTORY_OUT_OF_STOCK,\n EVT_CUSTOMER_CREATED,\n EVT_CUSTOMER_UPDATED,\n EVT_CUSTOMER_DELETED,\n EVT_VISITOR_MERGED,\n EVT_CART_CREATED,\n EVT_CART_ITEM_ADDED,\n EVT_CART_ITEM_REMOVED,\n EVT_CART_UPDATED,\n EVT_CART_ABANDONED,\n EVT_CART_CONVERTED,\n EVT_PAYMENT_INTENT_CREATED,\n EVT_PAYMENT_CAPTURED,\n EVT_PAYMENT_FAILED,\n EVT_PAYMENT_REFUNDED,\n EVT_ALERT_REQUESTED,\n EVT_ALERT_REPEATED,\n EVT_ALERT_CONFIRMED,\n EVT_ALERT_UNSUBSCRIBED,\n EVT_ALERT_SENT,\n EVT_ALERT_CONFIRMATION_SENT,\n EVT_REVIEW_SUBMITTED,\n EVT_PROPOSAL_CREATED,\n EVT_PROPOSAL_APPROVED,\n EVT_PROPOSAL_REJECTED,\n EVT_PROPOSAL_APPLIED,\n EVT_PROPOSAL_FAILED,\n EVT_ADAPTER_ERROR,\n EVT_SETTINGS_CHANGED,\n] as const\n","import type { Logger } from './http.js'\n\nexport type HealthStatus = 'healthy' | 'degraded' | 'down'\n\nexport interface HealthReport {\n status: HealthStatus\n detail?: string\n}\n\n/** How long a health check may run before it reports `down`. */\nexport const HEALTH_CHECK_TIMEOUT_MS = 5000\n\nexport interface BoundedHealthCheckOptions {\n /** `HEALTH_CHECK_TIMEOUT_MS` when not given. */\n timeoutMs?: number\n /** Values an error's message must never show (a URL's user, password or query values); each becomes `***`. */\n redact?: readonly string[]\n}\n\n/** An error's message for a report; an AggregateError (a refused connection to every address of a name) lists its own. */\nexport function errorText(error: unknown): string {\n if (!(error instanceof Error)) return String(error)\n if (error.message !== '') return error.message\n const inner = (error as { errors?: unknown }).errors\n if (Array.isArray(inner) && inner.length > 0) return inner.map(errorText).join('; ')\n const code = (error as { code?: unknown }).code\n return typeof code === 'string' ? code : error.name\n}\n\n/**\n * The shortest value worth removing from text as a secret. A shorter one is too likely to be an ordinary word (a local\n * password of `nymbal`), whose removal garbles the text; user info and query strings go with the URL rewrite anyway.\n */\nexport const SECRET_MIN_LENGTH = 8\n\n/**\n * The values in a connection URL that are credentials or may carry one: its password and its query values, and its\n * user unless `user` is false, each as written and decoded, if at least `SECRET_MIN_LENGTH` long. A user is worth\n * hiding in a driver's error, not in our own text.\n */\nexport function connectionSecrets(url: string, options: { user?: boolean } = {}): string[] {\n let parsed: URL\n try {\n parsed = new URL(url)\n } catch {\n return []\n }\n const raw = [\n ...(options.user === false ? [] : [parsed.username]),\n parsed.password,\n ...parsed.searchParams.values(),\n ]\n const decoded = raw.map((value) => {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n })\n return [...new Set([...raw, ...decoded])].filter((value) => value.length >= SECRET_MIN_LENGTH)\n}\n\n/**\n * A connection URL as it may be printed: `scheme://host:port/path`, with no user, password, query or fragment. Text\n * that is not a URL comes back as `<unparsable url>`, since it may be a URL with a secret the parser could not read.\n */\nexport function displayUrl(url: string): string {\n try {\n const parsed = new URL(url)\n return `${parsed.protocol}//${parsed.host}${parsed.pathname === '/' ? '' : parsed.pathname}`\n } catch {\n return '<unparsable url>'\n }\n}\n\n/**\n * Runs `check` within the bound. A check that throws is `down` with the error's message, every `redact` value in it\n * replaced; one still running when the bound passes is `down` with `timed out after <n> ms`, and its signal aborted.\n */\nexport async function boundedHealthCheck(\n check: (signal: AbortSignal) => Promise<HealthReport> | HealthReport,\n options: BoundedHealthCheckOptions = {},\n): Promise<HealthReport> {\n const timeoutMs = options.timeoutMs ?? HEALTH_CHECK_TIMEOUT_MS\n const controller = new AbortController()\n let timer: ReturnType<typeof setTimeout> | undefined\n const timedOut = new Promise<HealthReport>((resolve) => {\n timer = setTimeout(() => {\n controller.abort()\n resolve({ status: 'down', detail: `timed out after ${timeoutMs} ms` })\n }, timeoutMs)\n })\n const failed = (error: unknown): HealthReport => {\n let detail = errorText(error)\n for (const secret of options.redact ?? []) {\n if (secret !== '') detail = detail.split(secret).join('***')\n }\n return { status: 'down', detail }\n }\n try {\n // Started inside a promise so a check that throws before returning one, or returns a plain report, is still\n // mapped rather than escaping the caller.\n const checked = Promise.resolve()\n .then(() => check(controller.signal))\n .catch(failed)\n return await Promise.race([checked, timedOut])\n } finally {\n clearTimeout(timer)\n }\n}\n\nexport interface AdapterBase {\n /** What this adapter offers, for the app's per-slot report; each adapter answers for itself. */\n capabilities(): readonly string[]\n readonly producesEvents: readonly string[]\n readonly consumesEvents: readonly string[]\n initialize(config: unknown): Promise<void>\n healthCheck(): Promise<HealthReport>\n}\n\n/** The commerce slots a store's config names a provider for. */\nexport type CommerceSlot =\n | 'payments'\n | 'email'\n | 'reviews'\n | 'search'\n | 'analytics'\n | 'shipping'\n | 'tax'\n | 'ai'\n\n/**\n * Every slot an adapter package can fill: the commerce slots, the infrastructure primitives and `security`, whose\n * choice is `security.adapter`.\n */\nexport type Slot = CommerceSlot | 'documentStore' | 'eventBus' | 'media' | 'security'\n\n/**\n * What the kernel hands an adapter package's `createAdapter`. A package that serves more than one slot switches on\n * `slot`; `config` is a commerce slot's `config` block from nymbal.config.ts, a primitive's connection block (or a\n * third-party primitive's `config`), or for `security` the block named after the adapter (`security.cloudflare`) or a\n * third-party adapter's `security.config`.\n */\nexport interface AdapterContext {\n slot: Slot\n config: Record<string, unknown>\n logger: Logger\n env: NodeJS.ProcessEnv\n}\n\n/**\n * One field of a slot's config block that a store owner may fill from the admin's Settings, as a provider package\n * declares it in its `settings` export. The config block wins, then `env`, then a saved value.\n */\nexport interface AdapterSetting {\n /** A dot path inside the slot's config block: `secretKey`, `smtp.pass`. */\n path: string\n label: string\n /** Stored encrypted and never shown again once saved. */\n secret: boolean\n /** The environment variable that sets the field when the config block does not. */\n env?: string\n /**\n * The adapter works without it (SMTP without authentication), so a secret left unset does not leave the slot\n * unconfigured.\n */\n optional?: boolean\n}\n\n/**\n * What a provider package exports: `createAdapter`, the slot and provider it serves (checked when it is loaded), and\n * the fields it lets Settings fill.\n */\nexport interface ProviderPackageModule {\n createAdapter(ctx: AdapterContext): unknown\n slot: Slot\n provider: string\n settings?: readonly AdapterSetting[]\n}\n\n/**\n * How `nymbal.config.ts` names an infrastructure slot's adapter (`documentStore`, `eventBus`, `media`): the provider,\n * the npm package it comes from when not `@nymbalhq/<slot>-<provider>`, and a third-party adapter's `ctx.config`.\n */\nexport interface InfrastructureBlock {\n provider: string\n package?: string\n config?: Record<string, unknown>\n}\n","import { ValidationError } from '../errors.js'\nimport type { QueryParams, QueryResult } from './document-store.js'\n\n// The one order and filter every document store is held to: the in-memory store runs it, and stores that cannot\n// order or filter the same way on their server fetch candidates and run it in the process.\n\n// Made on first use: a top-level `new` is a side effect bundlers keep, so every storefront bundle that imports\n// these types would carry it.\nlet collator: Intl.Collator | undefined\n\n/** Number, then string, then any other type, then null and missing: so values of different types never tie. */\nfunction rankOf(value: unknown): number {\n if (typeof value === 'number') return 0\n if (typeof value === 'string') return 1\n if (value === null || value === undefined) return 3\n return 2\n}\n\nfunction sign(n: number): number {\n return n > 0 ? 1 : n < 0 ? -1 : 0\n}\n\nfunction compareOther(a: unknown, b: unknown): number {\n const ta = typeof a\n const tb = typeof b\n if (ta !== tb) return ta < tb ? -1 : 1\n if (ta === 'boolean') return Number(a) - Number(b)\n const ja = JSON.stringify(a) ?? ''\n const jb = JSON.stringify(b) ?? ''\n return ja < jb ? -1 : ja > jb ? 1 : 0\n}\n\n/**\n * A total order over document values: numbers numerically, strings by `Intl.Collator` at base sensitivity (case and\n * accents tie), other types after strings, and null or missing last. Values of different types rank by type first.\n */\nexport function compareDocumentValues(a: unknown, b: unknown): number {\n const ra = rankOf(a)\n const rb = rankOf(b)\n if (ra !== rb) return ra - rb\n switch (ra) {\n case 0:\n return sign((a as number) - (b as number))\n case 1:\n collator ??= new Intl.Collator(undefined, { sensitivity: 'base' })\n return sign(collator.compare(a as string, b as string))\n case 2:\n return sign(compareOther(a, b))\n default:\n return 0\n }\n}\n\nexport type SortKeyCondition = Pick<NonNullable<QueryParams['sortKey']>, 'operator' | 'value'>\n\n/** `eq` is `===`; `gt`, `lt` and `between` (inclusive, `[low, high]`) are `compareDocumentValues`. */\nexport function matchesSortKey(value: unknown, condition: SortKeyCondition): boolean {\n const target = condition.value\n switch (condition.operator) {\n case 'eq':\n return value === target\n case 'gt':\n return compareDocumentValues(value, target) > 0\n case 'lt':\n return compareDocumentValues(value, target) < 0\n case 'between': {\n if (!Array.isArray(target) || target.length !== 2) return false\n const [low, high] = target as [unknown, unknown]\n return compareDocumentValues(value, low) >= 0 && compareDocumentValues(value, high) <= 0\n }\n default:\n return false\n }\n}\n\n/** The value at a dotted path (`priceRange.minMinor`); missing anywhere along it is `undefined`. */\nexport function documentValueAt(doc: unknown, path: string): unknown {\n let value: unknown = doc\n for (const part of path.split('.')) {\n if (value === null || typeof value !== 'object') return undefined\n value = (value as Record<string, unknown>)[part]\n }\n return value\n}\n\nexport const DEFAULT_QUERY_LIMIT = 50\n\ninterface Order {\n /** The field ordered by, or null for the document key alone. */\n field: string | null\n direction: 'asc' | 'desc'\n}\n\ninterface Position {\n key: string\n value: unknown\n}\n\nfunction orderOf(params: QueryParams): Order {\n if (params.orderBy !== undefined) {\n return { field: params.orderBy.field, direction: params.orderBy.direction }\n }\n const direction = (params.ascending ?? true) ? 'asc' : 'desc'\n return { field: params.sortKey?.field ?? null, direction }\n}\n\nconst isNullish = (value: unknown): boolean => value === null || value === undefined\n\n/** Nulls and missing values last in either direction; ties by key ascending, so no two documents tie. */\nfunction comparePositions(a: Position, b: Position, order: Order): number {\n if (order.field !== null) {\n const an = isNullish(a.value)\n const bn = isNullish(b.value)\n if (an !== bn) return an ? 1 : -1\n let c = an ? 0 : compareDocumentValues(a.value, b.value)\n if (order.direction === 'desc') c = -c\n if (c !== 0) return c\n return a.key < b.key ? -1 : a.key > b.key ? 1 : 0\n }\n const c = a.key < b.key ? -1 : a.key > b.key ? 1 : 0\n return order.direction === 'desc' ? -c : c\n}\n\n/** What a cursor is bound to: the order and the partition, so a cursor reused under another is refused. */\nfunction cursorScope(order: Order, params: QueryParams): string {\n return JSON.stringify([order.field, order.direction, params.partitionKey])\n}\n\nfunction decodeCursor(cursor: string, scope: string): Position {\n let decoded: unknown\n try {\n decoded = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'))\n } catch {\n decoded = null\n }\n const c = decoded as { o?: unknown; k?: unknown; v?: unknown } | null\n if (c === null || typeof c !== 'object' || typeof c.o !== 'string' || typeof c.k !== 'string') {\n throw new ValidationError('The cursor is not valid', { context: { cursor } })\n }\n if (c.o !== scope) {\n throw new ValidationError('The cursor was issued under another order', { context: { cursor } })\n }\n return { key: c.k, value: c.v ?? null }\n}\n\n/**\n * One page of `params` over the documents of a collection: filter by partition (`'*'` for all) and sort key, order,\n * then seek past the cursor by value, so a document deleted between pages neither restarts nor repeats the walk.\n */\nexport function queryDocuments<T>(\n documents: Iterable<readonly [string, unknown]>,\n params: QueryParams,\n): QueryResult<T> {\n const limit = params.limit ?? DEFAULT_QUERY_LIMIT\n const order = orderOf(params)\n const scope = cursorScope(order, params)\n const after = params.cursor ? decodeCursor(params.cursor, scope) : null\n const { partitionKey, sortKey } = params\n\n const matches: Array<{ position: Position; doc: unknown }> = []\n for (const [key, doc] of documents) {\n const record = (doc ?? {}) as Record<string, unknown>\n if (partitionKey.value !== '*' && record[partitionKey.field] !== partitionKey.value) continue\n if (sortKey !== undefined && !matchesSortKey(record[sortKey.field], sortKey)) continue\n const value = order.field === null ? null : (documentValueAt(record, order.field) ?? null)\n const position = { key, value }\n if (after !== null && comparePositions(position, after, order) <= 0) continue\n matches.push({ position, doc })\n }\n matches.sort((a, b) => comparePositions(a.position, b.position, order))\n\n const page = matches.slice(0, limit)\n const last = page[page.length - 1]\n const nextCursor =\n matches.length > limit && last !== undefined\n ? Buffer.from(\n JSON.stringify({ o: scope, k: last.position.key, v: last.position.value }),\n 'utf8',\n ).toString('base64url')\n : null\n return { items: page.map((m) => m.doc as T), nextCursor }\n}\n","import { AdapterError } from '../errors.js'\nimport type { NymbalEvent, EventHandler } from '../events/envelope.js'\nimport type { HealthReport } from './adapter-base.js'\n\nexport interface RetryPolicy {\n maxAttempts: number\n backoff: 'exponential' | 'linear' | 'fixed'\n initialDelayMs?: number\n}\n\nexport interface SubscriptionConfig {\n retryPolicy?: RetryPolicy\n deadLetterQueue?: string\n batchSize?: number\n /**\n * A named subscription is handled once per name across every bus instance (one consumer group); an unnamed one is\n * handled by every subscriber on every instance. On a single-process bus the two are the same.\n */\n name?: string\n /** The subscriber only maintains derived state (a projection, a search index): it also receives `replay`. */\n derived?: boolean\n}\n\nexport interface Subscription {\n id: string\n patterns: string[]\n unsubscribe(): Promise<void>\n}\n\n/** A named subscription whose handler threw on an event. */\nexport interface HandlerFailure {\n subscription: string\n message: string\n}\n\n/** What `publish` rejects with when a named subscription's handler threw: code `adapter.handler_failed`. */\nexport function handlerFailedError(\n event: NymbalEvent<unknown>,\n failed: HandlerFailure[],\n): AdapterError {\n return new AdapterError(\n 'handler_failed',\n `${event.type} (${event.id}) failed in ${failed.map((f) => f.subscription).join(', ')}`,\n { context: { eventId: event.id, eventType: event.type, failed } },\n )\n}\n\n/**\n * Delivery is at least once: a failed delivery is published again, to every matching subscription, so every handler\n * is idempotent (projections overwrite their documents, side effects use `withIdempotency`). The bus never retries a\n * failed handler by itself.\n */\nexport interface EventBusAdapter {\n capabilities(): readonly string[]\n /**\n * Resolves only after every matching subscription this instance holds has handled the event, so a read straight\n * after a command sees its projections. When a named subscription's handler throws, it rejects with\n * `handlerFailedError` naming each one that threw, once every matching handler has settled. An unnamed subscription's\n * throw is logged and does not reject.\n */\n publish<T>(event: NymbalEvent<T>): Promise<void>\n /** Hands every event out in order, then rejects as `publish` does for the first event a named handler failed. */\n publishBatch(events: NymbalEvent<unknown>[]): Promise<void>\n /**\n * Delivers to `derived` subscriptions only, so rebuilding derived state never triggers side effects. A handler's\n * throw is logged and does not reject.\n */\n replay(events: NymbalEvent<unknown>[]): Promise<void>\n subscribe(\n eventPattern: string | string[],\n handler: EventHandler,\n config?: SubscriptionConfig,\n ): Promise<Subscription>\n unsubscribe(subscription: Subscription): Promise<void>\n close(): Promise<void>\n /**\n * Whether the bus can deliver, within `HEALTH_CHECK_TIMEOUT_MS`. Optional, so a bus written before it existed still\n * type-checks; `nymbal doctor` reports one without it as `no healthCheck`.\n */\n healthCheck?(): Promise<HealthReport>\n}\n","export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n\n/** The request header a caller may name its own correlation id in, and the response header that echoes the one used. */\nexport const CORRELATION_ID_HEADER = 'x-correlation-id'\n\nconst CORRELATION_ID = /^[A-Za-z0-9._:-]{1,128}$/\n\n/**\n * Whether `s` may be used as a correlation id: it reaches logs and the event log, so an inbound value that fails is\n * replaced, never cleaned. The id is a label only; nothing reads it to decide access.\n */\nexport function isValidCorrelationId(s: string): boolean {\n return CORRELATION_ID.test(s)\n}\n\nexport interface RequestAuth {\n userId?: string\n roles?: string[]\n token?: string\n}\n\nexport interface Logger {\n trace(obj: unknown, msg?: string): void\n debug(obj: unknown, msg?: string): void\n info(obj: unknown, msg?: string): void\n warn(obj: unknown, msg?: string): void\n error(obj: unknown, msg?: string): void\n child(bindings: Record<string, unknown>): Logger\n}\n\nexport interface RequestContext<TBody = unknown, TQuery = Record<string, string | string[] | undefined>, TParams = Record<string, string>> {\n method: HttpMethod\n path: string\n url: string\n params: TParams\n query: TQuery\n body: TBody\n rawBody?: Buffer\n headers: Record<string, string | string[] | undefined>\n cookies: Record<string, string>\n auth?: RequestAuth\n requestId: string\n /** The inbound `x-correlation-id` when `isValidCorrelationId` accepts it, else the request id. */\n correlationId: string\n logger: Logger\n ip: string\n /** The agent a tool call is made for. Only the tool invoker sets it; no HTTP adapter does, so no header can claim it. */\n agent?: { name: string }\n}\n\nexport interface ResponseCookie {\n name: string\n value: string\n path?: string\n domain?: string\n maxAge?: number\n httpOnly?: boolean\n secure?: boolean\n sameSite?: 'strict' | 'lax' | 'none'\n}\n\nexport interface ResponseEnvelope<T = unknown> {\n status: number\n headers?: Record<string, string>\n body?: T\n cookies?: ResponseCookie[]\n}\n\nexport type RouteHandler<TBody = unknown, TQuery = Record<string, string | string[] | undefined>, TParams = Record<string, string>, TResponse = unknown> = (\n ctx: RequestContext<TBody, TQuery, TParams>,\n) => Promise<ResponseEnvelope<TResponse>> | ResponseEnvelope<TResponse>\n\nexport interface RouteOptions {\n name?: string\n csrf?: boolean\n rateLimit?: string\n rawBody?: boolean\n}\n\nexport type MiddlewareHandler = (\n ctx: RequestContext,\n) => Promise<ResponseEnvelope | void> | ResponseEnvelope | void\n\nexport interface SseEvent {\n id?: string\n event?: string\n data: unknown\n}\n\nexport interface StreamContext {\n send(event: SseEvent): void\n close(): void\n onClose(callback: () => void): void\n}\n\nexport type StreamHandler = (\n ctx: RequestContext,\n stream: StreamContext,\n) => Promise<void> | void\n\nexport interface HttpAdapter {\n registerRoute(\n method: HttpMethod,\n path: string,\n handler: RouteHandler,\n options?: RouteOptions,\n ): void\n registerStreamRoute(\n method: 'GET',\n path: string,\n handler: StreamHandler,\n options?: RouteOptions,\n ): void\n registerMiddleware(middleware: MiddlewareHandler): void\n setDefaultHeaders(headers: Record<string, string>): void\n start(port: number, host?: string): Promise<void>\n stop(): Promise<void>\n}\n\n/** A JSON Schema whose root is an object, as an MCP tool's `inputSchema` must be. */\nexport interface JsonSchemaObject {\n type: 'object'\n properties?: Record<string, unknown>\n required?: string[]\n [keyword: string]: unknown\n}\n\n/** Who a tool runs for: the verified bearer's user (none for an anonymous agent) and the agent's client info. */\nexport interface Caller {\n auth?: RequestAuth\n agent: { name: string; version?: string }\n /** The address the agent's request came from, for the route's rate rule. */\n ip?: string\n}\n\n/**\n * One route of the API as an agent tool. `scope` is the least caller the route's gate admits; `kind` is `read` for a\n * GET, `session` for a write that changes nothing audited (the cart), `command` for an audited command.\n */\nexport interface ToolDefinition {\n name: string\n title: string\n description: string\n inputSchema: JsonSchemaObject\n scope: 'public' | 'customer' | 'admin'\n kind: 'read' | 'session' | 'command'\n route: { method: HttpMethod; path: string; command: string | null | undefined }\n}\n\nexport interface ToolResult {\n content: Array<{ type: 'text'; text: string }>\n structuredContent?: Record<string, unknown>\n isError?: boolean\n}\n\n/** What a caller of the tool surface may ask of one call beside its arguments. */\nexport interface ToolCallOptions {\n /** The correlation id the call's request, its proposal and its audit entries carry; a fresh one when absent. */\n correlationId?: string\n /** Keep a command as a pending proposal whatever the policy says (`agents.autoApprove`, `agents.mcp.writes`). */\n proposeWrites?: boolean\n}\n\n/** The tools a store serves to agents; the transports (HTTP, stdio) bind it. */\nexport interface ToolSurface {\n list(caller: Caller): ToolDefinition[]\n call(name: string, args: unknown, caller: Caller, options?: ToolCallOptions): Promise<ToolResult>\n}\n","import type { AdapterBase } from './adapter-base.js'\nimport type { HttpAdapter } from './http.js'\n\nexport type RateLimitAction = 'block' | 'challenge' | 'throttle'\n\nexport interface RateLimitRule {\n requests: number\n window: string\n action: RateLimitAction\n}\n\nexport interface RateLimitDecision {\n allowed: boolean\n retryAfterSeconds?: number\n action: RateLimitAction\n}\n\nexport interface SecurityConfigLike {\n rateLimit: Record<string, RateLimitRule>\n csrf: boolean\n headers: {\n hsts: boolean\n contentSecurityPolicy: string\n referrerPolicy: string\n xFrameOptions: string\n }\n [key: string]: unknown\n}\n\nexport interface ValidationResult {\n valid: boolean\n errors: Array<{ code: string; message: string; path?: string }>\n}\n\nexport interface DriftReport {\n drift: boolean\n differences: Array<{ path: string; configured: unknown; actual: unknown }>\n}\n\nexport interface SecurityState {\n rulesApplied: string[]\n lastValidatedAt: string\n}\n\n/**\n * What a security adapter tells the HTTP layer about the network in front of the store. Each value counts only when the\n * store sets neither its variable nor its config key.\n */\nexport interface SecurityNetwork {\n /** The proxies whose `X-Forwarded-For` (and client-address header) the API believes: presets, addresses or CIDRs. */\n trustedProxies?: string[]\n /** One header a listed proxy sets to the client's address, such as `CF-Connecting-IP`. */\n clientIpHeader?: string\n /** Whether `clientIpHeader` may be honoured only on a request the origin lock admitted. */\n clientIpRequiresLock?: boolean\n}\n\n/** Where a resolved network value came from: the variable, the config key, the adapter's name, or `default`. */\nexport interface NetworkSource {\n kind: 'env' | 'config' | 'adapter' | 'default'\n name: string\n}\n\n/** The network block the HTTP server is built from, resolved once at boot. */\nexport interface ResolvedNetwork {\n trustedProxies: string[]\n trustedProxiesSource: NetworkSource\n clientIpHeader?: string\n clientIpHeaderSource?: NetworkSource\n clientIpRequiresLock: boolean\n /** Present when `NYMBAL_ORIGIN_SECRET` is set: the header the CDN proves itself in, and the secrets it may send. */\n originLock?: ResolvedOriginLock\n}\n\n/** The origin lock, resolved once at boot: every entry is accepted, so a secret can be rotated without downtime. */\nexport interface ResolvedOriginLock {\n header: string\n secrets: readonly string[]\n}\n\n/**\n * Whether the CDN sends the origin secret: `missing` when it has no enabled rule; `present` with the header the rule\n * sets and `entry`, the 0-based index of the secret it sends, or null when it sends none of them.\n */\nexport type OriginLockRule =\n | { state: 'missing' }\n | { state: 'present'; header: string; entry: number | null }\n\nexport interface SecurityAdapter extends AdapterBase {\n applyMiddleware(app: HttpAdapter, config: SecurityConfigLike): Promise<void>\n validate(config: SecurityConfigLike): Promise<ValidationResult>\n drift?(config: SecurityConfigLike): Promise<DriftReport>\n inspect?(): Promise<SecurityState>\n /** The adapter's network settings, offered at boot; synchronous and without I/O. */\n network?(): SecurityNetwork\n /** Whether the CDN in front sends one of the lock's secrets, read from the CDN itself. */\n originLockRule?(lock: ResolvedOriginLock): Promise<OriginLockRule>\n}\n\n/** The response headers every security adapter sets in process, from the config's `headers` block. */\nexport function buildSecurityHeaders(security: SecurityConfigLike): Record<string, string> {\n const headers: Record<string, string> = {}\n if (security.headers.hsts) {\n headers['strict-transport-security'] = 'max-age=31536000; includeSubDomains'\n }\n const csp = security.headers.contentSecurityPolicy\n const cspValue =\n csp === 'strict'\n ? \"default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'\"\n : csp === 'relaxed'\n ? \"default-src 'self' https:; img-src * data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'\"\n : csp === 'off'\n ? null\n : csp\n if (cspValue) headers['content-security-policy'] = cspValue\n headers['referrer-policy'] = security.headers.referrerPolicy\n headers['x-frame-options'] = security.headers.xFrameOptions === 'deny' ? 'DENY' : 'SAMEORIGIN'\n headers['x-content-type-options'] = 'nosniff'\n return headers\n}\n","import type { AdapterBase } from './adapter-base.js'\nimport type {\n PaymentIntent,\n PaymentIntentParams,\n PaymentCapture,\n Refund,\n PaymentStatus,\n WebhookResult,\n} from '../domain/payment.js'\nimport type { CustomerProfile } from '../domain/customer.js'\nimport type { Address } from '../domain/order.js'\nimport type { Review, ReviewModerationStatus, AggregateRating } from '../domain/review.js'\nimport type { JsonSchemaObject } from './http.js'\n\n/** A webhook request's headers, names lower-cased. */\nexport type WebhookHeaders = Record<string, string | string[] | undefined>\n\n/** One event a webhook delivery carries; the kernel builds its envelope (id, source, correlation). */\nexport interface WebhookEventDraft {\n type: string\n payload: Record<string, unknown>\n}\n\nexport interface WebhookDelivery {\n verified: boolean\n /** The provider's id for this delivery: what makes a retry of it a replay. */\n deliveryId: string\n events: WebhookEventDraft[]\n}\n\nexport interface CommerceAdapter extends AdapterBase {\n readonly providerName: string\n /**\n * Takes a delivery to `POST /api/webhooks/<providerName>`: verifies its signature over `rawBody` (from whichever\n * header the provider signs in) and returns its events. The same bytes must give the same `deliveryId` and the same\n * events, so a provider's retry is processed once. Each event's type is `<providerName>.<event>.v<n>` (lower-case\n * words, `_` allowed), is listed in `producesEvents`, and its payload is a plain JSON object; a delivery breaking\n * this is refused whole. A kernel namespace (`order`, `payment`, ...) is never an adapter's. Not called on a\n * payments adapter, whose deliveries go through `parseWebhook`.\n */\n receiveWebhook?(rawBody: Buffer, headers: WebhookHeaders): Promise<WebhookDelivery>\n}\n\n// --- Payments ---------------------------------------------------------------\n\nexport interface RefundOptions {\n /** The same key on a retry makes the provider refund at most once. */\n idempotencyKey?: string\n /** The order's currency: what a provider that no longer holds the payment answers in. */\n currency?: string\n}\n\n/**\n * The storefront half of a payments adapter: which client-side driver collects the payment and the public values it\n * needs. GET /api/capabilities serves `options` to every shopper, so it holds public values only.\n */\nexport interface PaymentClient {\n driver: string\n options: Record<string, string>\n}\n\nexport interface PaymentsAdapter extends CommerceAdapter {\n readonly kind: 'payments'\n /** How a storefront collects a payment; absent or `null` when there is no client step. */\n client?(): PaymentClient | null\n createPaymentIntent(params: PaymentIntentParams): Promise<PaymentIntent>\n capturePayment(intentId: string, amountMinor?: number): Promise<PaymentCapture>\n /** Throws `PaymentError('refund_rejected')` when no money moved, `PaymentError('refund_unknown')` when it may have. */\n refund(\n paymentId: string,\n amountMinor?: number,\n reason?: string,\n opts?: RefundOptions,\n ): Promise<Refund>\n getPaymentStatus(intentId: string): Promise<PaymentStatus>\n /** The intent as the provider holds it now, client secret included: what a shopper needs to pay it again. */\n getPaymentIntent(intentId: string): Promise<PaymentIntent>\n /** Cancels an intent that has not been paid; throws `PaymentError('cancel_refused')` when the provider will not\n * (the intent is processing or already captured). */\n cancelPaymentIntent(intentId: string): Promise<void>\n parseWebhook(rawBody: Buffer | string, signature: string): Promise<WebhookResult>\n}\n\n// --- Email -----------------------------------------------------------------\n\nexport interface TransactionalEmail {\n template: string\n to: string\n vars: Record<string, unknown>\n locale?: string\n}\n\nexport interface EmailSendResult {\n messageId: string | null\n accepted: boolean\n}\n\nexport interface EmailAdapter extends CommerceAdapter {\n readonly kind: 'email'\n sendTransactional(params: TransactionalEmail): Promise<EmailSendResult>\n syncCustomerToList(customer: CustomerProfile): Promise<void>\n removeCustomerFromList(customerId: string): Promise<void>\n}\n\n// --- Reviews ---------------------------------------------------------------\n\nexport interface PaginationParams {\n limit?: number\n cursor?: string\n}\n\nexport interface ReviewsResult {\n items: Review[]\n nextCursor: string | null\n}\n\n/** What a reviews adapter can do; `capabilities()` of a reviews adapter answers a subset. */\nexport type ReviewsCapability = 'submit' | 'moderate' | 'aggregate' | 'invite' | 'widget'\n\n/**\n * The kernel resolves `customerId`, `author` and `orderId` (the reviewer's paid order holding the product) before the\n * adapter sees a submission; an adapter never looks up customers or orders. An adapter without `submit` refuses it\n * with `ReviewRefusedError('submit_unsupported')`.\n */\nexport interface ReviewSubmission {\n productId: string\n customerId: string | null\n orderId: string | null\n author: { name: string; email: string } | null\n rating: number\n title: string\n body: string\n}\n\nexport interface ReviewResult {\n review: Review\n}\n\nexport interface ReviewsAdapter extends CommerceAdapter {\n readonly kind: 'reviews'\n getProductReviews(\n productId: string,\n params?: PaginationParams & { status?: ReviewModerationStatus },\n ): Promise<ReviewsResult>\n getAggregateRating(productId: string): Promise<AggregateRating>\n submitReview(review: ReviewSubmission): Promise<ReviewResult>\n requestReview(orderId: string): Promise<void>\n}\n\n// --- Search ----------------------------------------------------------------\n\nexport interface SearchDocument {\n id: string\n type: string\n fields: Record<string, unknown>\n}\n\nexport interface IndexResult {\n indexed: number\n}\n\nexport interface SearchParams {\n limit?: number\n cursor?: string\n filters?: Record<string, unknown>\n sort?: Array<{ field: string; direction: 'asc' | 'desc' }>\n}\n\nexport interface SearchResult {\n items: SearchDocument[]\n total: number\n nextCursor: string | null\n}\n\nexport interface FacetParams {\n /** Count only the documents this query matches. */\n query?: string\n limit?: number\n filters?: Record<string, unknown>\n}\n\nexport interface Facet {\n field: string\n label: string\n values: Array<{ value: string; label?: string; count?: number }>\n}\n\nexport interface SearchAdapter extends CommerceAdapter {\n readonly kind: 'search'\n index(documents: SearchDocument[]): Promise<IndexResult>\n remove(documentIds: string[]): Promise<void>\n /**\n * Replaces the whole index with `documents`, so nothing outside them survives. Optional: without it a rebuild can\n * only `index` every document, and cannot remove ids the index holds beyond them (it has no way to list them).\n */\n replaceAll?(documents: Iterable<SearchDocument> | AsyncIterable<SearchDocument>): Promise<void>\n search(query: string, params?: SearchParams): Promise<SearchResult>\n facet(field: string, params?: FacetParams): Promise<Facet>\n}\n\n// --- Analytics -------------------------------------------------------------\n\nexport interface AnalyticsAdapter extends CommerceAdapter {\n readonly kind: 'analytics'\n track(event: string, properties: Record<string, unknown>): Promise<void>\n identify(userId: string, traits: Record<string, unknown>): Promise<void>\n}\n\n// --- Shipping --------------------------------------------------------------\n\n/** What a shipping adapter does; each word is declared exactly when the method it names is present. */\nexport const SHIPPING_CAPABILITIES = ['rates', 'labels', 'tracking'] as const\n\n/** `rates` has `rate`, `labels` has `createLabel` (and needs `rates`), `tracking` has `startTracking` and `receiveWebhook`. */\nexport type ShippingCapability = (typeof SHIPPING_CAPABILITIES)[number]\n\n/** A parcel has all three dimensions or none. Grams and millimetres, each positive. */\nexport type Parcel =\n | { weightGrams: number; lengthMm?: never; widthMm?: never; heightMm?: never }\n | { weightGrams: number; lengthMm: number; widthMm: number; heightMm: number }\n\nexport interface ShippingRateParams {\n from: Address\n to: Address\n parcel: Parcel\n}\n\nexport interface ShippingRate {\n /** Opaque to the kernel: handed back unchanged as `ShippingLabelParams.rateId`. */\n id: string\n carrier: string\n service: string\n amountMinor: number\n currency: string\n estimatedDays: number | null\n}\n\nexport interface ShippingLabelParams {\n rateId: string\n}\n\nexport interface ShippingLabel {\n labelUrl: string\n trackingNumber: string\n carrier: string\n service: string\n amountMinor: number\n currency: string\n}\n\nexport interface StartTrackingParams {\n trackingNumber: string\n /** As the merchant typed it; null leaves the carrier to the provider. */\n carrier: string | null\n}\n\n/**\n * `tracking`: the provider holds the number, now or from before. `refused`: it never will (an invalid number, an\n * unknown carrier, a refused key). An outage rejects instead, so the caller may try again.\n */\nexport type StartTrackingResult = { status: 'tracking' } | { status: 'refused'; reason: string }\n\nexport interface ShippingAdapter extends CommerceAdapter {\n readonly kind: 'shipping'\n capabilities(): readonly ShippingCapability[]\n rate?(params: ShippingRateParams): Promise<ShippingRate[]>\n createLabel?(params: ShippingLabelParams): Promise<ShippingLabel>\n startTracking?(params: StartTrackingParams): Promise<StartTrackingResult>\n}\n\n// --- Tax -------------------------------------------------------------------\n\nexport interface TaxLine {\n amountMinor: number\n qty: number\n}\n\nexport interface TaxCalculationParams {\n currency: string\n shippingCountry: string\n shippingRegion: string\n lines: TaxLine[]\n}\n\nexport interface TaxCalculationResult {\n totalTaxMinor: number\n ratePerLine: Array<{ taxMinor: number }>\n}\n\nexport interface TaxAdapter extends CommerceAdapter {\n readonly kind: 'tax'\n calculate(params: TaxCalculationParams): Promise<TaxCalculationResult>\n}\n\n// --- AI --------------------------------------------------------------------\n\n/** A tool the model may call. Its name matches `^[A-Za-z0-9_-]{1,64}$` and is unique among the tools of one request. */\nexport interface ChatTool {\n name: string\n description: string\n inputSchema: JsonSchemaObject\n}\n\n/** A call the model asked for, its arguments a JSON object. */\nexport interface ChatToolCall {\n id: string\n name: string\n args: Record<string, unknown>\n}\n\n/**\n * One turn of a conversation. The first turn is the user's. An assistant turn's `content` may be empty only when it\n * has calls; each call is answered by one `tool` turn before the next user or assistant turn.\n */\nexport type ChatMessage =\n | { role: 'user'; content: string }\n | { role: 'assistant'; content: string; toolCalls?: readonly ChatToolCall[] }\n | { role: 'tool'; toolCallId: string; content: string; isError?: boolean }\n\nexport interface ChatOptions {\n /** Instructions for the whole conversation, sent in the provider's own system field. */\n system?: string\n /** The most tokens the answer may take; 1024 when omitted. */\n maxTokens?: number\n tools?: readonly ChatTool[]\n /** Aborting it closes the request and rejects `adapter.cancelled`. */\n signal?: AbortSignal\n}\n\n/** Why the answer ended: complete, cut at `maxTokens`, waiting on its tool calls, or anything else reported. */\nexport type ChatStopReason = 'end' | 'max_tokens' | 'tool_use' | 'other'\n\nexport interface ChatResult {\n /** '' when the answer is calls only. */\n text: string\n stopReason: ChatStopReason\n /** 0 where the provider reports none. */\n usage: { inputTokens: number; outputTokens: number }\n /** The model as the provider reports it. */\n model: string\n /** The calls the model asked for, in its order; [] when none. */\n toolCalls: ChatToolCall[]\n}\n\n/** What `chatStream` yields: text as it arrives, each call once its arguments are complete, and one `done` last. */\nexport type ChatStreamEvent =\n | { type: 'text'; delta: string }\n | { type: 'tool_call'; call: ChatToolCall }\n | { type: 'done'; result: ChatResult }\n\nexport interface AiAdapter extends CommerceAdapter {\n readonly kind: 'ai'\n /** The base URL its requests go to, for `nymbal doctor`; never a key. Absent when the adapter does not say. */\n readonly endpoint?: string\n embed(texts: string[]): Promise<number[][]>\n /** The text `chat` answers to a single user turn. */\n complete(prompt: string, opts?: { maxTokens?: number }): Promise<string>\n /**\n * One request to the provider. Refused input, a provider failure and a timeout reject with an `AdapterError` whose\n * context is `{ provider, status? }` and which never carries the key.\n */\n chat(messages: readonly ChatMessage[], opts?: ChatOptions): Promise<ChatResult>\n /**\n * `chat` as it is generated: the text deltas concatenate to the `done` result's text and the `tool_call` events are\n * its calls in order. A stream broken after it started rejects `adapter.unavailable`; the whole stream is bounded as\n * one `chat`.\n */\n chatStream?(messages: readonly ChatMessage[], opts?: ChatOptions): AsyncIterable<ChatStreamEvent>\n}\n\n/** The adapter interface each commerce slot takes: what a project injects for a slot whose provider is `custom`. */\nexport interface CommerceSlotAdapters {\n payments: PaymentsAdapter\n email: EmailAdapter\n reviews: ReviewsAdapter\n search: SearchAdapter\n analytics: AnalyticsAdapter\n shipping: ShippingAdapter\n tax: TaxAdapter\n ai: AiAdapter\n}\n","import { STANDARD_EVENT_TYPES } from '../events/constants.js'\nimport type { WebhookDelivery } from './commerce.js'\n\n/** Why a delivery's events may not be published; `type` is the offending event's, when one is to blame. */\nexport interface WebhookDeliveryProblem {\n type?: unknown\n reason: string\n}\n\nconst VERSIONED_EVENT = /^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*\\.v[1-9][0-9]*$/\n\n/** Plain JSON objects only: a class instance would not survive the event log's serialisation as itself. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false\n const proto = Object.getPrototypeOf(value) as unknown\n return proto === Object.prototype || proto === null\n}\n\n/**\n * The first thing that makes a verified delivery from the adapter named `providerName` unpublishable, or null when\n * every event may be published: the rule of `receiveWebhook`, which the kernel refuses a delivery for and the\n * webhook contract suite certifies.\n */\nexport function webhookDeliveryProblem(\n providerName: string,\n producesEvents: readonly string[],\n delivery: WebhookDelivery,\n): WebhookDeliveryProblem | null {\n if (typeof delivery.deliveryId !== 'string' || delivery.deliveryId === '') {\n return { reason: 'the delivery id is empty' }\n }\n if (!Array.isArray(delivery.events)) return { reason: 'events is not an array' }\n for (const event of delivery.events as unknown[]) {\n const { type, payload } = (isPlainObject(event) ? event : {}) as {\n type?: unknown\n payload?: unknown\n }\n if (typeof type !== 'string') return { type, reason: 'an event has no type' }\n const dot = type.indexOf('.')\n // A provider name holding a dot never equals the text before the first dot, so it can publish nothing.\n if (dot === -1 || type.slice(0, dot) !== providerName) {\n return { type, reason: `the type is not in the ${providerName} namespace` }\n }\n // Computed here, not at module level: a top-level Set would keep every event type in each storefront bundle.\n if (STANDARD_EVENT_TYPES.some((standard) => standard.split('.')[0] === providerName)) {\n return { type, reason: 'the type is in a kernel namespace' }\n }\n if (!VERSIONED_EVENT.test(type.slice(dot + 1))) {\n return { type, reason: 'the type is not <provider>.<event>.v<n>' }\n }\n if (!producesEvents.includes(type)) {\n return { type, reason: 'the type is not listed in producesEvents' }\n }\n if (!isPlainObject(payload)) return { type, reason: 'the payload is not a JSON object' }\n }\n return null\n}\n","import type { WebhookDelivery } from './commerce.js'\nimport type { WebhookDeliveryProblem } from './webhook-delivery.js'\n\n/**\n * `<providerName>.bounced.v1`: a message to `recipient` bounced. `hard` is the provider's permanent failure, `soft`\n * its transient one; a classification the adapter cannot map is `soft`, so a doubtful bounce never suppresses.\n */\nexport interface EmailBouncedV1Payload {\n recipient: string\n bounce: 'hard' | 'soft'\n /** The `EmailSendResult.messageId` the bounce answers, when the provider gives one. */\n messageId: string | null\n /** ISO 8601. */\n occurredAt: string\n}\n\n/** `<providerName>.complained.v1`: `recipient` marked a message as spam. */\nexport interface EmailComplainedV1Payload {\n recipient: string\n messageId: string | null\n /** ISO 8601. */\n occurredAt: string\n}\n\nexport type EmailFeedbackKind = 'bounced' | 'complained'\n\n/** The event type an email adapter publishes a bounce or a complaint under. */\nexport function emailFeedbackType(providerName: string, what: EmailFeedbackKind): string {\n return `${providerName}.${what}.v1`\n}\n\nfunction payloadOf(event: unknown): Record<string, unknown> {\n const payload = (event as { payload?: unknown } | null)?.payload\n return typeof payload === 'object' && payload !== null ? (payload as Record<string, unknown>) : {}\n}\n\n/**\n * The first bounce or complaint in a verified delivery from the email adapter `providerName` that the kernel cannot\n * act on, or null when there is none. The route refuses such a delivery whole, before claiming it, so the provider's\n * retry after the adapter is fixed is still received; the email feedback contract suite certifies the same rule.\n */\nexport function emailFeedbackProblem(\n providerName: string,\n delivery: WebhookDelivery,\n): WebhookDeliveryProblem | null {\n const bounced = emailFeedbackType(providerName, 'bounced')\n const complained = emailFeedbackType(providerName, 'complained')\n if (!Array.isArray(delivery.events)) return null\n for (const event of delivery.events as unknown[]) {\n const type = (event as { type?: unknown } | null)?.type\n if (type !== bounced && type !== complained) continue\n const payload = payloadOf(event)\n const { recipient, messageId, occurredAt } = payload\n if (typeof recipient !== 'string' || recipient.trim() === '') {\n return { type, reason: 'the recipient is missing or empty' }\n }\n if (type === bounced && payload['bounce'] !== 'hard' && payload['bounce'] !== 'soft') {\n return { type, reason: \"the bounce is neither 'hard' nor 'soft'\" }\n }\n if (messageId !== null && typeof messageId !== 'string') {\n return { type, reason: 'the messageId is neither a string nor null' }\n }\n if (typeof occurredAt !== 'string' || Number.isNaN(Date.parse(occurredAt))) {\n return { type, reason: 'occurredAt is not a readable date' }\n }\n }\n return null\n}\n","import type { WebhookDelivery } from './commerce.js'\nimport type { WebhookDeliveryProblem } from './webhook-delivery.js'\n\n/** The statuses a shipping adapter maps its carrier's onto; one it cannot map is `unknown`. */\nexport const SHIPPING_TRACKING_STATUSES = [\n 'pre_transit',\n 'in_transit',\n 'out_for_delivery',\n 'delivered',\n 'exception',\n 'returned',\n 'unknown',\n] as const\n\nexport type ShippingTrackingStatus = (typeof SHIPPING_TRACKING_STATUSES)[number]\n\n/** `<providerName>.tracking.v1`: the carrier reported `status` for the parcel `trackingNumber`. */\nexport interface ShippingTrackingV1Payload {\n trackingNumber: string\n /** The carrier's code as the adapter's provider gives it, which need not match the merchant's own. */\n carrier: string | null\n status: ShippingTrackingStatus\n /** ISO 8601. */\n occurredAt: string\n}\n\n/** The event type a shipping adapter publishes a tracking update under. */\nexport function shippingTrackingType(providerName: string): string {\n return `${providerName}.tracking.v1`\n}\n\n/** A tracking number as stored and looked up: no whitespace, upper case, so a carrier's spelling finds the merchant's. */\nexport function normaliseTrackingNumber(value: string): string {\n return value.replace(/\\s+/g, '').toUpperCase()\n}\n\nfunction payloadOf(event: unknown): Record<string, unknown> {\n const payload = (event as { payload?: unknown } | null)?.payload\n return typeof payload === 'object' && payload !== null ? (payload as Record<string, unknown>) : {}\n}\n\n/**\n * The first tracking update in a verified delivery from the shipping adapter `providerName` that the kernel cannot act\n * on, or null when there is none. The route refuses such a delivery whole, before claiming it, so the provider's retry\n * after the adapter is fixed is still received; the shipping tracking contract suite certifies the same rule.\n */\nexport function shippingTrackingProblem(\n providerName: string,\n delivery: WebhookDelivery,\n): WebhookDeliveryProblem | null {\n const tracking = shippingTrackingType(providerName)\n if (!Array.isArray(delivery.events)) return null\n for (const event of delivery.events as unknown[]) {\n const type = (event as { type?: unknown } | null)?.type\n if (type !== tracking) continue\n const { trackingNumber, carrier, status, occurredAt } = payloadOf(event)\n if (typeof trackingNumber !== 'string' || normaliseTrackingNumber(trackingNumber) === '') {\n return { type, reason: 'the trackingNumber is missing or empty' }\n }\n if (carrier !== null && typeof carrier !== 'string') {\n return { type, reason: 'the carrier is neither a string nor null' }\n }\n if (!(SHIPPING_TRACKING_STATUSES as readonly unknown[]).includes(status)) {\n return { type, reason: 'the status is not one of the tracking statuses' }\n }\n if (typeof occurredAt !== 'string' || Number.isNaN(Date.parse(occurredAt))) {\n return { type, reason: 'occurredAt is not a readable date' }\n }\n }\n return null\n}\n","// The kernel's transactional email templates, shared by every email adapter (the console one and the SMTP package).\nexport interface RenderedEmail {\n subject: string\n text: string\n html: string\n}\n\nfunction render(template: string, vars: Record<string, unknown>): string {\n return template.replace(/\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g, (_, path: string) => {\n const parts = path.split('.')\n let cur: unknown = vars\n for (const p of parts) {\n if (cur && typeof cur === 'object' && p in (cur as Record<string, unknown>)) {\n cur = (cur as Record<string, unknown>)[p]\n } else {\n return ''\n }\n }\n return cur == null ? '' : String(cur)\n })\n}\n\nconst TEMPLATES: Record<string, { subject: string; body: string }> = {\n 'order-placed': {\n subject: 'Order {{orderNumber}} confirmed',\n // `orderLink` is the whole \"View your order\" paragraph, absent when the store has no URL to link from.\n body: `Hi {{customerName}},\\n\\nThank you for your order {{orderNumber}}. Total: {{totalFormatted}}.\\n\\nWe'll let you know when it ships.{{orderLink}}`,\n },\n 'order-shipped': {\n subject: 'Order {{orderNumber}} is on its way',\n // `trackingLink` and `orderLink` are whole paragraphs, absent for an unknown carrier or a store with no URL.\n body: `Hi {{customerName}},\\n\\nYour order {{orderNumber}} has shipped via {{carrier}}.\\nTracking: {{trackingNumber}}{{trackingLink}}\\n\\nIn this parcel:\\n{{itemsList}}{{orderLink}}`,\n },\n welcome: {\n subject: 'Welcome to {{storeName}}',\n body: `Welcome {{customerName}}! Your account is ready.`,\n },\n 'cart-abandoned': {\n subject: \"Don't forget your cart at {{storeName}}\",\n body: `Hi,\\n\\nYou left items in your cart. Pick up where you left off: {{resumeUrl}}`,\n },\n // Each link on a line of its own, so a mail client never runs the next word into it.\n 'alert-confirm': {\n subject: 'Confirm your alert for {{productName}}',\n body: `Hi,\\n\\nYou asked {{storeName}} to email you when {{productName}} is back in stock. Confirm by following this link:\\n\\n{{confirmUrl}}\\n\\nIf you did not ask, ignore this email and you will hear nothing more.\\n\\nStop this alert:\\n{{unsubscribeUrl}}`,\n },\n 'alert-back-in-stock': {\n subject: '{{productName}} is back in stock',\n body: `Hi,\\n\\n{{productName}} is back in stock at {{storeName}}:\\n\\n{{productUrl}}\\n\\nThis was a one-time alert. Stop it:\\n{{unsubscribeUrl}}`,\n },\n 'alert-confirm-price-drop': {\n subject: 'Confirm your price alert for {{productName}}',\n body: `Hi,\\n\\nYou asked {{storeName}} to email you when the price of {{productName}} drops, each time it falls below the last price we told you. Confirm by following this link:\\n\\n{{confirmUrl}}\\n\\nIf you did not ask, ignore this email and you will hear nothing more.\\n\\nStop this alert:\\n{{unsubscribeUrl}}`,\n },\n 'alert-price-drop': {\n subject: 'Price drop: {{productName}} is now {{priceFormatted}}',\n body: `Hi,\\n\\nThe price of {{productName}} at {{storeName}} has dropped from {{previousPriceFormatted}} to {{priceFormatted}}:\\n\\n{{productUrl}}\\n\\nWe'll email you again if it drops below {{priceFormatted}}. Stop this alert:\\n{{unsubscribeUrl}}`,\n },\n}\n\nexport function renderEmailTemplate(\n template: string,\n vars: Record<string, unknown>,\n): RenderedEmail {\n const t = TEMPLATES[template]\n if (!t) {\n return {\n subject: `[nymbal] ${template}`,\n text: JSON.stringify(vars),\n html: `<pre>${JSON.stringify(vars, null, 2)}</pre>`,\n }\n }\n const subject = render(t.subject, vars)\n const text = render(t.body, vars)\n const html = `<pre>${text.replace(/</g, '&lt;')}</pre>`\n return { subject, text, html }\n}\n","export type StockLevel = 'out' | 'low' | 'in'\n\n/** Below this many units a variant is low on stock. */\nconst LOW_STOCK_BELOW = 5\n\n/** The one stock rule every storefront renders from. */\nexport function stockLevel(stock: number): StockLevel {\n if (stock <= 0) return 'out'\n return stock < LOW_STOCK_BELOW ? 'low' : 'in'\n}\n\nexport const STOCK_LABELS: Record<StockLevel, string> = {\n out: 'Out of Stock',\n low: 'Low Stock',\n in: 'In Stock',\n}\n","/** Who a command ran for. A guest is `guest:anonymous`: a cart's token is a bearer credential and is never an id. */\nexport interface AuditActor {\n kind: 'admin' | 'customer' | 'guest' | 'system' | 'import'\n id: string\n}\n\n/** `applied`: the command ran and committed. `refused`: its service refused it. `replayed`: a keyed retry answered. */\nexport type AuditOutcome = 'applied' | 'refused' | 'replayed'\n\n/** A record a command touched, by id only. */\nexport interface AuditSubject {\n type: string\n id: string\n}\n\n/**\n * One audit entry as `GET /api/admin/activity` lists it and the exports print it. A stored response is shown by its\n * status only; its body never leaves the store.\n */\nexport interface AuditEntry {\n id: string\n at: string\n actor: AuditActor\n command: string\n subjects: AuditSubject[]\n correlationId: string | null\n idempotencyKey: string | null\n outcome: AuditOutcome\n responseStatus: number | null\n}\n\n/** The request header an admin write carries to be applied once, and the response header a replay carries. */\nexport const IDEMPOTENCY_KEY_HEADER = 'idempotency-key'\nexport const IDEMPOTENT_REPLAYED_HEADER = 'idempotent-replayed'\n\n/** 1-128 characters of `[A-Za-z0-9._:-]`. */\nexport function isValidIdempotencyKey(value: string): boolean {\n return /^[A-Za-z0-9._:-]{1,128}$/.test(value)\n}\n","/** Where an agent's write stands: waiting, decided, then run. */\nexport type ProposalStatus = 'pending' | 'approved' | 'rejected' | 'applied' | 'failed'\n\n/** Who decided a proposal: an admin, or a policy the store's config sets. */\nexport type ProposalDecider =\n | { kind: 'admin'; id: string }\n | { kind: 'policy'; rule: 'agents.mcp.writes' | 'agents.autoApprove' }\n\n/** One proposal as the admin reads it. A handle's value (a cart id) is never served: only its argument's name. */\nexport interface ProposalView {\n id: string\n tool: string\n title: string\n command: string\n /** The arguments the agent sent, less its idempotency key and its handles. */\n args: Record<string, unknown>\n handles: string[]\n idempotencyKey: string | null\n status: ProposalStatus\n proposer: { userId: string | null; agent: { name: string; version?: string } }\n correlationId: string\n createdAt: string\n decision?: { by: ProposalDecider; at: string; reason?: string }\n /** What the route answered when the proposal ran. */\n outcome?: { status: number; code?: string; message?: string }\n /** What a pending write would change, read from the store when the proposal is read on its own. */\n preview?: ProposalPreview\n}\n\n/** The commands whose pending proposal, read on its own, carries a preview. */\nexport const PREVIEWED_COMMANDS: readonly string[] = [\n 'product.update',\n 'inventory.adjust',\n 'order.refund',\n 'order.cancel',\n]\n\n/** One value a pending write changes, as it is now beside what the write would make it. */\nexport interface ProposalChange {\n /** What changes, as the admin reads it: \"Name\", \"Price of Large\", \"Stock\", \"Refunded\", \"Status\", \"Media\". */\n label: string\n /** `money`: minor units of `ProposalPreview.currency`; `collection`: item or key counts, with `changed`. */\n kind: 'value' | 'money' | 'collection'\n current: string | number | null\n proposed: string | number | null\n changed?: boolean\n}\n\n/** What a pending write acts on and what it changes there. */\nexport interface ProposalPreview {\n /** What the write acts on, read from the store now; null when the id names nothing. */\n target: { kind: 'product' | 'variant' | 'order'; id: string; label: string } | null\n changes: ProposalChange[]\n currency: string\n}\n\nexport interface ProposalPage {\n items: ProposalView[]\n nextCursor: string | null\n /** Every proposal still waiting for a decision, whatever the page holds. */\n pending: number\n}\n"],"mappings":"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","names":["c","payloadOf"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nymbalhq/types",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Shared TypeScript types and adapter contracts for Nymbal",
5
5
  "license": "MIT",
6
6
  "repository": {