@reblu/site-contracts 1.21.0 → 1.23.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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @reblu/site-contracts
2
2
 
3
+ ## 1.23.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f2ed9e2: Single-step checkout backend (REB-1197, REB-1198): delivery choice, pre-payment quote and terms acceptance.
8
+ - `ShopConfig.delivery` (`ShopCheckoutDeliveryConfig`): shipping cost and free-shipping threshold, which lines count toward it (`freeShippingScope`), max order weight and whether it applies to home delivery only (`maxOrderWeightScope`), the estimated delivery text and the pickup points (`ShopPickupLocation`, with opening hours and ready-in text). Optional: absent on older servers.
9
+ - `ShopConfig.delivery.methods` (`{ homeDelivery, pickup }`): the delivery methods the tenant offers (owner, 2-Nov). Render only these; `pickup` needs the switch and at least one active pickup point. A cart with nothing to ship shows no choice.
10
+ - `ShopQuote.blockingReason` (`ORDER_TOO_HEAVY` or `DELIVERY_UNAVAILABLE`, with a message) explains a `canCheckout: false`.
11
+ - New `ShopErrorCode` `DELIVERY_UNAVAILABLE` (422): a cart that ships, with no delivery method offered. A home delivery choice the tenant switched off answers `SHIPPING_UNAVAILABLE`.
12
+ - `ShopConfig.checkout` (`ShopCheckoutCopy`): the resolved checkout copy (button and option labels, line kind labels, terms checkbox label and link, small print under the button, full-invoice label and help text, tax line on/off) and `termsVersion` (`ShopTermsVersionRef`), the published terms version to accept, or `null`.
13
+ - `ShopCheckoutInput` gains `fulfillment` (`ShopFulfillmentInput`: `SHIPPING` or `PICKUP` with `pickupLocationId`), `acceptsTerms` and `termsVersionId`.
14
+ - New `POST /api/site/shop/quote` (`ShopQuoteInput` → `ShopQuote`): lines with kind and kind label, discount (an unusable code is left out and explained in `discountError`), delivery mode, shipping, tax breakdown, weight, delivery term, final total and the small print with the total filled in. The total is exactly what the checkout charges for the same body.
15
+ - New `ShopErrorCode`s: `PICKUP_UNAVAILABLE`, `PICKUP_LOCATION_INVALID`, `SHIPPING_ADDRESS_REQUIRED`, `ORDER_TOO_HEAVY` (all 422) and, when the tenant published terms, `TERMS_NOT_ACCEPTED` (422) and `TERMS_VERSION_CHANGED` (409).
16
+ - `Legal.termsVersion`, and `GET /api/site/legal/terms/{versionId}` (`LegalTermsVersion`): every published terms version stays readable.
17
+
18
+ ## 1.22.0
19
+
20
+ ### Minor Changes
21
+
22
+ - 01d0e15: Academy catalogue filters by price and instructor (REB-547). `@reblu/site-contracts` exports `AcademyCourseListQuery`, the documented query of `GET /api/site/academy/courses`. `@reblu/site-client` adds `academy.instructors()` (instructors of published courses, for the filter), and `AcademyCourseListParams` is now that query type, so the SDK cannot drift from the API. `@reblu/site-blocks` adds `CourseCatalogFilters`, a drop-in instructor and price filter (bands in integer cents plus «Gratis»), RSC-safe like `ShopFilters`.
23
+ - d1db04e: Tenant-configurable interest question on the contact form (REB-759).
24
+ - `Config.contactInterest`: `{ label, options }` or `null`, the tenant's own question and options from nexus. `null` means the field must not render; there is no default catalogue.
25
+ - `ContactFormInput.interests`: the chosen option labels (up to 20). The server keeps only labels the tenant offers.
26
+ - New drop-in `ContactInterestField` in `@reblu/site-blocks`: a controlled checkbox group that renders nothing when the tenant has no question. Slots: `contact-interest`, `contact-interest-option`.
27
+
28
+ - 5ca48ee: Discount code usage limits are enforced before payment (REB-912).
29
+ - New `ShopErrorCode` `DISCOUNT_LIMIT_REACHED` (400): checkout refuses a discount code that has used up its total uses (slots held by open payment sessions count), before any payment session is created. The buyer can continue without the code.
30
+ - `DEFAULT_SHOP_ERROR_MESSAGES.DISCOUNT_LIMIT_REACHED`: «Este código ya ha alcanzado su límite de usos».
31
+ - A per-customer limit still answers `DISCOUNT_INVALID` with a neutral message.
32
+
3
33
  ## 1.21.0
4
34
 
5
35
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAkFO,IAAM,kBAAA,GAAqB,OAAO,MAAA,CAAO;AAAA,EAC9C,QAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA;AAAA,EACA,oBAAA;AAAA,EACA;AACF,CAAU","file":"index.cjs","sourcesContent":["/**\n * `@reblu/site-contracts` — public, dependency-free TypeScript types for the\n * Reblu SITE API (REB-414 / design doc §2).\n *\n * These types are the SDK-facing view of every `/api/site/*` response. They are\n * re-derived (`z.infer`) from the hand-authored response schemas that live in\n * base-api (`apps/base-api/src/lib/contracts/*.contract.ts`) and imported here\n * through the build-time-only `@reblu/base-api-contracts` type alias.\n *\n * Guarantees (enforced by the leakage guard tests):\n * - Everything below is `export type` — the import is erased at build time, so\n * NO Zod and NO Prisma reaches a consumer's runtime or `node_modules` graph.\n * The one exception is `DATA_REQUEST_TYPES` (REB-1005): a plain, import-free\n * array literal, the package's only runtime value.\n * - The published `.d.ts` is flattened and self-contained: the alias and\n * `z.infer` do not survive into the emitted declarations.\n * - Never re-exports an internal / Prisma-generated type — only the public\n * output DTOs base-api explicitly publishes.\n *\n * When base-api adds a new public contract type, add it to the matching group\n * below. The explicit list (over `export type *`) keeps the public surface a\n * reviewed, intentional decision and avoids leaking internal `*Schema` names.\n */\n\n// Envelope / pagination\nexport type { PaginationMeta } from '@reblu/base-api-contracts'\n\n// Analytics + shared contact-form mode\nexport type { Analytics, ContactFormMode } from '@reblu/base-api-contracts'\n\n// REB-845 — the tenant-editable cookie banner copy shape, standalone (not just\n// nested in `Analytics`), so `@reblu/site-next`'s `cookieBannerToConsentTexts`\n// can type its input against the real contract instead of a hand-rolled copy.\nexport type { CookieBanner } from '@reblu/base-api-contracts'\n\n// Site config\nexport type {\n Config,\n ConfigAddress,\n ConfigFiscal,\n ConfigFiscalRegistry,\n ConfigSocial,\n ConfigTaxIdKind,\n // REB-826 — typed opening hours.\n ConfigOpeningHours,\n ConfigOpeningHoursRange,\n ConfigOpeningHoursDay,\n} from '@reblu/base-api-contracts'\n\n// REB-871 — the resolved `seo` block on every detail response (pages, blog\n// posts, shop products, academy courses). `seo.title` is the FINAL page title.\nexport type { Seo } from '@reblu/base-api-contracts'\n\n// Contact form\nexport type { ContactFormSubmission, ContactFormInput } from '@reblu/base-api-contracts'\n// REB-1078 — the raw lead-origin touches a SITE sends in `metadata.attribution`\n// (contact form and `/api/site/leads`); base-api classifies the channel.\nexport type { LeadAttribution, LeadTouch } from '@reblu/base-api-contracts'\n\n// Leads\nexport type { LeadInput, LeadSubmission } from '@reblu/base-api-contracts'\n\n// GDPR data-subject requests (REB-77 endpoint, REB-980 SDK) — the public\n// `/derechos-rgpd` form body and its 72h acknowledgement.\nexport type {\n DataRequestInput,\n DataRequestSubmission,\n DataRequestType,\n} from '@reblu/base-api-contracts'\nimport type { DataRequestType } from '@reblu/base-api-contracts'\n\n/**\n * REB-1005 — every GDPR right a data subject can exercise, in base-api's order.\n * The ONE runtime value this package ships: server code (a SITE's Route\n * Handler, `@reblu/site-next`'s `createDataRequestRoute`) needs the list too,\n * and `@reblu/site-blocks` re-exports it as before.\n *\n * Single source: base-api's `dataRequestTypeSchema`. `satisfies` rejects a\n * member base-api does not know, `MissingDataRequestType` below rejects one it\n * knows and this list lacks, and `tests/unit/data-request-types.test.ts` pins\n * the order. Plain array literal, no import — the runtime stays dependency-free.\n */\nexport const DATA_REQUEST_TYPES = Object.freeze([\n 'ACCESS',\n 'RECTIFICATION',\n 'ERASURE',\n 'OBJECTION',\n 'PORTABILITY',\n 'RESTRICTION',\n 'AUTOMATED_DECISION',\n 'INFORMATION',\n] as const) satisfies readonly DataRequestType[]\n\n/** Compile error when base-api adds a right this list does not carry. */\ntype AssertNoneMissing<T extends never> = T\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ntype MissingDataRequestType = AssertNoneMissing<\n Exclude<DataRequestType, (typeof DATA_REQUEST_TYPES)[number]>\n>\n\n// Blog\nexport type {\n BlogAuthor,\n BlogAuthorDetail,\n BlogCategory,\n BlogCategoryWithCount,\n BlogTag,\n BlogPostListItem,\n BlogPostDetail,\n BlogPostPreview,\n BlogSlug,\n} from '@reblu/base-api-contracts'\n\n// Pages\nexport type {\n // The flexible pageType (service|custom|treatments|specialties|team|generic)\n // — REB-63: this is what `pages.list`'s `type` filter actually validates\n // against server-side (`pagesListParamsSchema`), not `LegacyPageType`.\n PageType,\n // Legacy DB page type (SERVICE|CUSTOM) — the `type`/`category` fields still\n // carried on each `PageListItem`/`PageDetail` for backward compat during the\n // REB-75 dual-read window. NOT the `pages.list` filter's value space anymore.\n LegacyPageType,\n PageContentMode,\n SectionLayout,\n PageCategoryRef,\n PageListItem,\n PageSectionImage,\n PageSection,\n PageDetail,\n PageSlug,\n // REB-63: `pages.list`'s sort param — same reasoning as AcademyCourseSort\n // below (a typo is a compile error instead of a silent no-op fallback).\n PagesListSort,\n // REB-869 — `pages.list`'s query, incl. `featured` (with `category`: that\n // category's featured pages, max 3, in their stored order).\n PagesListQuery,\n // REB-75 D-D — the typed block registry on `PageDetail.blocks[]`.\n BlockType,\n Block,\n // REB-849 — each block mirrors its parent section's heading/backgroundColor/\n // cta; REB-851 phase 1 — doctor_embed/specialty_embed blocks additionally\n // carry `resolved` (present only on those two block types).\n PageBlock,\n BlockCta,\n ResolvedEmbed,\n // REB-867 — slugs are unique per category: the embed reference shape\n // (`pageId`/`categorySlug`) and the 409 body of an ambiguous `GET /pages/[slug]`.\n EmbedReferenceData,\n PageAmbiguousSlugError,\n} from '@reblu/base-api-contracts'\n\n// Professional profile (REB-63) — pageType='team' only. Sector-agnostic\n// structured CV, same platform-shape/tenant-content split as CustomFieldDefinition.\n// REB-1099: `ProfessionalField` — a tenant-defined field (e.g. a registration\n// number) on the card (`ProfessionalSummary.fields`) and profile.\nexport type {\n ProfessionalField,\n ProfessionalSummary,\n ProfessionalProfile,\n ProfessionalCv,\n ProfessionalCvEntry,\n ProfessionalLanguage,\n ProfessionalLanguageLevel,\n ProfessionalSocialLink,\n ProfessionalSocialPlatform,\n} from '@reblu/base-api-contracts'\n\n// Custom-field registry (REB-475 / REB-75) — entity-agnostic, shared with pages/team\nexport type {\n CustomFieldValueType,\n CustomFieldEntityType,\n CustomFieldOption,\n CustomFieldDefinition,\n} from '@reblu/base-api-contracts'\n\n// Shop\nexport type {\n TaxType,\n ProductType,\n ShopConfig,\n // Store-wide product trust items (REB-858)\n ShopProductTrustIcon,\n ShopProductTrustItem,\n ShopTaxRateRef,\n ShopProductImage,\n ShopProductVideo,\n ShopProductCategoryRef,\n ShopInventoryRef,\n ShopCategory,\n ShopProductListItem,\n ShopProductVariant,\n ShopBundleComponent,\n ShopRelatedProduct,\n ShopProductDetail,\n // Taxonomy + attributes + facets (REB-474/475)\n ShopTaxonomyTermRef,\n ShopProductAttributes,\n // Per-axis selection mode (REB-809/REB-801)\n ShopTaxonomySelectionMode,\n ShopSort,\n ShopProductListParams,\n ShopFacets,\n // Write inputs (REB-438)\n ShopCartItemInput,\n ShopAddressInput,\n ShopBillingInput,\n ShopCheckoutInput,\n ShopValidateCartInput,\n ShopApplyDiscountInput,\n // Write responses (REB-438)\n ShopCheckoutSession,\n ShopCartValidationError,\n ShopCartValidationItem,\n ShopCartValidation,\n ShopDiscountApplication,\n // Automatic staff discount, additive on the write responses (REB-1131)\n ShopStaffAutoDiscount,\n // Order lookup (REB-829)\n ShopOrderItem,\n ShopOrder,\n // The order's invoice reference (REB-73)\n ShopOrderInvoice,\n // Stable, machine-readable write-endpoint error codes (REB-836)\n ShopErrorCode,\n} from '@reblu/base-api-contracts'\n\n// REB-834 — `POST /api/site/shop/check-email` is deprecated: it was an\n// account-enumeration oracle and now always answers `{ exists: false }`.\n// Kept (a minor may only deprecate); removed in the next major.\nexport type {\n /**\n * @deprecated REB-834 — `POST /api/site/shop/check-email` no longer reveals\n * whether an account exists. Do not call it; removed in the next major.\n */\n ShopCheckEmailInput,\n /**\n * @deprecated REB-834 — `exists` is always `false`. Never branch UI on it:\n * the checkout always offers «Iniciar sesión o continuar como invitado» and\n * the server links the order to an existing account after payment. Removed\n * in the next major.\n */\n ShopEmailCheck,\n} from '@reblu/base-api-contracts'\n\n// Academy (REB-507) — public course catalogue. `AcademyCourseSort` is the query\n// contract for the `sort` param, so the SDK can type it as a union instead of a\n// bare string (an unknown key is silently ignored server-side).\nexport type {\n AcademyCourseSort,\n AcademyCategoryPublic,\n AcademyLessonPublic,\n AcademyModulePublic,\n AcademyInstructorPublic,\n AcademyCourseSummary,\n AcademyCourseDetail,\n // REB-83 — tenant-configurable validation fields (drives the enrollment CTA form).\n AcademyValidationFieldPublic,\n // REB-1135 — a free-preview lesson, viewable by the public\n // (`GET /api/site/academy/courses/{slug}/lessons/{lessonSlug}`).\n AcademyLessonPreview,\n AcademyLessonPreviewVideo,\n} from '@reblu/base-api-contracts'\n\n// Legal\nexport type { Legal } from '@reblu/base-api-contracts'\n\n// CTAs\nexport type { Cta } from '@reblu/base-api-contracts'\n\n// Trust badges\nexport type { TrustBadge, TrustBadgeType } from '@reblu/base-api-contracts'\n\n// SITE capability manifest (`/.well-known/reblu-site.json`) — REB-777.\n// Direction note (see `site-manifest.contract.ts` in base-api): this is the\n// one contract group a SITE produces and base-api consumes, the opposite of\n// every group above. The TYPE still flows through this same alias so the\n// SDK and nexus never describe two different shapes for it; the real Zod\n// validation stays in base-api only — this package never gains a Zod\n// runtime dependency.\nexport type {\n SiteManifest,\n SiteManifestSurfaceValue,\n SiteCapabilitySurfaceKey,\n} from '@reblu/base-api-contracts'\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAkFO,IAAM,kBAAA,GAAqB,OAAO,MAAA,CAAO;AAAA,EAC9C,QAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA;AAAA,EACA,oBAAA;AAAA,EACA;AACF,CAAU","file":"index.cjs","sourcesContent":["/**\n * `@reblu/site-contracts` — public, dependency-free TypeScript types for the\n * Reblu SITE API (REB-414 / design doc §2).\n *\n * These types are the SDK-facing view of every `/api/site/*` response. They are\n * re-derived (`z.infer`) from the hand-authored response schemas that live in\n * base-api (`apps/base-api/src/lib/contracts/*.contract.ts`) and imported here\n * through the build-time-only `@reblu/base-api-contracts` type alias.\n *\n * Guarantees (enforced by the leakage guard tests):\n * - Everything below is `export type` — the import is erased at build time, so\n * NO Zod and NO Prisma reaches a consumer's runtime or `node_modules` graph.\n * The one exception is `DATA_REQUEST_TYPES` (REB-1005): a plain, import-free\n * array literal, the package's only runtime value.\n * - The published `.d.ts` is flattened and self-contained: the alias and\n * `z.infer` do not survive into the emitted declarations.\n * - Never re-exports an internal / Prisma-generated type — only the public\n * output DTOs base-api explicitly publishes.\n *\n * When base-api adds a new public contract type, add it to the matching group\n * below. The explicit list (over `export type *`) keeps the public surface a\n * reviewed, intentional decision and avoids leaking internal `*Schema` names.\n */\n\n// Envelope / pagination\nexport type { PaginationMeta } from '@reblu/base-api-contracts'\n\n// Analytics + shared contact-form mode\nexport type { Analytics, ContactFormMode } from '@reblu/base-api-contracts'\n\n// REB-845 — the tenant-editable cookie banner copy shape, standalone (not just\n// nested in `Analytics`), so `@reblu/site-next`'s `cookieBannerToConsentTexts`\n// can type its input against the real contract instead of a hand-rolled copy.\nexport type { CookieBanner } from '@reblu/base-api-contracts'\n\n// Site config\nexport type {\n Config,\n ConfigAddress,\n ConfigFiscal,\n ConfigFiscalRegistry,\n ConfigSocial,\n ConfigTaxIdKind,\n // REB-826 — typed opening hours.\n ConfigOpeningHours,\n ConfigOpeningHoursRange,\n ConfigOpeningHoursDay,\n} from '@reblu/base-api-contracts'\n\n// REB-871 — the resolved `seo` block on every detail response (pages, blog\n// posts, shop products, academy courses). `seo.title` is the FINAL page title.\nexport type { Seo } from '@reblu/base-api-contracts'\n\n// Contact form\nexport type { ContactFormSubmission, ContactFormInput } from '@reblu/base-api-contracts'\n// REB-1078 — the raw lead-origin touches a SITE sends in `metadata.attribution`\n// (contact form and `/api/site/leads`); base-api classifies the channel.\nexport type { LeadAttribution, LeadTouch } from '@reblu/base-api-contracts'\n\n// Leads\nexport type { LeadInput, LeadSubmission } from '@reblu/base-api-contracts'\n\n// GDPR data-subject requests (REB-77 endpoint, REB-980 SDK) — the public\n// `/derechos-rgpd` form body and its 72h acknowledgement.\nexport type {\n DataRequestInput,\n DataRequestSubmission,\n DataRequestType,\n} from '@reblu/base-api-contracts'\nimport type { DataRequestType } from '@reblu/base-api-contracts'\n\n/**\n * REB-1005 — every GDPR right a data subject can exercise, in base-api's order.\n * The ONE runtime value this package ships: server code (a SITE's Route\n * Handler, `@reblu/site-next`'s `createDataRequestRoute`) needs the list too,\n * and `@reblu/site-blocks` re-exports it as before.\n *\n * Single source: base-api's `dataRequestTypeSchema`. `satisfies` rejects a\n * member base-api does not know, `MissingDataRequestType` below rejects one it\n * knows and this list lacks, and `tests/unit/data-request-types.test.ts` pins\n * the order. Plain array literal, no import — the runtime stays dependency-free.\n */\nexport const DATA_REQUEST_TYPES = Object.freeze([\n 'ACCESS',\n 'RECTIFICATION',\n 'ERASURE',\n 'OBJECTION',\n 'PORTABILITY',\n 'RESTRICTION',\n 'AUTOMATED_DECISION',\n 'INFORMATION',\n] as const) satisfies readonly DataRequestType[]\n\n/** Compile error when base-api adds a right this list does not carry. */\ntype AssertNoneMissing<T extends never> = T\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ntype MissingDataRequestType = AssertNoneMissing<\n Exclude<DataRequestType, (typeof DATA_REQUEST_TYPES)[number]>\n>\n\n// Blog\nexport type {\n BlogAuthor,\n BlogAuthorDetail,\n BlogCategory,\n BlogCategoryWithCount,\n BlogTag,\n BlogPostListItem,\n BlogPostDetail,\n BlogPostPreview,\n BlogSlug,\n} from '@reblu/base-api-contracts'\n\n// Pages\nexport type {\n // The flexible pageType (service|custom|treatments|specialties|team|generic)\n // — REB-63: this is what `pages.list`'s `type` filter actually validates\n // against server-side (`pagesListParamsSchema`), not `LegacyPageType`.\n PageType,\n // Legacy DB page type (SERVICE|CUSTOM) — the `type`/`category` fields still\n // carried on each `PageListItem`/`PageDetail` for backward compat during the\n // REB-75 dual-read window. NOT the `pages.list` filter's value space anymore.\n LegacyPageType,\n PageContentMode,\n SectionLayout,\n PageCategoryRef,\n PageListItem,\n PageSectionImage,\n PageSection,\n PageDetail,\n PageSlug,\n // REB-63: `pages.list`'s sort param — same reasoning as AcademyCourseSort\n // below (a typo is a compile error instead of a silent no-op fallback).\n PagesListSort,\n // REB-869 — `pages.list`'s query, incl. `featured` (with `category`: that\n // category's featured pages, max 3, in their stored order).\n PagesListQuery,\n // REB-75 D-D — the typed block registry on `PageDetail.blocks[]`.\n BlockType,\n Block,\n // REB-849 — each block mirrors its parent section's heading/backgroundColor/\n // cta; REB-851 phase 1 — doctor_embed/specialty_embed blocks additionally\n // carry `resolved` (present only on those two block types).\n PageBlock,\n BlockCta,\n ResolvedEmbed,\n // REB-867 — slugs are unique per category: the embed reference shape\n // (`pageId`/`categorySlug`) and the 409 body of an ambiguous `GET /pages/[slug]`.\n EmbedReferenceData,\n PageAmbiguousSlugError,\n} from '@reblu/base-api-contracts'\n\n// Professional profile (REB-63) — pageType='team' only. Sector-agnostic\n// structured CV, same platform-shape/tenant-content split as CustomFieldDefinition.\n// REB-1099: `ProfessionalField` — a tenant-defined field (e.g. a registration\n// number) on the card (`ProfessionalSummary.fields`) and profile.\nexport type {\n ProfessionalField,\n ProfessionalSummary,\n ProfessionalProfile,\n ProfessionalCv,\n ProfessionalCvEntry,\n ProfessionalLanguage,\n ProfessionalLanguageLevel,\n ProfessionalSocialLink,\n ProfessionalSocialPlatform,\n} from '@reblu/base-api-contracts'\n\n// Custom-field registry (REB-475 / REB-75) — entity-agnostic, shared with pages/team\nexport type {\n CustomFieldValueType,\n CustomFieldEntityType,\n CustomFieldOption,\n CustomFieldDefinition,\n} from '@reblu/base-api-contracts'\n\n// Shop\nexport type {\n TaxType,\n ProductType,\n ShopConfig,\n // Store-wide product trust items (REB-858)\n ShopProductTrustIcon,\n ShopProductTrustItem,\n ShopTaxRateRef,\n ShopProductImage,\n ShopProductVideo,\n ShopProductCategoryRef,\n ShopInventoryRef,\n ShopCategory,\n ShopProductListItem,\n ShopProductVariant,\n ShopBundleComponent,\n ShopRelatedProduct,\n ShopProductDetail,\n // Taxonomy + attributes + facets (REB-474/475)\n ShopTaxonomyTermRef,\n ShopProductAttributes,\n // Per-axis selection mode (REB-809/REB-801)\n ShopTaxonomySelectionMode,\n ShopSort,\n ShopProductListParams,\n ShopFacets,\n // Write inputs (REB-438)\n ShopCartItemInput,\n ShopAddressInput,\n ShopBillingInput,\n ShopCheckoutInput,\n ShopValidateCartInput,\n ShopApplyDiscountInput,\n // Write responses (REB-438)\n ShopCheckoutSession,\n ShopCartValidationError,\n ShopCartValidationItem,\n ShopCartValidation,\n ShopDiscountApplication,\n // Automatic staff discount, additive on the write responses (REB-1131)\n ShopStaffAutoDiscount,\n // Order lookup (REB-829)\n ShopOrderItem,\n ShopOrder,\n // The order's invoice reference (REB-73)\n ShopOrderInvoice,\n // Stable, machine-readable write-endpoint error codes (REB-836)\n ShopErrorCode,\n // Single-step checkout (REB-1197 / REB-1198): delivery rules, pickup\n // points, resolved checkout copy, the terms version to accept, the\n // delivery choice and the pre-payment quote.\n ShopPickupLocation,\n ShopCheckoutDeliveryConfig,\n ShopCheckoutCopy,\n ShopTermsVersionRef,\n ShopFulfillmentInput,\n ShopQuoteInput,\n ShopQuoteLine,\n ShopQuote,\n} from '@reblu/base-api-contracts'\n\n// REB-834 — `POST /api/site/shop/check-email` is deprecated: it was an\n// account-enumeration oracle and now always answers `{ exists: false }`.\n// Kept (a minor may only deprecate); removed in the next major.\nexport type {\n /**\n * @deprecated REB-834 — `POST /api/site/shop/check-email` no longer reveals\n * whether an account exists. Do not call it; removed in the next major.\n */\n ShopCheckEmailInput,\n /**\n * @deprecated REB-834 — `exists` is always `false`. Never branch UI on it:\n * the checkout always offers «Iniciar sesión o continuar como invitado» and\n * the server links the order to an existing account after payment. Removed\n * in the next major.\n */\n ShopEmailCheck,\n} from '@reblu/base-api-contracts'\n\n// Academy (REB-507) — public course catalogue. `AcademyCourseSort` is the query\n// contract for the `sort` param, so the SDK can type it as a union instead of a\n// bare string (an unknown key is silently ignored server-side).\nexport type {\n AcademyCourseSort,\n // REB-547 — the documented catalogue query: q, sort, category, instructor\n // (a user id from `GET /api/site/academy/instructors`), free and the price\n // range in integer cents.\n AcademyCourseListQuery,\n AcademyCategoryPublic,\n AcademyLessonPublic,\n AcademyModulePublic,\n AcademyInstructorPublic,\n AcademyCourseSummary,\n AcademyCourseDetail,\n // REB-83 — tenant-configurable validation fields (drives the enrollment CTA form).\n AcademyValidationFieldPublic,\n // REB-1135 — a free-preview lesson, viewable by the public\n // (`GET /api/site/academy/courses/{slug}/lessons/{lessonSlug}`).\n AcademyLessonPreview,\n AcademyLessonPreviewVideo,\n} from '@reblu/base-api-contracts'\n\n// Legal\nexport type { Legal, LegalTermsVersion } from '@reblu/base-api-contracts'\n\n// CTAs\nexport type { Cta } from '@reblu/base-api-contracts'\n\n// Trust badges\nexport type { TrustBadge, TrustBadgeType } from '@reblu/base-api-contracts'\n\n// SITE capability manifest (`/.well-known/reblu-site.json`) — REB-777.\n// Direction note (see `site-manifest.contract.ts` in base-api): this is the\n// one contract group a SITE produces and base-api consumes, the opposite of\n// every group above. The TYPE still flows through this same alias so the\n// SDK and nexus never describe two different shapes for it; the real Zod\n// validation stays in base-api only — this package never gains a Zod\n// runtime dependency.\nexport type {\n SiteManifest,\n SiteManifestSurfaceValue,\n SiteCapabilitySurfaceKey,\n} from '@reblu/base-api-contracts'\n"]}
package/dist/index.d.cts CHANGED
@@ -8,6 +8,8 @@ export type AcademyCategoryPublic = { id: string; name: string; slug: s
8
8
 
9
9
  export type AcademyCourseDetail = { id: string; slug: string; title: string; shortDescription: string | null; coverUrl: string | null; priceCents: number; accessMode: "IMMEDIATE" | "DRIP" | "COHORT"; category: { id: string; name: string; slug: string; sortOrder: number; } | null; description: string | null; modules: { id: string; title: string; description: string | null; lessons: { id: string; slug: string; title: string; isFreePreview: boolean; durationSeconds: number | null; hasVideo: boolean; }[]; }[]; instructors: { id: string; firstName: string | null; lastName: string | null; avatarUrl: string | null; }[]; salesEnabled: boolean; requiresValidation: boolean; validationFieldLabel: string | null; validationFields: { key: string; label: string; required: boolean; maxLength?: number | undefined; }[]; metaTitle?: string | null | undefined; seo?: { title: string; } | undefined;}
10
10
 
11
+ export type AcademyCourseListQuery = { page?: number | undefined; limit?: number | undefined; q?: string | undefined; sort?: "price_asc" | "price_desc" | "newest" | undefined; category?: string | undefined; instructor?: string | undefined; free?: boolean | undefined; priceMin?: number | undefined; priceMax?: number | undefined;}
12
+
11
13
  export type AcademyCourseSort = "price_asc" | "price_desc" | "newest"
12
14
 
13
15
  export type AcademyCourseSummary = { id: string; slug: string; title: string; shortDescription: string | null; coverUrl: string | null; priceCents: number; accessMode: "IMMEDIATE" | "DRIP" | "COHORT"; category: { id: string; name: string; slug: string; sortOrder: number; } | null;}
@@ -50,7 +52,7 @@ export type BlogSlug = { slug: string; updatedAt: string;}
50
52
 
51
53
  export type BlogTag = { id: string; name: string; slug: string; color: string;}
52
54
 
53
- export type Config = { siteName: string; siteDescription: string | null; language: string | null; contactEmail: string | null; keywords: string[]; businessType: string | null; isSetupComplete: boolean; logoLightExpandedUrl: string | null; logoDarkExpandedUrl: string | null; logoLightSmallUrl: string | null; logoDarkSmallUrl: string | null; ogImageUrl: string | null; owner: { name: string | null; email: string | null; jobTitle: string | null; }; business: { name: string | null; }; address: { street: string | null; city: string | null; region: string | null; postalCode: string | null; country: string | null; telephone: string | null; lat: number | null; lng: number | null; } | null; fiscal: { taxId: string | null; taxIdKind: "NIF" | "CIF" | null; legalName: string | null; registry: { name: string; number: string; } | null; } | null; social: { linkedin: string | null; twitter: string | null; facebook: string | null; instagram: string | null; youtube: string | null; } | null; metadata: unknown; openingHours: { ranges: { days: ("MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN")[]; opens: string; closes: string; }[]; note?: string | undefined; } | null; contactFormMode: "NOT_CONFIGURED" | "DISABLED" | "DB_ONLY" | "WEBHOOK" | "RESEND"; features: Record<string, unknown>; seoTitleTemplate?: string | undefined; seoTitle?: string | null | undefined; seoDescription?: string | null | undefined; canonicalUrl?: string | null | undefined;}
55
+ export type Config = { siteName: string; siteDescription: string | null; language: string | null; contactEmail: string | null; keywords: string[]; businessType: string | null; isSetupComplete: boolean; logoLightExpandedUrl: string | null; logoDarkExpandedUrl: string | null; logoLightSmallUrl: string | null; logoDarkSmallUrl: string | null; ogImageUrl: string | null; owner: { name: string | null; email: string | null; jobTitle: string | null; }; business: { name: string | null; }; address: { street: string | null; city: string | null; region: string | null; postalCode: string | null; country: string | null; telephone: string | null; lat: number | null; lng: number | null; } | null; fiscal: { taxId: string | null; taxIdKind: "NIF" | "CIF" | null; legalName: string | null; registry: { name: string; number: string; } | null; } | null; social: { linkedin: string | null; twitter: string | null; facebook: string | null; instagram: string | null; youtube: string | null; } | null; metadata: unknown; openingHours: { ranges: { days: ("MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN")[]; opens: string; closes: string; }[]; note?: string | undefined; } | null; contactFormMode: "NOT_CONFIGURED" | "DISABLED" | "DB_ONLY" | "WEBHOOK" | "RESEND"; features: Record<string, unknown>; seoTitleTemplate?: string | undefined; seoTitle?: string | null | undefined; seoDescription?: string | null | undefined; canonicalUrl?: string | null | undefined; contactInterest?: { label: string; options: string[]; } | null | undefined;}
54
56
 
55
57
  export type ConfigAddress = { street: string | null; city: string | null; region: string | null; postalCode: string | null; country: string | null; telephone: string | null; lat: number | null; lng: number | null;}
56
58
 
@@ -68,7 +70,7 @@ export type ConfigSocial = { linkedin: string | null; twitter: string | nu
68
70
 
69
71
  export type ConfigTaxIdKind = "NIF" | "CIF"
70
72
 
71
- export type ContactFormInput = { name: string; email: string; phone: string; message: string; agreement: boolean; captcha: string; metadata: { fromPage: string; timestamp: string; currentUrl: string; leadSource?: { platform: string; } | undefined; attribution?: { first?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; last?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; } | undefined; }; surname?: string | undefined; businessName?: string | undefined;}
73
+ export type ContactFormInput = { name: string; email: string; phone: string; message: string; agreement: boolean; captcha: string; metadata: { fromPage: string; timestamp: string; currentUrl: string; leadSource?: { platform: string; } | undefined; attribution?: { first?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; last?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; } | undefined; }; surname?: string | undefined; businessName?: string | undefined; interests?: string[] | undefined;}
72
74
 
73
75
  export type ContactFormMode = "NOT_CONFIGURED" | "DISABLED" | "DB_ONLY" | "WEBHOOK" | "RESEND"
74
76
 
@@ -104,7 +106,9 @@ export type LeadTouch = { utm?: { source?: string | undefined;
104
106
 
105
107
  export type LegacyPageType = "SERVICE" | "CUSTOM"
106
108
 
107
- export type Legal = { privacyPolicy: string | null; termsAndConditions: string | null; cookiePolicy: string | null;}
109
+ export type Legal = { privacyPolicy: string | null; termsAndConditions: string | null; cookiePolicy: string | null; termsVersion?: { id: string; version: number; publishedAt: string; } | null | undefined;}
110
+
111
+ export type LegalTermsVersion = { id: string; version: number; publishedAt: string; content: string; contentHash: string;}
108
112
 
109
113
  export type PageAmbiguousSlugError = { error: string; code: "ambiguous_slug"; candidates: { categorySlug: string | null; }[];}
110
114
 
@@ -182,11 +186,15 @@ export type ShopCategory = { id: string; name: string; slug: string;
182
186
  */
183
187
  export type ShopCheckEmailInput = { email: string;}
184
188
 
185
- export type ShopCheckoutInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; customerEmail: string; customerName: string; successUrl: string; cancelUrl: string; customerPhone?: string | null | undefined; discountCode?: string | undefined; shippingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; billing?: { billingName: string; billingNif?: string | undefined; billingIdType?: "NIF" | "02" | "03" | "04" | "05" | "06" | undefined; billingIdCountry?: string | undefined; billingEmail?: string | undefined; billingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; } | undefined; wantsFullInvoice?: boolean | undefined;}
189
+ export type ShopCheckoutCopy = { tenantName: string; continueToPaymentLabel: string; shippingOptionLabel: string; pickupOptionLabel: string; emptyCartText: string; lineKindLabels: Record<string, string>; isTaxLineVisible: boolean; termsCheckboxLabel: string; termsLinkText: string; termsLinkUrl: string | null; isTermsLinkNewTab: boolean; payNotice: string; fullInvoiceCheckboxLabel: string; fullInvoiceHelpText: string; termsVersion: { id: string; version: number; publishedAt: string; } | null;}
190
+
191
+ export type ShopCheckoutDeliveryConfig = { methods: { homeDelivery: boolean; pickup: boolean; }; shippingEnabled: boolean; defaultShippingCost: number | null; freeShippingThreshold: number | null; freeShippingScope: "SHIPPABLE_ONLY" | "WHOLE_CART"; maxOrderWeightGrams: number | null; maxOrderWeightScope: "SHIPPING_ONLY" | "ALL_ORDERS"; estimatedDeliveryText: string | null; pickupLocations: { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null; }[];}
192
+
193
+ export type ShopCheckoutInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; customerEmail: string; customerName: string; successUrl: string; cancelUrl: string; customerPhone?: string | null | undefined; discountCode?: string | undefined; shippingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; billing?: { billingName: string; billingNif?: string | undefined; billingIdType?: "NIF" | "02" | "03" | "04" | "05" | "06" | undefined; billingIdCountry?: string | undefined; billingEmail?: string | undefined; billingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; } | undefined; wantsFullInvoice?: boolean | undefined; fulfillment?: { type: "SHIPPING" | "PICKUP"; pickupLocationId?: string | undefined; } | undefined; acceptsTerms?: boolean | undefined; termsVersionId?: string | undefined;}
186
194
 
187
195
  export type ShopCheckoutSession = { url: string; sessionId: string; autoDiscount?: { code: string; label: string; amount: number; } | undefined;}
188
196
 
189
- export type ShopConfig = { shopName: string | null; defaultLowStockThreshold: number; currencyCode: string; stripePublishableKey: string | null; stripeTestPublishableKey: string | null; stripeTestMode: boolean; shippingEnabled: boolean; pickupEnabled: boolean; taxEnabled: boolean; taxIncludedInPrice: boolean; simplifiedInvoiceCapCents?: number | null | undefined; productTrustItems?: { [x: string]: unknown; icon: (string & {}) | "shipping" | "returns" | "secure-payment" | "support" | "gift" | "pickup" | "warranty" | "quality"; text: string; }[] | null | undefined;}
197
+ export type ShopConfig = { shopName: string | null; defaultLowStockThreshold: number; currencyCode: string; stripePublishableKey: string | null; stripeTestPublishableKey: string | null; stripeTestMode: boolean; shippingEnabled: boolean; pickupEnabled: boolean; taxEnabled: boolean; taxIncludedInPrice: boolean; simplifiedInvoiceCapCents?: number | null | undefined; productTrustItems?: { [x: string]: unknown; icon: (string & {}) | "shipping" | "returns" | "secure-payment" | "support" | "gift" | "pickup" | "warranty" | "quality"; text: string; }[] | null | undefined; delivery?: { methods: { homeDelivery: boolean; pickup: boolean; }; shippingEnabled: boolean; defaultShippingCost: number | null; freeShippingThreshold: number | null; freeShippingScope: "SHIPPABLE_ONLY" | "WHOLE_CART"; maxOrderWeightGrams: number | null; maxOrderWeightScope: "SHIPPING_ONLY" | "ALL_ORDERS"; estimatedDeliveryText: string | null; pickupLocations: { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null; }[]; } | undefined; checkout?: { tenantName: string; continueToPaymentLabel: string; shippingOptionLabel: string; pickupOptionLabel: string; emptyCartText: string; lineKindLabels: Record<string, string>; isTaxLineVisible: boolean; termsCheckboxLabel: string; termsLinkText: string; termsLinkUrl: string | null; isTermsLinkNewTab: boolean; payNotice: string; fullInvoiceCheckboxLabel: string; fullInvoiceHelpText: string; termsVersion: { id: string; version: number; publishedAt: string; } | null; } | undefined;}
190
198
 
191
199
  export type ShopDiscountApplication = { valid: boolean; discount: number; type: "PERCENTAGE" | "FIXED" | null; code: string | null; error?: string | undefined; autoDiscount?: { code: string; label: string; amount: number; } | undefined;}
192
200
 
@@ -198,10 +206,12 @@ export type ShopDiscountApplication = { valid: boolean; discount: number;
198
206
  */
199
207
  export type ShopEmailCheck = { exists: boolean;}
200
208
 
201
- export type ShopErrorCode = "STOCK_CHANGED" | "PRICE_CHANGED" | "PRODUCT_UNAVAILABLE" | "DISCOUNT_INVALID" | "SHIPPING_UNAVAILABLE" | "INVALID_INPUT" | "STORE_DISABLED" | "PAYMENT_PROVIDER_ERROR" | "INTERNAL_ERROR" | "BILLING_DATA_REQUIRED" | "INVOICING_NOT_READY" | "FULL_INVOICE_UNAVAILABLE"
209
+ export type ShopErrorCode = "STOCK_CHANGED" | "PRICE_CHANGED" | "PRODUCT_UNAVAILABLE" | "DISCOUNT_INVALID" | "SHIPPING_UNAVAILABLE" | "INVALID_INPUT" | "STORE_DISABLED" | "PAYMENT_PROVIDER_ERROR" | "INTERNAL_ERROR" | "BILLING_DATA_REQUIRED" | "INVOICING_NOT_READY" | "FULL_INVOICE_UNAVAILABLE" | "DISCOUNT_LIMIT_REACHED" | "PICKUP_UNAVAILABLE" | "PICKUP_LOCATION_INVALID" | "SHIPPING_ADDRESS_REQUIRED" | "ORDER_TOO_HEAVY" | "TERMS_NOT_ACCEPTED" | "TERMS_VERSION_CHANGED" | "DELIVERY_UNAVAILABLE"
202
210
 
203
211
  export type ShopFacets = { taxonomies: { key: string; label: string; showAsBadge: boolean; selectionMode: "single" | "multiple"; terms: { slug: string; name: string; count: number; }[]; }[]; attributes: { key: string; label: string; valueType: "boolean" | "text" | "select" | "multiselect"; options: { value: string; label: string; count: number; }[]; }[]; price: { min: number; max: number; }; onSale: { count: number; };}
204
212
 
213
+ export type ShopFulfillmentInput = { type: "SHIPPING" | "PICKUP"; pickupLocationId?: string | undefined;}
214
+
205
215
  export type ShopInventoryRef = { stockQuantity: number; reservedQuantity: number; lowStockThreshold: number | null;}
206
216
 
207
217
  export type ShopOrder = { id: string; orderNumber: string; status: "PENDING" | "CONFIRMED" | "PROCESSING" | "SHIPPED" | "DELIVERED" | "CANCELLED" | "REFUNDED"; paymentStatus: "PENDING" | "REFUNDED" | "PAID" | "FAILED" | "PARTIALLY_REFUNDED"; customerEmail: string; total: number; items: { product: { id: string; name: string; slug: string; }; quantity: number; unitPrice: number; total: number; variant?: { id: string; name: string; } | null | undefined; }[]; invoice?: { number: string; url: string; } | null | undefined;}
@@ -210,6 +220,8 @@ export type ShopOrderInvoice = { number: string; url: string;}
210
220
 
211
221
  export type ShopOrderItem = { product: { id: string; name: string; slug: string; }; quantity: number; unitPrice: number; total: number; variant?: { id: string; name: string; } | null | undefined;}
212
222
 
223
+ export type ShopPickupLocation = { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null;}
224
+
213
225
  export type ShopProductAttributes = unknown
214
226
 
215
227
  export type ShopProductCategoryRef = { id: string; name: string; slug: string;}
@@ -230,6 +242,12 @@ export type ShopProductVariant = { id: string; name: string; sku: strin
230
242
 
231
243
  export type ShopProductVideo = { url: string; title: string | null; isExternal: boolean; thumbnailUrl: string | null; sortOrder: number;}
232
244
 
245
+ export type ShopQuote = { lines: { productId: string; variantId: string | null; name: string; variantName: string | null; imageUrl: string | null; kind: string | null; kindLabel: string | null; quantity: number; unitPrice: number; total: number; isShippable: boolean; unitWeightGrams: number | null; }[]; subtotal: number; discount: { code: string; amount: number; isAutomatic: boolean; label: string | null; } | null; discountError: { code: "DISCOUNT_INVALID" | "DISCOUNT_LIMIT_REACHED"; message: string; } | null; fulfillment: { type: "SHIPPING" | "PICKUP" | null; label: string | null; pickupLocation: { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null; } | null; }; shipping: { cost: number; isFree: boolean; freeShippingThreshold: number | null; freeShippingScope: "SHIPPABLE_ONLY" | "WHOLE_CART"; remainingForFree: number | null; }; tax: { isIncluded: boolean; amount: number; isLineVisible: boolean; breakdown: { label: string; amount: number; }[]; }; weight: { totalGrams: number; maxGrams: number | null; scope: "SHIPPING_ONLY" | "ALL_ORDERS"; exceeded: boolean; }; deliveryEstimateText: string | null; total: number; payNotice: string; canCheckout: boolean; blockingReason: { code: "ORDER_TOO_HEAVY" | "DELIVERY_UNAVAILABLE"; message: string; } | null;}
246
+
247
+ export type ShopQuoteInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; discountCode?: string | undefined; customerEmail?: string | undefined; fulfillment?: { type: "SHIPPING" | "PICKUP"; pickupLocationId?: string | undefined; } | undefined;}
248
+
249
+ export type ShopQuoteLine = { productId: string; variantId: string | null; name: string; variantName: string | null; imageUrl: string | null; kind: string | null; kindLabel: string | null; quantity: number; unitPrice: number; total: number; isShippable: boolean; unitWeightGrams: number | null;}
250
+
233
251
  export type ShopRelatedProduct = { id: string; slug: string; name: string; price: number; comparePrice: number | null; images: { url: string; alt: string | null; isPrimary: boolean; sortOrder: number; }[];}
234
252
 
235
253
  export type ShopSort = "price_asc" | "price_desc" | "newest" | "on_sale"
@@ -242,6 +260,8 @@ export type ShopTaxonomySelectionMode = "single" | "multiple"
242
260
 
243
261
  export type ShopTaxonomyTermRef = { taxonomyKey: string; taxonomyLabel: string; showAsBadge: boolean; slug: string; name: string; imageUrl: string | null;}
244
262
 
263
+ export type ShopTermsVersionRef = { id: string; version: number; publishedAt: string;}
264
+
245
265
  export type ShopValidateCartInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[];}
246
266
 
247
267
  export type SiteCapabilitySurfaceKey = "academy.enrollment" | "academy.catalogue" | "auth.login" | "consent.banner" | "legal.cookies"
package/dist/index.d.ts CHANGED
@@ -8,6 +8,8 @@ export type AcademyCategoryPublic = { id: string; name: string; slug: s
8
8
 
9
9
  export type AcademyCourseDetail = { id: string; slug: string; title: string; shortDescription: string | null; coverUrl: string | null; priceCents: number; accessMode: "IMMEDIATE" | "DRIP" | "COHORT"; category: { id: string; name: string; slug: string; sortOrder: number; } | null; description: string | null; modules: { id: string; title: string; description: string | null; lessons: { id: string; slug: string; title: string; isFreePreview: boolean; durationSeconds: number | null; hasVideo: boolean; }[]; }[]; instructors: { id: string; firstName: string | null; lastName: string | null; avatarUrl: string | null; }[]; salesEnabled: boolean; requiresValidation: boolean; validationFieldLabel: string | null; validationFields: { key: string; label: string; required: boolean; maxLength?: number | undefined; }[]; metaTitle?: string | null | undefined; seo?: { title: string; } | undefined;}
10
10
 
11
+ export type AcademyCourseListQuery = { page?: number | undefined; limit?: number | undefined; q?: string | undefined; sort?: "price_asc" | "price_desc" | "newest" | undefined; category?: string | undefined; instructor?: string | undefined; free?: boolean | undefined; priceMin?: number | undefined; priceMax?: number | undefined;}
12
+
11
13
  export type AcademyCourseSort = "price_asc" | "price_desc" | "newest"
12
14
 
13
15
  export type AcademyCourseSummary = { id: string; slug: string; title: string; shortDescription: string | null; coverUrl: string | null; priceCents: number; accessMode: "IMMEDIATE" | "DRIP" | "COHORT"; category: { id: string; name: string; slug: string; sortOrder: number; } | null;}
@@ -50,7 +52,7 @@ export type BlogSlug = { slug: string; updatedAt: string;}
50
52
 
51
53
  export type BlogTag = { id: string; name: string; slug: string; color: string;}
52
54
 
53
- export type Config = { siteName: string; siteDescription: string | null; language: string | null; contactEmail: string | null; keywords: string[]; businessType: string | null; isSetupComplete: boolean; logoLightExpandedUrl: string | null; logoDarkExpandedUrl: string | null; logoLightSmallUrl: string | null; logoDarkSmallUrl: string | null; ogImageUrl: string | null; owner: { name: string | null; email: string | null; jobTitle: string | null; }; business: { name: string | null; }; address: { street: string | null; city: string | null; region: string | null; postalCode: string | null; country: string | null; telephone: string | null; lat: number | null; lng: number | null; } | null; fiscal: { taxId: string | null; taxIdKind: "NIF" | "CIF" | null; legalName: string | null; registry: { name: string; number: string; } | null; } | null; social: { linkedin: string | null; twitter: string | null; facebook: string | null; instagram: string | null; youtube: string | null; } | null; metadata: unknown; openingHours: { ranges: { days: ("MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN")[]; opens: string; closes: string; }[]; note?: string | undefined; } | null; contactFormMode: "NOT_CONFIGURED" | "DISABLED" | "DB_ONLY" | "WEBHOOK" | "RESEND"; features: Record<string, unknown>; seoTitleTemplate?: string | undefined; seoTitle?: string | null | undefined; seoDescription?: string | null | undefined; canonicalUrl?: string | null | undefined;}
55
+ export type Config = { siteName: string; siteDescription: string | null; language: string | null; contactEmail: string | null; keywords: string[]; businessType: string | null; isSetupComplete: boolean; logoLightExpandedUrl: string | null; logoDarkExpandedUrl: string | null; logoLightSmallUrl: string | null; logoDarkSmallUrl: string | null; ogImageUrl: string | null; owner: { name: string | null; email: string | null; jobTitle: string | null; }; business: { name: string | null; }; address: { street: string | null; city: string | null; region: string | null; postalCode: string | null; country: string | null; telephone: string | null; lat: number | null; lng: number | null; } | null; fiscal: { taxId: string | null; taxIdKind: "NIF" | "CIF" | null; legalName: string | null; registry: { name: string; number: string; } | null; } | null; social: { linkedin: string | null; twitter: string | null; facebook: string | null; instagram: string | null; youtube: string | null; } | null; metadata: unknown; openingHours: { ranges: { days: ("MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN")[]; opens: string; closes: string; }[]; note?: string | undefined; } | null; contactFormMode: "NOT_CONFIGURED" | "DISABLED" | "DB_ONLY" | "WEBHOOK" | "RESEND"; features: Record<string, unknown>; seoTitleTemplate?: string | undefined; seoTitle?: string | null | undefined; seoDescription?: string | null | undefined; canonicalUrl?: string | null | undefined; contactInterest?: { label: string; options: string[]; } | null | undefined;}
54
56
 
55
57
  export type ConfigAddress = { street: string | null; city: string | null; region: string | null; postalCode: string | null; country: string | null; telephone: string | null; lat: number | null; lng: number | null;}
56
58
 
@@ -68,7 +70,7 @@ export type ConfigSocial = { linkedin: string | null; twitter: string | nu
68
70
 
69
71
  export type ConfigTaxIdKind = "NIF" | "CIF"
70
72
 
71
- export type ContactFormInput = { name: string; email: string; phone: string; message: string; agreement: boolean; captcha: string; metadata: { fromPage: string; timestamp: string; currentUrl: string; leadSource?: { platform: string; } | undefined; attribution?: { first?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; last?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; } | undefined; }; surname?: string | undefined; businessName?: string | undefined;}
73
+ export type ContactFormInput = { name: string; email: string; phone: string; message: string; agreement: boolean; captcha: string; metadata: { fromPage: string; timestamp: string; currentUrl: string; leadSource?: { platform: string; } | undefined; attribution?: { first?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; last?: { utm?: { source?: string | undefined; medium?: string | undefined; campaign?: string | undefined; content?: string | undefined; term?: string | undefined; } | undefined; clickIds?: string[] | undefined; referrer?: string | undefined; landingPath?: string | undefined; at?: string | undefined; } | undefined; } | undefined; }; surname?: string | undefined; businessName?: string | undefined; interests?: string[] | undefined;}
72
74
 
73
75
  export type ContactFormMode = "NOT_CONFIGURED" | "DISABLED" | "DB_ONLY" | "WEBHOOK" | "RESEND"
74
76
 
@@ -104,7 +106,9 @@ export type LeadTouch = { utm?: { source?: string | undefined;
104
106
 
105
107
  export type LegacyPageType = "SERVICE" | "CUSTOM"
106
108
 
107
- export type Legal = { privacyPolicy: string | null; termsAndConditions: string | null; cookiePolicy: string | null;}
109
+ export type Legal = { privacyPolicy: string | null; termsAndConditions: string | null; cookiePolicy: string | null; termsVersion?: { id: string; version: number; publishedAt: string; } | null | undefined;}
110
+
111
+ export type LegalTermsVersion = { id: string; version: number; publishedAt: string; content: string; contentHash: string;}
108
112
 
109
113
  export type PageAmbiguousSlugError = { error: string; code: "ambiguous_slug"; candidates: { categorySlug: string | null; }[];}
110
114
 
@@ -182,11 +186,15 @@ export type ShopCategory = { id: string; name: string; slug: string;
182
186
  */
183
187
  export type ShopCheckEmailInput = { email: string;}
184
188
 
185
- export type ShopCheckoutInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; customerEmail: string; customerName: string; successUrl: string; cancelUrl: string; customerPhone?: string | null | undefined; discountCode?: string | undefined; shippingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; billing?: { billingName: string; billingNif?: string | undefined; billingIdType?: "NIF" | "02" | "03" | "04" | "05" | "06" | undefined; billingIdCountry?: string | undefined; billingEmail?: string | undefined; billingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; } | undefined; wantsFullInvoice?: boolean | undefined;}
189
+ export type ShopCheckoutCopy = { tenantName: string; continueToPaymentLabel: string; shippingOptionLabel: string; pickupOptionLabel: string; emptyCartText: string; lineKindLabels: Record<string, string>; isTaxLineVisible: boolean; termsCheckboxLabel: string; termsLinkText: string; termsLinkUrl: string | null; isTermsLinkNewTab: boolean; payNotice: string; fullInvoiceCheckboxLabel: string; fullInvoiceHelpText: string; termsVersion: { id: string; version: number; publishedAt: string; } | null;}
190
+
191
+ export type ShopCheckoutDeliveryConfig = { methods: { homeDelivery: boolean; pickup: boolean; }; shippingEnabled: boolean; defaultShippingCost: number | null; freeShippingThreshold: number | null; freeShippingScope: "SHIPPABLE_ONLY" | "WHOLE_CART"; maxOrderWeightGrams: number | null; maxOrderWeightScope: "SHIPPING_ONLY" | "ALL_ORDERS"; estimatedDeliveryText: string | null; pickupLocations: { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null; }[];}
192
+
193
+ export type ShopCheckoutInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; customerEmail: string; customerName: string; successUrl: string; cancelUrl: string; customerPhone?: string | null | undefined; discountCode?: string | undefined; shippingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; billing?: { billingName: string; billingNif?: string | undefined; billingIdType?: "NIF" | "02" | "03" | "04" | "05" | "06" | undefined; billingIdCountry?: string | undefined; billingEmail?: string | undefined; billingAddress?: { firstName: string; lastName: string; street: string; city: string; postalCode: string; region?: string | undefined; country?: string | undefined; phone?: string | null | undefined; } | undefined; } | undefined; wantsFullInvoice?: boolean | undefined; fulfillment?: { type: "SHIPPING" | "PICKUP"; pickupLocationId?: string | undefined; } | undefined; acceptsTerms?: boolean | undefined; termsVersionId?: string | undefined;}
186
194
 
187
195
  export type ShopCheckoutSession = { url: string; sessionId: string; autoDiscount?: { code: string; label: string; amount: number; } | undefined;}
188
196
 
189
- export type ShopConfig = { shopName: string | null; defaultLowStockThreshold: number; currencyCode: string; stripePublishableKey: string | null; stripeTestPublishableKey: string | null; stripeTestMode: boolean; shippingEnabled: boolean; pickupEnabled: boolean; taxEnabled: boolean; taxIncludedInPrice: boolean; simplifiedInvoiceCapCents?: number | null | undefined; productTrustItems?: { [x: string]: unknown; icon: (string & {}) | "shipping" | "returns" | "secure-payment" | "support" | "gift" | "pickup" | "warranty" | "quality"; text: string; }[] | null | undefined;}
197
+ export type ShopConfig = { shopName: string | null; defaultLowStockThreshold: number; currencyCode: string; stripePublishableKey: string | null; stripeTestPublishableKey: string | null; stripeTestMode: boolean; shippingEnabled: boolean; pickupEnabled: boolean; taxEnabled: boolean; taxIncludedInPrice: boolean; simplifiedInvoiceCapCents?: number | null | undefined; productTrustItems?: { [x: string]: unknown; icon: (string & {}) | "shipping" | "returns" | "secure-payment" | "support" | "gift" | "pickup" | "warranty" | "quality"; text: string; }[] | null | undefined; delivery?: { methods: { homeDelivery: boolean; pickup: boolean; }; shippingEnabled: boolean; defaultShippingCost: number | null; freeShippingThreshold: number | null; freeShippingScope: "SHIPPABLE_ONLY" | "WHOLE_CART"; maxOrderWeightGrams: number | null; maxOrderWeightScope: "SHIPPING_ONLY" | "ALL_ORDERS"; estimatedDeliveryText: string | null; pickupLocations: { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null; }[]; } | undefined; checkout?: { tenantName: string; continueToPaymentLabel: string; shippingOptionLabel: string; pickupOptionLabel: string; emptyCartText: string; lineKindLabels: Record<string, string>; isTaxLineVisible: boolean; termsCheckboxLabel: string; termsLinkText: string; termsLinkUrl: string | null; isTermsLinkNewTab: boolean; payNotice: string; fullInvoiceCheckboxLabel: string; fullInvoiceHelpText: string; termsVersion: { id: string; version: number; publishedAt: string; } | null; } | undefined;}
190
198
 
191
199
  export type ShopDiscountApplication = { valid: boolean; discount: number; type: "PERCENTAGE" | "FIXED" | null; code: string | null; error?: string | undefined; autoDiscount?: { code: string; label: string; amount: number; } | undefined;}
192
200
 
@@ -198,10 +206,12 @@ export type ShopDiscountApplication = { valid: boolean; discount: number;
198
206
  */
199
207
  export type ShopEmailCheck = { exists: boolean;}
200
208
 
201
- export type ShopErrorCode = "STOCK_CHANGED" | "PRICE_CHANGED" | "PRODUCT_UNAVAILABLE" | "DISCOUNT_INVALID" | "SHIPPING_UNAVAILABLE" | "INVALID_INPUT" | "STORE_DISABLED" | "PAYMENT_PROVIDER_ERROR" | "INTERNAL_ERROR" | "BILLING_DATA_REQUIRED" | "INVOICING_NOT_READY" | "FULL_INVOICE_UNAVAILABLE"
209
+ export type ShopErrorCode = "STOCK_CHANGED" | "PRICE_CHANGED" | "PRODUCT_UNAVAILABLE" | "DISCOUNT_INVALID" | "SHIPPING_UNAVAILABLE" | "INVALID_INPUT" | "STORE_DISABLED" | "PAYMENT_PROVIDER_ERROR" | "INTERNAL_ERROR" | "BILLING_DATA_REQUIRED" | "INVOICING_NOT_READY" | "FULL_INVOICE_UNAVAILABLE" | "DISCOUNT_LIMIT_REACHED" | "PICKUP_UNAVAILABLE" | "PICKUP_LOCATION_INVALID" | "SHIPPING_ADDRESS_REQUIRED" | "ORDER_TOO_HEAVY" | "TERMS_NOT_ACCEPTED" | "TERMS_VERSION_CHANGED" | "DELIVERY_UNAVAILABLE"
202
210
 
203
211
  export type ShopFacets = { taxonomies: { key: string; label: string; showAsBadge: boolean; selectionMode: "single" | "multiple"; terms: { slug: string; name: string; count: number; }[]; }[]; attributes: { key: string; label: string; valueType: "boolean" | "text" | "select" | "multiselect"; options: { value: string; label: string; count: number; }[]; }[]; price: { min: number; max: number; }; onSale: { count: number; };}
204
212
 
213
+ export type ShopFulfillmentInput = { type: "SHIPPING" | "PICKUP"; pickupLocationId?: string | undefined;}
214
+
205
215
  export type ShopInventoryRef = { stockQuantity: number; reservedQuantity: number; lowStockThreshold: number | null;}
206
216
 
207
217
  export type ShopOrder = { id: string; orderNumber: string; status: "PENDING" | "CONFIRMED" | "PROCESSING" | "SHIPPED" | "DELIVERED" | "CANCELLED" | "REFUNDED"; paymentStatus: "PENDING" | "REFUNDED" | "PAID" | "FAILED" | "PARTIALLY_REFUNDED"; customerEmail: string; total: number; items: { product: { id: string; name: string; slug: string; }; quantity: number; unitPrice: number; total: number; variant?: { id: string; name: string; } | null | undefined; }[]; invoice?: { number: string; url: string; } | null | undefined;}
@@ -210,6 +220,8 @@ export type ShopOrderInvoice = { number: string; url: string;}
210
220
 
211
221
  export type ShopOrderItem = { product: { id: string; name: string; slug: string; }; quantity: number; unitPrice: number; total: number; variant?: { id: string; name: string; } | null | undefined;}
212
222
 
223
+ export type ShopPickupLocation = { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null;}
224
+
213
225
  export type ShopProductAttributes = unknown
214
226
 
215
227
  export type ShopProductCategoryRef = { id: string; name: string; slug: string;}
@@ -230,6 +242,12 @@ export type ShopProductVariant = { id: string; name: string; sku: strin
230
242
 
231
243
  export type ShopProductVideo = { url: string; title: string | null; isExternal: boolean; thumbnailUrl: string | null; sortOrder: number;}
232
244
 
245
+ export type ShopQuote = { lines: { productId: string; variantId: string | null; name: string; variantName: string | null; imageUrl: string | null; kind: string | null; kindLabel: string | null; quantity: number; unitPrice: number; total: number; isShippable: boolean; unitWeightGrams: number | null; }[]; subtotal: number; discount: { code: string; amount: number; isAutomatic: boolean; label: string | null; } | null; discountError: { code: "DISCOUNT_INVALID" | "DISCOUNT_LIMIT_REACHED"; message: string; } | null; fulfillment: { type: "SHIPPING" | "PICKUP" | null; label: string | null; pickupLocation: { id: string; name: string; address: string; city: string; postalCode: string; region: string | null; country: string; phone: string | null; email: string | null; openingHoursText: string | null; pickupReadyText: string | null; } | null; }; shipping: { cost: number; isFree: boolean; freeShippingThreshold: number | null; freeShippingScope: "SHIPPABLE_ONLY" | "WHOLE_CART"; remainingForFree: number | null; }; tax: { isIncluded: boolean; amount: number; isLineVisible: boolean; breakdown: { label: string; amount: number; }[]; }; weight: { totalGrams: number; maxGrams: number | null; scope: "SHIPPING_ONLY" | "ALL_ORDERS"; exceeded: boolean; }; deliveryEstimateText: string | null; total: number; payNotice: string; canCheckout: boolean; blockingReason: { code: "ORDER_TOO_HEAVY" | "DELIVERY_UNAVAILABLE"; message: string; } | null;}
246
+
247
+ export type ShopQuoteInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[]; discountCode?: string | undefined; customerEmail?: string | undefined; fulfillment?: { type: "SHIPPING" | "PICKUP"; pickupLocationId?: string | undefined; } | undefined;}
248
+
249
+ export type ShopQuoteLine = { productId: string; variantId: string | null; name: string; variantName: string | null; imageUrl: string | null; kind: string | null; kindLabel: string | null; quantity: number; unitPrice: number; total: number; isShippable: boolean; unitWeightGrams: number | null;}
250
+
233
251
  export type ShopRelatedProduct = { id: string; slug: string; name: string; price: number; comparePrice: number | null; images: { url: string; alt: string | null; isPrimary: boolean; sortOrder: number; }[];}
234
252
 
235
253
  export type ShopSort = "price_asc" | "price_desc" | "newest" | "on_sale"
@@ -242,6 +260,8 @@ export type ShopTaxonomySelectionMode = "single" | "multiple"
242
260
 
243
261
  export type ShopTaxonomyTermRef = { taxonomyKey: string; taxonomyLabel: string; showAsBadge: boolean; slug: string; name: string; imageUrl: string | null;}
244
262
 
263
+ export type ShopTermsVersionRef = { id: string; version: number; publishedAt: string;}
264
+
245
265
  export type ShopValidateCartInput = { items: { productId: string; quantity: number; variantId?: string | null | undefined; }[];}
246
266
 
247
267
  export type SiteCapabilitySurfaceKey = "academy.enrollment" | "academy.catalogue" | "auth.login" | "consent.banner" | "legal.cookies"
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAkFO,IAAM,kBAAA,GAAqB,OAAO,MAAA,CAAO;AAAA,EAC9C,QAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA;AAAA,EACA,oBAAA;AAAA,EACA;AACF,CAAU","file":"index.js","sourcesContent":["/**\n * `@reblu/site-contracts` — public, dependency-free TypeScript types for the\n * Reblu SITE API (REB-414 / design doc §2).\n *\n * These types are the SDK-facing view of every `/api/site/*` response. They are\n * re-derived (`z.infer`) from the hand-authored response schemas that live in\n * base-api (`apps/base-api/src/lib/contracts/*.contract.ts`) and imported here\n * through the build-time-only `@reblu/base-api-contracts` type alias.\n *\n * Guarantees (enforced by the leakage guard tests):\n * - Everything below is `export type` — the import is erased at build time, so\n * NO Zod and NO Prisma reaches a consumer's runtime or `node_modules` graph.\n * The one exception is `DATA_REQUEST_TYPES` (REB-1005): a plain, import-free\n * array literal, the package's only runtime value.\n * - The published `.d.ts` is flattened and self-contained: the alias and\n * `z.infer` do not survive into the emitted declarations.\n * - Never re-exports an internal / Prisma-generated type — only the public\n * output DTOs base-api explicitly publishes.\n *\n * When base-api adds a new public contract type, add it to the matching group\n * below. The explicit list (over `export type *`) keeps the public surface a\n * reviewed, intentional decision and avoids leaking internal `*Schema` names.\n */\n\n// Envelope / pagination\nexport type { PaginationMeta } from '@reblu/base-api-contracts'\n\n// Analytics + shared contact-form mode\nexport type { Analytics, ContactFormMode } from '@reblu/base-api-contracts'\n\n// REB-845 — the tenant-editable cookie banner copy shape, standalone (not just\n// nested in `Analytics`), so `@reblu/site-next`'s `cookieBannerToConsentTexts`\n// can type its input against the real contract instead of a hand-rolled copy.\nexport type { CookieBanner } from '@reblu/base-api-contracts'\n\n// Site config\nexport type {\n Config,\n ConfigAddress,\n ConfigFiscal,\n ConfigFiscalRegistry,\n ConfigSocial,\n ConfigTaxIdKind,\n // REB-826 — typed opening hours.\n ConfigOpeningHours,\n ConfigOpeningHoursRange,\n ConfigOpeningHoursDay,\n} from '@reblu/base-api-contracts'\n\n// REB-871 — the resolved `seo` block on every detail response (pages, blog\n// posts, shop products, academy courses). `seo.title` is the FINAL page title.\nexport type { Seo } from '@reblu/base-api-contracts'\n\n// Contact form\nexport type { ContactFormSubmission, ContactFormInput } from '@reblu/base-api-contracts'\n// REB-1078 — the raw lead-origin touches a SITE sends in `metadata.attribution`\n// (contact form and `/api/site/leads`); base-api classifies the channel.\nexport type { LeadAttribution, LeadTouch } from '@reblu/base-api-contracts'\n\n// Leads\nexport type { LeadInput, LeadSubmission } from '@reblu/base-api-contracts'\n\n// GDPR data-subject requests (REB-77 endpoint, REB-980 SDK) — the public\n// `/derechos-rgpd` form body and its 72h acknowledgement.\nexport type {\n DataRequestInput,\n DataRequestSubmission,\n DataRequestType,\n} from '@reblu/base-api-contracts'\nimport type { DataRequestType } from '@reblu/base-api-contracts'\n\n/**\n * REB-1005 — every GDPR right a data subject can exercise, in base-api's order.\n * The ONE runtime value this package ships: server code (a SITE's Route\n * Handler, `@reblu/site-next`'s `createDataRequestRoute`) needs the list too,\n * and `@reblu/site-blocks` re-exports it as before.\n *\n * Single source: base-api's `dataRequestTypeSchema`. `satisfies` rejects a\n * member base-api does not know, `MissingDataRequestType` below rejects one it\n * knows and this list lacks, and `tests/unit/data-request-types.test.ts` pins\n * the order. Plain array literal, no import — the runtime stays dependency-free.\n */\nexport const DATA_REQUEST_TYPES = Object.freeze([\n 'ACCESS',\n 'RECTIFICATION',\n 'ERASURE',\n 'OBJECTION',\n 'PORTABILITY',\n 'RESTRICTION',\n 'AUTOMATED_DECISION',\n 'INFORMATION',\n] as const) satisfies readonly DataRequestType[]\n\n/** Compile error when base-api adds a right this list does not carry. */\ntype AssertNoneMissing<T extends never> = T\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ntype MissingDataRequestType = AssertNoneMissing<\n Exclude<DataRequestType, (typeof DATA_REQUEST_TYPES)[number]>\n>\n\n// Blog\nexport type {\n BlogAuthor,\n BlogAuthorDetail,\n BlogCategory,\n BlogCategoryWithCount,\n BlogTag,\n BlogPostListItem,\n BlogPostDetail,\n BlogPostPreview,\n BlogSlug,\n} from '@reblu/base-api-contracts'\n\n// Pages\nexport type {\n // The flexible pageType (service|custom|treatments|specialties|team|generic)\n // — REB-63: this is what `pages.list`'s `type` filter actually validates\n // against server-side (`pagesListParamsSchema`), not `LegacyPageType`.\n PageType,\n // Legacy DB page type (SERVICE|CUSTOM) — the `type`/`category` fields still\n // carried on each `PageListItem`/`PageDetail` for backward compat during the\n // REB-75 dual-read window. NOT the `pages.list` filter's value space anymore.\n LegacyPageType,\n PageContentMode,\n SectionLayout,\n PageCategoryRef,\n PageListItem,\n PageSectionImage,\n PageSection,\n PageDetail,\n PageSlug,\n // REB-63: `pages.list`'s sort param — same reasoning as AcademyCourseSort\n // below (a typo is a compile error instead of a silent no-op fallback).\n PagesListSort,\n // REB-869 — `pages.list`'s query, incl. `featured` (with `category`: that\n // category's featured pages, max 3, in their stored order).\n PagesListQuery,\n // REB-75 D-D — the typed block registry on `PageDetail.blocks[]`.\n BlockType,\n Block,\n // REB-849 — each block mirrors its parent section's heading/backgroundColor/\n // cta; REB-851 phase 1 — doctor_embed/specialty_embed blocks additionally\n // carry `resolved` (present only on those two block types).\n PageBlock,\n BlockCta,\n ResolvedEmbed,\n // REB-867 — slugs are unique per category: the embed reference shape\n // (`pageId`/`categorySlug`) and the 409 body of an ambiguous `GET /pages/[slug]`.\n EmbedReferenceData,\n PageAmbiguousSlugError,\n} from '@reblu/base-api-contracts'\n\n// Professional profile (REB-63) — pageType='team' only. Sector-agnostic\n// structured CV, same platform-shape/tenant-content split as CustomFieldDefinition.\n// REB-1099: `ProfessionalField` — a tenant-defined field (e.g. a registration\n// number) on the card (`ProfessionalSummary.fields`) and profile.\nexport type {\n ProfessionalField,\n ProfessionalSummary,\n ProfessionalProfile,\n ProfessionalCv,\n ProfessionalCvEntry,\n ProfessionalLanguage,\n ProfessionalLanguageLevel,\n ProfessionalSocialLink,\n ProfessionalSocialPlatform,\n} from '@reblu/base-api-contracts'\n\n// Custom-field registry (REB-475 / REB-75) — entity-agnostic, shared with pages/team\nexport type {\n CustomFieldValueType,\n CustomFieldEntityType,\n CustomFieldOption,\n CustomFieldDefinition,\n} from '@reblu/base-api-contracts'\n\n// Shop\nexport type {\n TaxType,\n ProductType,\n ShopConfig,\n // Store-wide product trust items (REB-858)\n ShopProductTrustIcon,\n ShopProductTrustItem,\n ShopTaxRateRef,\n ShopProductImage,\n ShopProductVideo,\n ShopProductCategoryRef,\n ShopInventoryRef,\n ShopCategory,\n ShopProductListItem,\n ShopProductVariant,\n ShopBundleComponent,\n ShopRelatedProduct,\n ShopProductDetail,\n // Taxonomy + attributes + facets (REB-474/475)\n ShopTaxonomyTermRef,\n ShopProductAttributes,\n // Per-axis selection mode (REB-809/REB-801)\n ShopTaxonomySelectionMode,\n ShopSort,\n ShopProductListParams,\n ShopFacets,\n // Write inputs (REB-438)\n ShopCartItemInput,\n ShopAddressInput,\n ShopBillingInput,\n ShopCheckoutInput,\n ShopValidateCartInput,\n ShopApplyDiscountInput,\n // Write responses (REB-438)\n ShopCheckoutSession,\n ShopCartValidationError,\n ShopCartValidationItem,\n ShopCartValidation,\n ShopDiscountApplication,\n // Automatic staff discount, additive on the write responses (REB-1131)\n ShopStaffAutoDiscount,\n // Order lookup (REB-829)\n ShopOrderItem,\n ShopOrder,\n // The order's invoice reference (REB-73)\n ShopOrderInvoice,\n // Stable, machine-readable write-endpoint error codes (REB-836)\n ShopErrorCode,\n} from '@reblu/base-api-contracts'\n\n// REB-834 — `POST /api/site/shop/check-email` is deprecated: it was an\n// account-enumeration oracle and now always answers `{ exists: false }`.\n// Kept (a minor may only deprecate); removed in the next major.\nexport type {\n /**\n * @deprecated REB-834 — `POST /api/site/shop/check-email` no longer reveals\n * whether an account exists. Do not call it; removed in the next major.\n */\n ShopCheckEmailInput,\n /**\n * @deprecated REB-834 — `exists` is always `false`. Never branch UI on it:\n * the checkout always offers «Iniciar sesión o continuar como invitado» and\n * the server links the order to an existing account after payment. Removed\n * in the next major.\n */\n ShopEmailCheck,\n} from '@reblu/base-api-contracts'\n\n// Academy (REB-507) — public course catalogue. `AcademyCourseSort` is the query\n// contract for the `sort` param, so the SDK can type it as a union instead of a\n// bare string (an unknown key is silently ignored server-side).\nexport type {\n AcademyCourseSort,\n AcademyCategoryPublic,\n AcademyLessonPublic,\n AcademyModulePublic,\n AcademyInstructorPublic,\n AcademyCourseSummary,\n AcademyCourseDetail,\n // REB-83 — tenant-configurable validation fields (drives the enrollment CTA form).\n AcademyValidationFieldPublic,\n // REB-1135 — a free-preview lesson, viewable by the public\n // (`GET /api/site/academy/courses/{slug}/lessons/{lessonSlug}`).\n AcademyLessonPreview,\n AcademyLessonPreviewVideo,\n} from '@reblu/base-api-contracts'\n\n// Legal\nexport type { Legal } from '@reblu/base-api-contracts'\n\n// CTAs\nexport type { Cta } from '@reblu/base-api-contracts'\n\n// Trust badges\nexport type { TrustBadge, TrustBadgeType } from '@reblu/base-api-contracts'\n\n// SITE capability manifest (`/.well-known/reblu-site.json`) — REB-777.\n// Direction note (see `site-manifest.contract.ts` in base-api): this is the\n// one contract group a SITE produces and base-api consumes, the opposite of\n// every group above. The TYPE still flows through this same alias so the\n// SDK and nexus never describe two different shapes for it; the real Zod\n// validation stays in base-api only — this package never gains a Zod\n// runtime dependency.\nexport type {\n SiteManifest,\n SiteManifestSurfaceValue,\n SiteCapabilitySurfaceKey,\n} from '@reblu/base-api-contracts'\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAkFO,IAAM,kBAAA,GAAqB,OAAO,MAAA,CAAO;AAAA,EAC9C,QAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA;AAAA,EACA,oBAAA;AAAA,EACA;AACF,CAAU","file":"index.js","sourcesContent":["/**\n * `@reblu/site-contracts` — public, dependency-free TypeScript types for the\n * Reblu SITE API (REB-414 / design doc §2).\n *\n * These types are the SDK-facing view of every `/api/site/*` response. They are\n * re-derived (`z.infer`) from the hand-authored response schemas that live in\n * base-api (`apps/base-api/src/lib/contracts/*.contract.ts`) and imported here\n * through the build-time-only `@reblu/base-api-contracts` type alias.\n *\n * Guarantees (enforced by the leakage guard tests):\n * - Everything below is `export type` — the import is erased at build time, so\n * NO Zod and NO Prisma reaches a consumer's runtime or `node_modules` graph.\n * The one exception is `DATA_REQUEST_TYPES` (REB-1005): a plain, import-free\n * array literal, the package's only runtime value.\n * - The published `.d.ts` is flattened and self-contained: the alias and\n * `z.infer` do not survive into the emitted declarations.\n * - Never re-exports an internal / Prisma-generated type — only the public\n * output DTOs base-api explicitly publishes.\n *\n * When base-api adds a new public contract type, add it to the matching group\n * below. The explicit list (over `export type *`) keeps the public surface a\n * reviewed, intentional decision and avoids leaking internal `*Schema` names.\n */\n\n// Envelope / pagination\nexport type { PaginationMeta } from '@reblu/base-api-contracts'\n\n// Analytics + shared contact-form mode\nexport type { Analytics, ContactFormMode } from '@reblu/base-api-contracts'\n\n// REB-845 — the tenant-editable cookie banner copy shape, standalone (not just\n// nested in `Analytics`), so `@reblu/site-next`'s `cookieBannerToConsentTexts`\n// can type its input against the real contract instead of a hand-rolled copy.\nexport type { CookieBanner } from '@reblu/base-api-contracts'\n\n// Site config\nexport type {\n Config,\n ConfigAddress,\n ConfigFiscal,\n ConfigFiscalRegistry,\n ConfigSocial,\n ConfigTaxIdKind,\n // REB-826 — typed opening hours.\n ConfigOpeningHours,\n ConfigOpeningHoursRange,\n ConfigOpeningHoursDay,\n} from '@reblu/base-api-contracts'\n\n// REB-871 — the resolved `seo` block on every detail response (pages, blog\n// posts, shop products, academy courses). `seo.title` is the FINAL page title.\nexport type { Seo } from '@reblu/base-api-contracts'\n\n// Contact form\nexport type { ContactFormSubmission, ContactFormInput } from '@reblu/base-api-contracts'\n// REB-1078 — the raw lead-origin touches a SITE sends in `metadata.attribution`\n// (contact form and `/api/site/leads`); base-api classifies the channel.\nexport type { LeadAttribution, LeadTouch } from '@reblu/base-api-contracts'\n\n// Leads\nexport type { LeadInput, LeadSubmission } from '@reblu/base-api-contracts'\n\n// GDPR data-subject requests (REB-77 endpoint, REB-980 SDK) — the public\n// `/derechos-rgpd` form body and its 72h acknowledgement.\nexport type {\n DataRequestInput,\n DataRequestSubmission,\n DataRequestType,\n} from '@reblu/base-api-contracts'\nimport type { DataRequestType } from '@reblu/base-api-contracts'\n\n/**\n * REB-1005 — every GDPR right a data subject can exercise, in base-api's order.\n * The ONE runtime value this package ships: server code (a SITE's Route\n * Handler, `@reblu/site-next`'s `createDataRequestRoute`) needs the list too,\n * and `@reblu/site-blocks` re-exports it as before.\n *\n * Single source: base-api's `dataRequestTypeSchema`. `satisfies` rejects a\n * member base-api does not know, `MissingDataRequestType` below rejects one it\n * knows and this list lacks, and `tests/unit/data-request-types.test.ts` pins\n * the order. Plain array literal, no import — the runtime stays dependency-free.\n */\nexport const DATA_REQUEST_TYPES = Object.freeze([\n 'ACCESS',\n 'RECTIFICATION',\n 'ERASURE',\n 'OBJECTION',\n 'PORTABILITY',\n 'RESTRICTION',\n 'AUTOMATED_DECISION',\n 'INFORMATION',\n] as const) satisfies readonly DataRequestType[]\n\n/** Compile error when base-api adds a right this list does not carry. */\ntype AssertNoneMissing<T extends never> = T\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ntype MissingDataRequestType = AssertNoneMissing<\n Exclude<DataRequestType, (typeof DATA_REQUEST_TYPES)[number]>\n>\n\n// Blog\nexport type {\n BlogAuthor,\n BlogAuthorDetail,\n BlogCategory,\n BlogCategoryWithCount,\n BlogTag,\n BlogPostListItem,\n BlogPostDetail,\n BlogPostPreview,\n BlogSlug,\n} from '@reblu/base-api-contracts'\n\n// Pages\nexport type {\n // The flexible pageType (service|custom|treatments|specialties|team|generic)\n // — REB-63: this is what `pages.list`'s `type` filter actually validates\n // against server-side (`pagesListParamsSchema`), not `LegacyPageType`.\n PageType,\n // Legacy DB page type (SERVICE|CUSTOM) — the `type`/`category` fields still\n // carried on each `PageListItem`/`PageDetail` for backward compat during the\n // REB-75 dual-read window. NOT the `pages.list` filter's value space anymore.\n LegacyPageType,\n PageContentMode,\n SectionLayout,\n PageCategoryRef,\n PageListItem,\n PageSectionImage,\n PageSection,\n PageDetail,\n PageSlug,\n // REB-63: `pages.list`'s sort param — same reasoning as AcademyCourseSort\n // below (a typo is a compile error instead of a silent no-op fallback).\n PagesListSort,\n // REB-869 — `pages.list`'s query, incl. `featured` (with `category`: that\n // category's featured pages, max 3, in their stored order).\n PagesListQuery,\n // REB-75 D-D — the typed block registry on `PageDetail.blocks[]`.\n BlockType,\n Block,\n // REB-849 — each block mirrors its parent section's heading/backgroundColor/\n // cta; REB-851 phase 1 — doctor_embed/specialty_embed blocks additionally\n // carry `resolved` (present only on those two block types).\n PageBlock,\n BlockCta,\n ResolvedEmbed,\n // REB-867 — slugs are unique per category: the embed reference shape\n // (`pageId`/`categorySlug`) and the 409 body of an ambiguous `GET /pages/[slug]`.\n EmbedReferenceData,\n PageAmbiguousSlugError,\n} from '@reblu/base-api-contracts'\n\n// Professional profile (REB-63) — pageType='team' only. Sector-agnostic\n// structured CV, same platform-shape/tenant-content split as CustomFieldDefinition.\n// REB-1099: `ProfessionalField` — a tenant-defined field (e.g. a registration\n// number) on the card (`ProfessionalSummary.fields`) and profile.\nexport type {\n ProfessionalField,\n ProfessionalSummary,\n ProfessionalProfile,\n ProfessionalCv,\n ProfessionalCvEntry,\n ProfessionalLanguage,\n ProfessionalLanguageLevel,\n ProfessionalSocialLink,\n ProfessionalSocialPlatform,\n} from '@reblu/base-api-contracts'\n\n// Custom-field registry (REB-475 / REB-75) — entity-agnostic, shared with pages/team\nexport type {\n CustomFieldValueType,\n CustomFieldEntityType,\n CustomFieldOption,\n CustomFieldDefinition,\n} from '@reblu/base-api-contracts'\n\n// Shop\nexport type {\n TaxType,\n ProductType,\n ShopConfig,\n // Store-wide product trust items (REB-858)\n ShopProductTrustIcon,\n ShopProductTrustItem,\n ShopTaxRateRef,\n ShopProductImage,\n ShopProductVideo,\n ShopProductCategoryRef,\n ShopInventoryRef,\n ShopCategory,\n ShopProductListItem,\n ShopProductVariant,\n ShopBundleComponent,\n ShopRelatedProduct,\n ShopProductDetail,\n // Taxonomy + attributes + facets (REB-474/475)\n ShopTaxonomyTermRef,\n ShopProductAttributes,\n // Per-axis selection mode (REB-809/REB-801)\n ShopTaxonomySelectionMode,\n ShopSort,\n ShopProductListParams,\n ShopFacets,\n // Write inputs (REB-438)\n ShopCartItemInput,\n ShopAddressInput,\n ShopBillingInput,\n ShopCheckoutInput,\n ShopValidateCartInput,\n ShopApplyDiscountInput,\n // Write responses (REB-438)\n ShopCheckoutSession,\n ShopCartValidationError,\n ShopCartValidationItem,\n ShopCartValidation,\n ShopDiscountApplication,\n // Automatic staff discount, additive on the write responses (REB-1131)\n ShopStaffAutoDiscount,\n // Order lookup (REB-829)\n ShopOrderItem,\n ShopOrder,\n // The order's invoice reference (REB-73)\n ShopOrderInvoice,\n // Stable, machine-readable write-endpoint error codes (REB-836)\n ShopErrorCode,\n // Single-step checkout (REB-1197 / REB-1198): delivery rules, pickup\n // points, resolved checkout copy, the terms version to accept, the\n // delivery choice and the pre-payment quote.\n ShopPickupLocation,\n ShopCheckoutDeliveryConfig,\n ShopCheckoutCopy,\n ShopTermsVersionRef,\n ShopFulfillmentInput,\n ShopQuoteInput,\n ShopQuoteLine,\n ShopQuote,\n} from '@reblu/base-api-contracts'\n\n// REB-834 — `POST /api/site/shop/check-email` is deprecated: it was an\n// account-enumeration oracle and now always answers `{ exists: false }`.\n// Kept (a minor may only deprecate); removed in the next major.\nexport type {\n /**\n * @deprecated REB-834 — `POST /api/site/shop/check-email` no longer reveals\n * whether an account exists. Do not call it; removed in the next major.\n */\n ShopCheckEmailInput,\n /**\n * @deprecated REB-834 — `exists` is always `false`. Never branch UI on it:\n * the checkout always offers «Iniciar sesión o continuar como invitado» and\n * the server links the order to an existing account after payment. Removed\n * in the next major.\n */\n ShopEmailCheck,\n} from '@reblu/base-api-contracts'\n\n// Academy (REB-507) — public course catalogue. `AcademyCourseSort` is the query\n// contract for the `sort` param, so the SDK can type it as a union instead of a\n// bare string (an unknown key is silently ignored server-side).\nexport type {\n AcademyCourseSort,\n // REB-547 — the documented catalogue query: q, sort, category, instructor\n // (a user id from `GET /api/site/academy/instructors`), free and the price\n // range in integer cents.\n AcademyCourseListQuery,\n AcademyCategoryPublic,\n AcademyLessonPublic,\n AcademyModulePublic,\n AcademyInstructorPublic,\n AcademyCourseSummary,\n AcademyCourseDetail,\n // REB-83 — tenant-configurable validation fields (drives the enrollment CTA form).\n AcademyValidationFieldPublic,\n // REB-1135 — a free-preview lesson, viewable by the public\n // (`GET /api/site/academy/courses/{slug}/lessons/{lessonSlug}`).\n AcademyLessonPreview,\n AcademyLessonPreviewVideo,\n} from '@reblu/base-api-contracts'\n\n// Legal\nexport type { Legal, LegalTermsVersion } from '@reblu/base-api-contracts'\n\n// CTAs\nexport type { Cta } from '@reblu/base-api-contracts'\n\n// Trust badges\nexport type { TrustBadge, TrustBadgeType } from '@reblu/base-api-contracts'\n\n// SITE capability manifest (`/.well-known/reblu-site.json`) — REB-777.\n// Direction note (see `site-manifest.contract.ts` in base-api): this is the\n// one contract group a SITE produces and base-api consumes, the opposite of\n// every group above. The TYPE still flows through this same alias so the\n// SDK and nexus never describe two different shapes for it; the real Zod\n// validation stays in base-api only — this package never gains a Zod\n// runtime dependency.\nexport type {\n SiteManifest,\n SiteManifestSurfaceValue,\n SiteCapabilitySurfaceKey,\n} from '@reblu/base-api-contracts'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reblu/site-contracts",
3
- "version": "1.21.0",
3
+ "version": "1.23.0",
4
4
  "private": false,
5
5
  "description": "Public, dependency-free TypeScript types for the Reblu SITE API (no Zod, no Prisma at runtime; one runtime value: DATA_REQUEST_TYPES).",
6
6
  "license": "Apache-2.0",