@surfiran/public-api-client 0.37.0 → 0.38.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/README.md CHANGED
@@ -22,7 +22,16 @@ The package version, contract version, source commit, and API release identity o
22
22
 
23
23
  New client versions must consume the exact immutable GitHub Release assets produced from a successful `SURFIRAN/surfiran-crm-api` main-branch CI run. The release tag is `public-api-contract/<source SHA>-<run ID>-<attempt>` and contains only `public-api-v1.yaml` and `manifest.json`. Publication verifies the immutable release and both GitHub release attestations, the tag SHA, final successful source run, asset digests, manifest identity, and checked-in snapshot bytes. If any check is unavailable, publication stops. Pull-request artifacts are verification evidence only and are not valid release inputs. This repository must never read another repository's private working tree at build time.
24
24
 
25
- `src/generated/public-api-v1.ts` is generated from the checked-in snapshot; `src/client.ts` and `src/types.ts` are maintained in this repository against that reviewed contract. Never hand-edit generated source.
25
+ ### Generated and hand-written source
26
+
27
+ `npm run generate` produces two files from the checked-in snapshot. Never hand-edit either.
28
+
29
+ - `src/generated/public-api-v1.ts` (`scripts/generate-public-api-v1.cjs`): the contract version and the path, operation, operation ID and schema name unions. Published under the `./generated` export.
30
+ - `src/generated/public-api-v1.schema.ts` (`scripts/generate-public-api-v1-schema.cjs`, exact-pinned `openapi-typescript` devDependency): the full contract `paths`, `components` and `operations` types. Verification input only; it is excluded from the build and not published.
31
+
32
+ `src/client.ts` and `src/types.ts` stay hand-written against that reviewed contract, and they are what the package exports. `test/types-conformance.ts` checks every hand-written type whose name matches a contract schema against `components['schemas'][Name]` field by field: missing and extra fields, required/optional, and field types. It also checks that every `PublicApiV1RouteMap` route is in `PublicApiV1Path` and in the contract. `npm run typecheck` runs it through `tsconfig.conformance.json`, and `test/types-conformance-coverage.test.ts` (run by `npm test`) keeps its list of checked names exact.
33
+
34
+ Known deviations are listed in that file's `Allowlist`, one entry per field with its kind and reason. Kept-required fields (contract optional), kept-optional fields (contract required), branded localisation IDs and the contract-debt field `PublicTourListItem.travelFormat` are listed there. An entry that stops deviating fails the check, so the fix and the entry's removal land together. `npm run verify:generated` fails when either generated file is stale.
26
35
 
27
36
  ## Boundary
28
37
 
@@ -69,8 +78,8 @@ A new contract update must:
69
78
  2. make a reviewed package version change before rewriting contract provenance;
70
79
  3. verify/download the release assets, then run `sync:api-contract-artifact` with the exact API commit, run ID, and run attempt;
71
80
  4. update `contract/public-api-v1.yaml` and `contract/provenance.json` together;
72
- 5. regenerate the client;
73
- 6. pass artifact verification, package verification, reproducibility, tests, typecheck, and build;
81
+ 5. regenerate the client (`npm run generate` writes both generated files);
82
+ 6. pass artifact verification, package verification, reproducibility, tests, typecheck (including the type conformance check, adding new contract fields to `src/types.ts` or allowlisting a reviewed deviation), and build;
74
83
  7. use a reviewed compatible or breaking version decision before publication.
75
84
 
76
85
  The sync command rejects rewriting provenance for the current package version and rejects API artifacts that are not valid release inputs.
@@ -83,7 +92,7 @@ Do not duplicate the endpoint list here; the pinned OpenAPI snapshot and generat
83
92
 
84
93
  ## Publication and rollback
85
94
 
86
- Routine package CI uses the owned non-production `owned-ci-linux` / `owned-ci-heavy` runner pool with the Node.js version pinned by [ci.yml](.github/workflows/ci.yml). CodeQL Default Setup independently selects `owned-ci-linux` in repository code-scanning settings; its TypeScript extractor requires Node.js 20 or newer on the runner service's standard PATH, even when ordinary CI uses `setup-node`. Follow the [GitHub Actions budget policy effective 2026-10-13](https://github.com/SURFIRAN/surfiran-crm-api/blob/main/docs/architecture/REPOSITORY_BOUNDARIES.md#github-actions-budget-policy-owner-override-2026-10-13): GitHub Actions must use owned runners, with no GitHub-hosted or paid fallback. The separate [Codex Cloud development preference](https://github.com/SURFIRAN/surfiran-crm-api/blob/main/docs/architecture/REPOSITORY_BOUNDARIES.md#execution-resources-owner-policy-2026-10-05) remains unchanged. Manual publication uses the dedicated trusted `package-publish` runner and never occupies `surfdmc-prod-deploy`; package and cross-repository artifact credentials do not move to Home or general CI workers.
95
+ Routine package CI runs `Verify Public API client` on a standard GitHub-hosted `ubuntu-24.04` runner with the Node.js version pinned by [ci.yml](.github/workflows/ci.yml). It is light and secretless, refuses to run unless the runner reports `github-hosted` Linux X64, and is bounded by a 5-minute timeout. CodeQL Default Setup independently selects `owned-ci-linux` in repository code-scanning settings; its TypeScript extractor requires Node.js 20 or newer on the runner service's standard PATH, even when ordinary CI uses `setup-node`. Approved providers, quotas and limits are owned by [Ordinary CI providers](https://github.com/SURFIRAN/surfiran-crm-api/blob/main/docs/architecture/REPOSITORY_BOUNDARIES.md#ordinary-ci-providers-owner-decision-2026-11-01); paid capacity needs new owner approval. Manual publication uses the dedicated trusted `package-publish` runner and never occupies `surfdmc-prod-deploy`; package and cross-repository artifact credentials do not move to Home or general CI workers.
87
96
 
88
97
  Publication is manual and repository-local through [public-api-client-publish.yml](.github/workflows/public-api-client-publish.yml). It runs on `main` only with explicit publication authorization and `publish_confirm=PUBLISH`. A future version must be bound to an exact reviewed API contract release, pass package/reproducibility checks and publication preflight, create release provenance, and provide an exact previous rollback version. The workflow uses `PUBLIC_API_CONTRACT_READ_TOKEN` to read the private immutable API release and a separate `PUBLIC_API_CLIENT_RELEASE_SETTINGS_READ_TOKEN` with scoped Administration:read to confirm immutable Releases are enabled for this client repository. Before publishing to npmjs.org, it creates or exactly reuses an immutable client release `public-api-client/v<package version>` containing the packed tarball and provenance JSON. It marks the evidence release as non-latest and verifies its asset digests, source tag SHA, and GitHub release attestations. Retries must reproduce the same bytes, including the explicit generation timestamp. Missing or unverifiable release or publication evidence blocks npm publication; the workflow does not use Actions artifact storage for these gates.
89
98
 
@@ -1,4 +1,4 @@
1
- export type GeneratedPublicApiVersion = '0.29.0';
1
+ export type GeneratedPublicApiVersion = '0.30.1';
2
2
  export type GeneratedPublicApiPath = '/booking-intents' | '/booking-intents/{id}' | '/bot-protection' | '/brochure-leads' | '/destinations' | '/destinations/{slug}' | '/homepage' | '/inquiries' | '/locales' | '/localization/previews/{candidateId}' | '/localization/releases/{releaseId}' | '/localization/ui-catalogs/{snapshotId}' | '/marketing-unsubscribes' | '/newsletter/confirm' | '/newsletter/subscriptions' | '/reviews' | '/search' | '/search-suggestions' | '/site-content/about' | '/site-content/accessibility-statement' | '/site-content/booking-cancellation-policy' | '/site-content/contact' | '/site-content/cookie-policy' | '/site-content/destination-iran' | '/site-content/destinations/{country}' | '/site-content/faqs' | '/site-content/footer' | '/site-content/header' | '/site-content/health-safety-support' | '/site-content/home' | '/site-content/iran-travel-status' | '/site-content/iran-visa-information' | '/site-content/money-in-iran' | '/site-content/privacy' | '/site-content/responsible-travel' | '/site-content/tailor-made' | '/site-content/terms' | '/site-content/travel-insurance' | '/site-features' | '/site-navigation' | '/sitemap' | '/support-requests' | '/tour-categories' | '/tours' | '/tours/{slug}' | '/tours/{slug}/accommodations' | '/tours/{slug}/availability' | '/tours/{slug}/departures' | '/tours/{slug}/experiences' | '/tours/{slug}/itinerary' | '/tours/{slug}/page' | '/tours/{slug}/preview' | '/tours/{slug}/preview/page' | '/tours/{slug}/questions' | '/tours/{slug}/reviews' | '/tours/{slug}/reviews-summary' | '/tours/{slug}/route' | '/travel-insurance/rates' | '/travel-knowledge' | '/travel-status' | '/traveller-stories';
3
3
  export type GeneratedPublicApiOperation = 'GET /booking-intents/{id}' | 'GET /bot-protection' | 'GET /destinations' | 'GET /destinations/{slug}' | 'GET /homepage' | 'GET /locales' | 'GET /localization/previews/{candidateId}' | 'GET /localization/releases/{releaseId}' | 'GET /localization/ui-catalogs/{snapshotId}' | 'GET /marketing-unsubscribes' | 'GET /newsletter/confirm' | 'GET /reviews' | 'GET /search' | 'GET /search-suggestions' | 'GET /site-content/about' | 'GET /site-content/accessibility-statement' | 'GET /site-content/booking-cancellation-policy' | 'GET /site-content/contact' | 'GET /site-content/cookie-policy' | 'GET /site-content/destination-iran' | 'GET /site-content/destinations/{country}' | 'GET /site-content/faqs' | 'GET /site-content/footer' | 'GET /site-content/header' | 'GET /site-content/health-safety-support' | 'GET /site-content/home' | 'GET /site-content/iran-travel-status' | 'GET /site-content/iran-visa-information' | 'GET /site-content/money-in-iran' | 'GET /site-content/privacy' | 'GET /site-content/responsible-travel' | 'GET /site-content/tailor-made' | 'GET /site-content/terms' | 'GET /site-content/travel-insurance' | 'GET /site-features' | 'GET /site-navigation' | 'GET /sitemap' | 'GET /tour-categories' | 'GET /tours' | 'GET /tours/{slug}' | 'GET /tours/{slug}/accommodations' | 'GET /tours/{slug}/availability' | 'GET /tours/{slug}/departures' | 'GET /tours/{slug}/experiences' | 'GET /tours/{slug}/itinerary' | 'GET /tours/{slug}/page' | 'GET /tours/{slug}/preview' | 'GET /tours/{slug}/preview/page' | 'GET /tours/{slug}/questions' | 'GET /tours/{slug}/reviews' | 'GET /tours/{slug}/reviews-summary' | 'GET /tours/{slug}/route' | 'GET /travel-insurance/rates' | 'GET /travel-knowledge' | 'GET /travel-status' | 'GET /traveller-stories' | 'POST /booking-intents' | 'POST /brochure-leads' | 'POST /inquiries' | 'POST /marketing-unsubscribes' | 'POST /newsletter/subscriptions' | 'POST /support-requests';
4
4
  export type GeneratedPublicApiActiveOperation = 'GET /bot-protection' | 'GET /destinations' | 'GET /destinations/{slug}' | 'GET /homepage' | 'GET /locales' | 'GET /localization/previews/{candidateId}' | 'GET /localization/releases/{releaseId}' | 'GET /localization/ui-catalogs/{snapshotId}' | 'GET /marketing-unsubscribes' | 'GET /newsletter/confirm' | 'GET /reviews' | 'GET /search' | 'GET /search-suggestions' | 'GET /site-content/about' | 'GET /site-content/accessibility-statement' | 'GET /site-content/booking-cancellation-policy' | 'GET /site-content/contact' | 'GET /site-content/cookie-policy' | 'GET /site-content/destination-iran' | 'GET /site-content/destinations/{country}' | 'GET /site-content/faqs' | 'GET /site-content/footer' | 'GET /site-content/header' | 'GET /site-content/health-safety-support' | 'GET /site-content/home' | 'GET /site-content/iran-travel-status' | 'GET /site-content/iran-visa-information' | 'GET /site-content/money-in-iran' | 'GET /site-content/privacy' | 'GET /site-content/responsible-travel' | 'GET /site-content/tailor-made' | 'GET /site-content/terms' | 'GET /site-content/travel-insurance' | 'GET /site-features' | 'GET /site-navigation' | 'GET /sitemap' | 'GET /tour-categories' | 'GET /tours' | 'GET /tours/{slug}' | 'GET /tours/{slug}/accommodations' | 'GET /tours/{slug}/availability' | 'GET /tours/{slug}/departures' | 'GET /tours/{slug}/experiences' | 'GET /tours/{slug}/itinerary' | 'GET /tours/{slug}/page' | 'GET /tours/{slug}/preview' | 'GET /tours/{slug}/preview/page' | 'GET /tours/{slug}/questions' | 'GET /tours/{slug}/reviews' | 'GET /tours/{slug}/reviews-summary' | 'GET /tours/{slug}/route' | 'GET /travel-insurance/rates' | 'GET /travel-knowledge' | 'GET /travel-status' | 'GET /traveller-stories' | 'POST /booking-intents' | 'POST /brochure-leads' | 'POST /inquiries' | 'POST /marketing-unsubscribes' | 'POST /newsletter/subscriptions' | 'POST /support-requests';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { createPublicApiClient } from './client.js';
2
- export type { PublicApiErrorCode, PublicApiV1Path, PublicApiV1RouteMap, PublicError, PublicErrorEnvelope, PublicApiError, PublicApiNotImplementedError, PublicApiValidationError, PublicApiRateLimitError, PublicApiAbuseBlockedError, PublicTourListItem, PublicTourRegion, PublicTourDetail, PublicTourDetailEditorial, PublicDepartureListItem, PublicAvailabilityStatus, PublicAvailabilityListItem, PublicAvailabilityListResponse, PublicDestinationListItem, PublicCityDestinationContent, PublicCityExperience, PublicCityImage, PublicCityLabelledValue, PublicCityLink, PublicCityNextPlace, PublicCityReason, PublicCityResource, PublicCityStayLink, PublicDestinationDetail, PublicTourListResponse, PublicTourCatalogMetadata, PublicTourCatalogFacetKey, PublicTourCatalogFacetOption, PublicTourCatalogSort, PublicTourCatalogStartingFrom, PublicTourCatalogDepartureSummary, PublicTourDetailResponse, PublicAvailabilityState, PublicTourPageFormat, PublicTourPagePrivateOption, PublicTourPageLeg, PublicTourPageMeal, PublicTourPageKnowSlot, PublicTourPageBadgeTone, PublicTourPageFactKey, PublicTourPageFactIcon, PublicTourPageGalleryKey, PublicTourPageRoomOptionId, PublicTourPageImage, PublicTourPageKeyValue, PublicTourPageBadge, PublicTourPageOpenGraphImage, PublicTourPageByline, PublicTourPageChecked, PublicTourPagePrivateOverride, PublicTourPageFact, PublicTourPageHero, PublicTourPageVideo, PublicTourPageQuote, PublicTourPageGalleryGroup, PublicTourPageExperienceFrame, PublicTourPageExperience, PublicTourPageMap, PublicTourPageStop, PublicTourPageItineraryDay, PublicTourPagePlace, PublicTourPageStay, PublicTourPageTitledBody, PublicTourPageDeparturesEmpty, PublicTourPageMonth, PublicTourPageRoomOption, PublicTourPageRoomPriceByCurrency, PublicTourPageDeparture, PublicTourPagePriceFrom, PublicTourPageReviewSubRating, PublicTourPageReviewReply, PublicTourPageReviewItem, PublicTourPageReviews, PublicTourPageQuestion, PublicTourPageKnowExtra, PublicTourPageCredibility, PublicTourPageOperator, PublicTourPageTailorBand, PublicTourPageBrochure, PublicTourPageRelatedCard, PublicTourPage, PublicTourPageResponse, PublicDepartureListResponse, PublicDestinationListResponse, PublicDestinationDetailResponse, PublicApiResult, ListPublicToursParams, GetPublicTourParams, GetPublicDestinationParams, PublicContentLocale, GetPublicTourPreviewParams, ListPublicDeparturesParams, ListPublicTourAvailabilityParams, ListPublicDestinationsParams, PublicInquiryRequest, PublicTourTailorJourney, PublicInquiryResponse, PublicSupportRequestCategory, PublicSupportRequest, PublicSupportRequestRecaptcha, PublicSupportRequestResponse, PublicMarketingUnsubscribeRequest, PublicMarketingUnsubscribeResponse, PublicNewsletterSubscriptionSource, PublicNewsletterSubscriptionLocale, PublicNewsletterSubscriptionRequest, PublicNewsletterSubscriptionResponse, PublicNewsletterSubscriptionOptions, PublicBookingIntentRequest, PublicBookingIntentData, PublicBookingIntentResponse, PublicHomepageStats, PublicHomepageSeo, PublicHomepageResponse, PublicSitemapEntry, PublicSitemapResponse, PublicNavLink, PublicStaticLink, PublicSiteNavigationResponse, PublicTourCategoryListItem, PublicTourCategoryListResponse, PublicPriceDisplayByCurrency, PublicSearchResultItem, PublicSearchResultGroup, PublicSearchResultsResponse, PublicSearchSuggestion, PublicSearchSuggestionsResponse, PublicSiteRouteKey, PublicSiteCta, PublicSiteHomeSectionType, PublicSiteHomeSection, PublicSiteHomeContent, PublicSiteHomeContentResponse, PublicSiteNavItem, PublicSiteSocialPlatform, PublicSiteSocialLink, PublicSiteHeaderOffice, PublicSiteHeaderMenuCategory, PublicSiteHeaderMenuEditorial, PublicSiteHeaderQuickLink, PublicSiteHeaderSearchShortcuts, PublicSiteFooterNavGroupLink, PublicSiteFooterNavGroup, PublicSiteFooterSupportBand, PublicSiteFooterPaymentNote, PublicSiteChromeContent, PublicSiteChromeContentResponse, PublicSiteFeatureStates, PublicSiteFeaturesResponse, PublicBotCheckErrorCode, PublicBotProtectionRecaptcha, PublicBotProtectionResponse, PublicTravelInsuranceAgeBand, PublicTravelInsuranceDurationBand, PublicTravelInsuranceRates, PublicTravelInsuranceRatesResponse, LocaleTag, LocalizationReleaseId, UiCatalogSnapshotId, CmsContentSnapshotId, PublicLocaleRegistryItem, PublicLocaleRegistryResponse, PublicLocalizationRelease, PublicLocalizationReleaseResponse, PublicUiCatalogMessage, PublicUiCatalog, PublicUiCatalogResponse, PublicSiteTrustSectionType, PublicSiteTrustTitledItem, PublicSiteTrustParagraphGroup, PublicSiteTrustTitledItemList, PublicSiteTrustBulletList, PublicSiteTrustSection, PublicSiteTrustPageSeo, PublicSiteTrustPageContent, PublicSiteTrustPageContentResponse, PublicSiteBlock, PublicSiteBlockPageContent, PublicSiteBlockPageContentResponse, PublicTravelStatusState, PublicTravelStatusFreshness, PublicSurfiranOperationalUpdate, PublicTravelStatusResponse, PublicTravelKnowledgeType, PublicTravelKnowledgeItem, PublicTravelKnowledgeResponse, GetPublicSitemapParams, GetPublicSearchResultsParams, GetPublicSearchSuggestionsParams, GetPublicTravelKnowledgeParams, ListPublicTourCategoriesParams, PublicTravellerStoryListItem, PublicTravellerStoryListResponse, ListPublicTravellerStoriesParams, PublicTourQuestionListItem, PublicTourQuestionListResponse, ListPublicTourQuestionsParams, PublicTourExperienceFrame, PublicTourExperienceListItem, PublicTourExperienceListResponse, ListPublicTourExperiencesParams, PublicTourAccommodationListItem, PublicTourAccommodationListResponse, ListPublicTourAccommodationsParams, PublicTourReviewsAggregate, PublicTourRatingBreakdownItem, PublicTourReviewsSummary, PublicTourReviewsSummaryResponse, PublicTourReviewListItem, PublicTourReviewListResponse, PublicSiteReviewListResponse, ListPublicSiteReviewsParams, PublicTourReviewsLimit, ListPublicTourReviewsParams, PublicTourRouteStop, PublicRouteTransportMode, PublicTourRouteResponse, ListPublicTourRouteParams, PublicItineraryMealCode, PublicTourItineraryDay, PublicTourItineraryResponse, ListPublicTourItineraryParams, PublicBrochureLeadRequest, PublicBrochureLeadResponse, } from './types.js';
2
+ export type { PublicApiErrorCode, PublicApiV1Path, PublicApiV1RouteMap, PublicError, PublicErrorEnvelope, PublicApiError, PublicApiNotImplementedError, PublicApiValidationError, PublicApiRateLimitError, PublicApiAbuseBlockedError, PublicTourListItem, PublicTourRegion, PublicTourDetail, PublicTourDetailEditorial, PublicTourMediaItem, PublicDepartureListItem, PublicAvailabilityStatus, PublicAvailabilityListItem, PublicAvailabilityListResponse, PublicDestinationListItem, PublicCityDestinationContent, PublicCityExperience, PublicCityImage, PublicCityLabelledValue, PublicCityLink, PublicCityNextPlace, PublicCityReason, PublicCityResource, PublicCityStayLink, PublicDestinationDetail, PublicTourListResponse, PublicTourCatalogMetadata, PublicTourCatalogFacetKey, PublicTourCatalogFacetOption, PublicTourCatalogSort, PublicTourCatalogStartingFrom, PublicTourCatalogDepartureSummary, PublicTourDetailResponse, PublicAvailabilityState, PublicTourPageFormat, PublicTourPagePrivateOption, PublicTourPageLeg, PublicTourPageMeal, PublicTourPageKnowSlot, PublicTourPageBadgeTone, PublicTourPageFactKey, PublicTourPageFactIcon, PublicTourPageGalleryKey, PublicTourPageRoomOptionId, PublicTourPageImage, PublicTourPageKeyValue, PublicTourPageBadge, PublicTourPageOpenGraphImage, PublicTourPageByline, PublicTourPageChecked, PublicTourPagePrivateOverride, PublicTourPageFact, PublicTourPageHero, PublicTourPageVideo, PublicTourPageQuote, PublicTourPageGalleryGroup, PublicTourPageExperienceFrame, PublicTourPageExperience, PublicTourPageMap, PublicTourPageStop, PublicTourPageItineraryDay, PublicTourPagePlace, PublicTourPageStay, PublicTourPageTitledBody, PublicTourPageDeparturesEmpty, PublicTourPageMonth, PublicTourPageRoomOption, PublicTourPageRoomPriceByCurrency, PublicTourPageDeparture, PublicTourPagePriceFrom, PublicTourPageReviewSubRating, PublicTourPageReviewReply, PublicTourPageReviewItem, PublicTourPageReviews, PublicTourPageQuestion, PublicTourPageKnowExtra, PublicTourPageCredibility, PublicTourPageOperator, PublicTourPageTailorBand, PublicTourPageBrochure, PublicTourPageRelatedCard, PublicTourPage, PublicTourPageResponse, PublicDepartureListResponse, PublicDestinationListResponse, PublicDestinationDetailResponse, PublicApiResult, ListPublicToursParams, GetPublicTourParams, GetPublicDestinationParams, PublicContentLocale, GetPublicTourPreviewParams, ListPublicDeparturesParams, ListPublicTourAvailabilityParams, ListPublicDestinationsParams, PublicInquiryRequest, PublicTourTailorJourney, PublicInquiryResponse, PublicSupportRequestCategory, PublicSupportRequest, PublicSupportRequestRecaptcha, PublicSupportRequestResponse, PublicMarketingUnsubscribeRequest, PublicMarketingUnsubscribeResponse, PublicNewsletterSubscriptionSource, PublicNewsletterSubscriptionLocale, PublicNewsletterSubscriptionRequest, PublicNewsletterSubscriptionResponse, PublicNewsletterSubscriptionOptions, PublicBookingIntentRequest, PublicBookingIntentData, PublicBookingIntentResponse, PublicHomepageStats, PublicHomepageSeo, PublicHomepageResponse, PublicSitemapEntry, PublicSitemapResponse, PublicNavLink, PublicStaticLink, PublicSiteNavigationResponse, PublicTourCategoryListItem, PublicTourCategoryListResponse, PublicPriceDisplayByCurrency, PublicSearchResultItem, PublicSearchResultGroup, PublicSearchResultsResponse, PublicSearchSuggestion, PublicSearchSuggestionsResponse, PublicSiteRouteKey, PublicSiteCta, PublicSiteHomeSectionType, PublicSiteHomeSection, PublicSiteHomeContent, PublicSiteHomeContentResponse, PublicSiteNavItem, PublicSiteSocialPlatform, PublicSiteSocialLink, PublicSiteHeaderOffice, PublicSiteHeaderMenuCategory, PublicSiteHeaderMenuEditorial, PublicSiteHeaderQuickLink, PublicSiteHeaderSearchShortcuts, PublicSiteFooterNavGroupLink, PublicSiteFooterNavGroup, PublicSiteFooterSupportBand, PublicSiteFooterPaymentNote, PublicSiteChromeContent, PublicSiteChromeContentResponse, PublicSiteFeatureStates, PublicSiteFeaturesResponse, PublicBotCheckErrorCode, PublicBotProtectionRecaptcha, PublicBotProtectionResponse, PublicTravelInsuranceAgeBand, PublicTravelInsuranceDurationBand, PublicTravelInsuranceRates, PublicTravelInsuranceRatesResponse, LocaleTag, LocalizationReleaseId, UiCatalogSnapshotId, CmsContentSnapshotId, PublicLocaleRegistryItem, PublicLocaleRegistryResponse, PublicLocalizationRelease, PublicLocalizationReleaseResponse, PublicUiCatalogMessage, PublicUiCatalog, PublicUiCatalogResponse, PublicSiteTrustSectionType, PublicSiteTrustTitledItem, PublicSiteTrustParagraphGroup, PublicSiteTrustTitledItemList, PublicSiteTrustBulletList, PublicSiteTrustSection, PublicSiteTrustPageSeo, PublicSiteTrustPageContent, PublicSiteTrustPageContentResponse, PublicSiteBlock, PublicSiteBlockPageContent, PublicSiteBlockPageContentResponse, PublicTravelStatusState, PublicTravelStatusFreshness, PublicSurfiranOperationalUpdate, PublicTravelStatusResponse, PublicTravelKnowledgeType, PublicTravelKnowledgeItem, PublicTravelKnowledgeResponse, GetPublicSitemapParams, GetPublicSearchResultsParams, GetPublicSearchSuggestionsParams, GetPublicTravelKnowledgeParams, ListPublicTourCategoriesParams, PublicTravellerStoryListItem, PublicTravellerStoryListResponse, ListPublicTravellerStoriesParams, PublicTourQuestionListItem, PublicTourQuestionListResponse, ListPublicTourQuestionsParams, PublicTourExperienceFrame, PublicTourExperienceListItem, PublicTourExperienceListResponse, ListPublicTourExperiencesParams, PublicTourAccommodationListItem, PublicTourAccommodationListResponse, ListPublicTourAccommodationsParams, PublicTourReviewsAggregate, PublicTourRatingBreakdownItem, PublicTourReviewsSummary, PublicTourReviewsSummaryResponse, PublicTourReviewListItem, PublicTourReviewListResponse, PublicSiteReviewListResponse, ListPublicSiteReviewsParams, PublicTourReviewsLimit, ListPublicTourReviewsParams, PublicTourRouteStop, PublicRouteTransportMode, PublicTourRouteResponse, ListPublicTourRouteParams, PublicItineraryMealCode, PublicTourItineraryDay, PublicTourItineraryResponse, ListPublicTourItineraryParams, PublicBrochureLeadRequest, PublicBrochureLeadResponse, } from './types.js';
3
3
  export type { PublicApiClientOptions } from './client.js';
4
4
  export type { GeneratedPublicApiVersion, GeneratedPublicApiPath, GeneratedPublicApiOperation, GeneratedPublicApiActiveOperation, GeneratedPublicApiBlockedOperation, GeneratedPublicApiOperationId, GeneratedPublicApiSchemaName, } from './generated/public-api-v1.js';
package/dist/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export type PublicApiErrorCode = 'PUBLIC_API_ROUTE_NOT_IMPLEMENTED' | 'PUBLIC_API_INTERNAL_ERROR' | 'PUBLIC_API_TOUR_NOT_FOUND' | 'PUBLIC_API_DESTINATION_NOT_FOUND' | 'PUBLIC_API_INVALID_QUERY' | 'PUBLIC_API_BOOKING_INTENT_VALIDATION_ERROR' | 'PUBLIC_API_IDEMPOTENCY_CONFLICT' | 'PUBLIC_API_RATE_LIMITED' | 'PUBLIC_API_ABUSE_BLOCKED' | 'PUBLIC_API_TRAVEL_STATUS_UNAVAILABLE' | 'PUBLIC_API_SITE_CONTENT_UNAVAILABLE' | 'PUBLIC_API_LOCALIZATION_NOT_FOUND' | 'PUBLIC_API_LOCALIZATION_UNAVAILABLE' | 'PUBLIC_API_CLIENT_TIMEOUT';
2
- export type PublicApiV1Path = '/homepage' | '/site-navigation' | '/site-features' | '/locales' | '/localization/previews/{candidateId}' | '/localization/releases/{releaseId}' | '/localization/ui-catalogs/{snapshotId}' | '/sitemap' | '/search-suggestions' | '/tour-categories' | '/tours' | '/tours/{slug}' | '/tours/{slug}/preview' | '/tours/{slug}/departures' | '/tours/{slug}/availability' | '/tours/{slug}/reviews' | '/destinations' | '/destinations/{slug}' | '/inquiries' | '/booking-intents' | '/travel-status' | '/travel-knowledge' | '/site-content/home' | '/site-content/header' | '/site-content/footer' | '/site-content/about' | '/site-content/contact' | '/site-content/tailor-made' | '/site-content/faqs' | '/site-content/privacy' | '/site-content/terms' | '/site-content/responsible-travel' | '/site-content/iran-travel-status' | '/site-content/iran-visa-information' | '/site-content/money-in-iran' | '/site-content/travel-insurance' | '/site-content/health-safety-support' | '/site-content/booking-cancellation-policy' | '/site-content/cookie-policy' | '/site-content/accessibility-statement' | '/site-content/destination-iran';
2
+ export type PublicApiV1Path = '/homepage' | '/site-navigation' | '/site-features' | '/bot-protection' | '/travel-insurance/rates' | '/locales' | '/localization/previews/{candidateId}' | '/localization/releases/{releaseId}' | '/localization/ui-catalogs/{snapshotId}' | '/sitemap' | '/search' | '/search-suggestions' | '/tour-categories' | '/tours' | '/tours/{slug}' | '/tours/{slug}/preview' | '/tours/{slug}/page' | '/tours/{slug}/preview/page' | '/tours/{slug}/departures' | '/tours/{slug}/availability' | '/tours/{slug}/reviews' | '/destinations' | '/destinations/{slug}' | '/inquiries' | '/support-requests' | '/marketing-unsubscribes' | '/newsletter/subscriptions' | '/booking-intents' | '/brochure-leads' | '/travel-status' | '/travel-knowledge' | '/site-content/home' | '/site-content/header' | '/site-content/footer' | '/site-content/about' | '/site-content/contact' | '/site-content/tailor-made' | '/site-content/faqs' | '/site-content/privacy' | '/site-content/terms' | '/site-content/responsible-travel' | '/site-content/iran-travel-status' | '/site-content/iran-visa-information' | '/site-content/money-in-iran' | '/site-content/travel-insurance' | '/site-content/health-safety-support' | '/site-content/booking-cancellation-policy' | '/site-content/cookie-policy' | '/site-content/accessibility-statement' | '/site-content/destination-iran';
3
3
  export type PublicError = {
4
4
  code: string;
5
5
  message: string;
6
6
  status: number;
7
- requestId: string | null;
7
+ requestId?: string | null;
8
8
  /**
9
9
  * Optional public-safe diagnostic object (contract schema PublicError.details).
10
10
  * Shape varies by error code — e.g. the destination-not-found error may
@@ -78,10 +78,24 @@ export type PublicTourListItem = {
78
78
  id: number;
79
79
  slug: string;
80
80
  title: string;
81
- summary: string | null;
81
+ summary?: string | null;
82
+ /** Card excerpt. Optional: older responses omit it. */
83
+ excerpt?: string | null;
82
84
  durationDays: number | null;
83
85
  durationNights: number | null;
86
+ /** Finished duration label. Optional: older responses omit it. */
87
+ durationDisplay?: string | null;
88
+ /**
89
+ * Published fixed starting price only. Null when this tour has no fixed
90
+ * public price; Private tours may instead carry `priceOnRequestLabel`.
91
+ */
84
92
  startingPriceDisplay: string | null;
93
+ /**
94
+ * Owner-approved controlled on-request price label for a Private tour with
95
+ * no fixed public price. Never a raw pricing row, amount, commercial
96
+ * calculation, or internal note. Optional: older responses omit it.
97
+ */
98
+ priceOnRequestLabel?: string | null;
85
99
  currency: string | null;
86
100
  /**
87
101
  * The same published starting price rendered in each display currency the
@@ -92,9 +106,13 @@ export type PublicTourListItem = {
92
106
  * A currency with no active rate for this price's own currency is `null`, and
93
107
  * the consumer falls back to `startingPriceDisplay` as published.
94
108
  */
95
- priceDisplayByCurrency: PublicPriceDisplayByCurrency;
109
+ priceDisplayByCurrency?: PublicPriceDisplayByCurrency;
96
110
  heroImage: string | null;
111
+ /** Public hero image URL. Optional: older responses omit it. */
112
+ heroImageUrl?: string | null;
97
113
  primaryDestinations: string[];
114
+ /** Display label for the tour's destinations. Optional: older responses omit it. */
115
+ destinationLabel?: string | null;
98
116
  /**
99
117
  * Public trip-theme labels derived from `tour_packages.style` (for example
100
118
  * "Cultural", "Family"). Already served by `GET /api/v1/tours`; declared
@@ -185,18 +203,41 @@ export type PublicTourListItem = {
185
203
  * provenance and null semantics as `startCity`.
186
204
  */
187
205
  finishCity?: string | null;
206
+ /**
207
+ * The same verified external review aggregate GET /tours/{slug}/reviews-summary
208
+ * publishes for this tour, from the same projection, so a catalogue card and
209
+ * the tour page never disagree. Null when the tour has no visible source
210
+ * aggregate and no visible review rows: render nothing rather than a zero
211
+ * rating or an invented count. Optional: older responses omit it.
212
+ */
213
+ reviewAggregate?: PublicTourReviewsAggregate | null;
214
+ /** Canonical public page URL. Optional: older responses omit it. */
215
+ publicUrl?: string;
216
+ /** Call-to-action URL. Optional: older responses omit it. */
217
+ ctaUrl?: string;
188
218
  status: string;
189
- updatedAt: string | null;
219
+ updatedAt?: string | null;
220
+ /**
221
+ * Public-safe SEO projection. The contract requires it; optional here so
222
+ * consumers stay compatible with servers predating the seo projection, as on
223
+ * PublicDestinationListItem.
224
+ */
225
+ seo?: PublicSeo;
190
226
  };
191
227
  export type PublicTourDetail = PublicTourListItem & {
192
- description: string | null;
228
+ description?: string | null;
193
229
  route: string[];
194
- highlights: string[];
195
- includedItems: string[];
196
- excludedItems: string[];
230
+ highlights?: string[];
231
+ includedItems?: string[];
232
+ excludedItems?: string[];
197
233
  gallery: string[];
198
- seoTitle: string | null;
199
- seoDescription: string | null;
234
+ /**
235
+ * Published tour photographs with authored alt text, grouping and focal
236
+ * point. Optional: older responses omit it.
237
+ */
238
+ media?: PublicTourMediaItem[];
239
+ seoTitle?: string | null;
240
+ seoDescription?: string | null;
200
241
  editorial?: PublicTourDetailEditorial | null;
201
242
  /**
202
243
  * Operator-configured "Tailor This Journey" call to action.
@@ -214,6 +255,34 @@ export type PublicTourDetail = PublicTourListItem & {
214
255
  /** Operator-curated related tours (PR-13 / OA-9), in the pinned order. Empty when none are pinned/published. */
215
256
  relatedJourneys?: PublicTourListItem[];
216
257
  };
258
+ /** One published tour photograph (contract schema PublicTourMediaItem). */
259
+ export type PublicTourMediaItem = {
260
+ url: string;
261
+ /**
262
+ * Alternative text authored against this asset. An empty string marks a
263
+ * decorative photograph, a deliberate published state, not missing data.
264
+ */
265
+ alt: string;
266
+ /** Operator-authored caption, or null when none. */
267
+ caption: string | null;
268
+ /**
269
+ * Gallery tab this photograph belongs under. Null means ungrouped: it appears
270
+ * under All photos only and never invents a tab of its own.
271
+ */
272
+ group: 'route' | 'people' | 'activities' | 'stay' | 'transport' | null;
273
+ /**
274
+ * 0-1 pair a renderer turns into object-position. Null means no focal point
275
+ * was authored; a null axis means centre on that axis.
276
+ */
277
+ focalPoint: {
278
+ x: number | null;
279
+ y: number | null;
280
+ } | null;
281
+ /** True for the tour listing photograph. Exactly one per tour. */
282
+ isLead: boolean;
283
+ /** The operator's own ordering position within this tour's media. */
284
+ order: number;
285
+ };
217
286
  export type PublicTourTailorJourney = {
218
287
  enabled: boolean;
219
288
  /** Canonical public slug of the source tour, for inquiry attribution. Present only when enabled. */
@@ -225,24 +294,29 @@ export type PublicTourTailorJourney = {
225
294
  /** Published customisation prompts, in the operator's authored order. */
226
295
  suggestedCustomizationOptions?: string[];
227
296
  };
297
+ /**
298
+ * Operator-authored tour page editorial. Every field and sub-field is optional,
299
+ * as in the contract (client 0.38.0 aligned them): render nothing for an absent
300
+ * value rather than assume the projection sent it.
301
+ */
228
302
  export type PublicTourDetailEditorial = {
229
- tourType: string | null;
230
- tourCode: string | null;
231
- breadcrumbTitle: string | null;
232
- authorName: string | null;
233
- authorUrl: string | null;
234
- authorRole: string | null;
235
- lastReviewedAt: string | null;
236
- guideLabel: string | null;
237
- groupSizeNote: string | null;
238
- paceLabel: string | null;
239
- paceNote: string | null;
240
- comfortLabel: string | null;
241
- comfortNote: string | null;
242
- guideNote: string | null;
243
- startLabel: string | null;
244
- endLabel: string | null;
245
- startEndNote: string | null;
303
+ tourType?: string | null;
304
+ tourCode?: string | null;
305
+ breadcrumbTitle?: string | null;
306
+ authorName?: string | null;
307
+ authorUrl?: string | null;
308
+ authorRole?: string | null;
309
+ lastReviewedAt?: string | null;
310
+ guideLabel?: string | null;
311
+ groupSizeNote?: string | null;
312
+ paceLabel?: string | null;
313
+ paceNote?: string | null;
314
+ comfortLabel?: string | null;
315
+ comfortNote?: string | null;
316
+ guideNote?: string | null;
317
+ startLabel?: string | null;
318
+ endLabel?: string | null;
319
+ startEndNote?: string | null;
246
320
  /** Operator-authored prose above the route map; null when none is stored (contract 0.23.0). */
247
321
  routeIntro?: string | null;
248
322
  /** Note under the included / not included lists; null when none is stored (contract 0.23.0). */
@@ -254,45 +328,65 @@ export type PublicTourDetailEditorial = {
254
328
  arrivesByAir?: boolean;
255
329
  /** As arrivesByAir, for leaving by air from the last stop (contract 0.26.0). */
256
330
  departsByAir?: boolean;
257
- overviewParagraphs: string[];
258
- heroAltText: string | null;
259
- heroObjectPosition: string | null;
260
- galleryAltTexts: string[];
261
- galleryObjectPositions: string[];
262
- heroVideo: {
263
- url: string;
264
- posterUrl: string | null;
265
- durationLabel: string | null;
331
+ /**
332
+ * Private-version product state: `exact` = the published itinerary can run
333
+ * privately as shown, `similar` = only a similar private journey designed
334
+ * from it, `none` = scheduled group departures only. Absent or null means no
335
+ * state is published and a consumer renders no private call to action. Never
336
+ * carries a price; a private trip is always priced on request.
337
+ */
338
+ privateOption?: PublicTourPagePrivateOption | null;
339
+ /**
340
+ * The visa benefit this tour sells. Absent, null or mode `pending` means
341
+ * nothing is published and a consumer renders no visa line. Never carries a
342
+ * processing time, an eligibility promise, or an approval claim.
343
+ */
344
+ visa?: {
345
+ mode: 'included' | 'advice' | 'optional' | 'pending';
346
+ /** Whether the Iranian authorisation code is included. Null when the tour does not state it. */
347
+ authCodeIncluded?: boolean | null;
348
+ /** The separately payable government or consular fee, in the tour's own published words. */
349
+ feeNote?: string | null;
350
+ } | null;
351
+ overviewParagraphs?: string[];
352
+ heroAltText?: string | null;
353
+ heroObjectPosition?: string | null;
354
+ galleryAltTexts?: string[];
355
+ galleryObjectPositions?: string[];
356
+ heroVideo?: {
357
+ url?: string;
358
+ posterUrl?: string | null;
359
+ durationLabel?: string | null;
266
360
  } | null;
267
- heroQuote: {
268
- text: string;
269
- authorName: string | null;
270
- travelDate: string | null;
271
- sourceUrl: string | null;
361
+ heroQuote?: {
362
+ text?: string;
363
+ authorName?: string | null;
364
+ travelDate?: string | null;
365
+ sourceUrl?: string | null;
272
366
  } | null;
273
367
  payment?: {
274
- depositSummary: string | null;
275
- balanceSummary: string | null;
276
- cancellationSummary: string | null;
277
- paymentSummary: string | null;
368
+ depositSummary?: string | null;
369
+ balanceSummary?: string | null;
370
+ cancellationSummary?: string | null;
371
+ paymentSummary?: string | null;
278
372
  };
279
373
  operator?: {
280
- body: string | null;
281
- imageUrl: string | null;
282
- aboutUrl: string | null;
283
- licenceLabel: string | null;
284
- officeLabel: string | null;
285
- metrics: Array<{
286
- value: string;
287
- label: string;
374
+ body?: string | null;
375
+ imageUrl?: string | null;
376
+ aboutUrl?: string | null;
377
+ licenceLabel?: string | null;
378
+ officeLabel?: string | null;
379
+ metrics?: Array<{
380
+ value?: string;
381
+ label?: string;
288
382
  }>;
289
383
  };
290
- goodToKnow: Array<{
291
- title: string;
292
- body: string;
293
- ctaLabel: string | null;
294
- href: string | null;
295
- icon: 'info' | 'card' | 'shield' | 'globe' | 'people' | 'route' | null;
384
+ goodToKnow?: Array<{
385
+ title?: string;
386
+ body?: string;
387
+ ctaLabel?: string | null;
388
+ href?: string | null;
389
+ icon?: 'info' | 'card' | 'shield' | 'globe' | 'people' | 'route' | null;
296
390
  }>;
297
391
  };
298
392
  export type PublicDepartureListItem = {
@@ -301,21 +395,21 @@ export type PublicDepartureListItem = {
301
395
  startDate: string | null;
302
396
  endDate: string | null;
303
397
  availabilityStatus: 'Scheduled' | 'Guaranteed' | 'Sold Out';
304
- priceDisplay: string | null;
398
+ priceDisplay?: string | null;
305
399
  currency: string | null;
306
- bookingStatus: string | null;
400
+ bookingStatus?: string | null;
307
401
  state: PublicAvailabilityState;
308
402
  /** Places left, published only for the limited state (1 to 4); otherwise null. */
309
403
  left: number | null;
310
404
  };
311
405
  export type PublicTourQuestionListItem = {
312
406
  id: number;
313
- askerName: string | null;
314
- askedAt: string | null;
407
+ askerName?: string | null;
408
+ askedAt?: string | null;
315
409
  question: string;
316
410
  answer: string;
317
- answeredByName: string | null;
318
- answeredByRole: string | null;
411
+ answeredByName?: string | null;
412
+ answeredByRole?: string | null;
319
413
  answeredAt: string;
320
414
  /** Computed from (answeredAt - askedAt) at read time. Never a stored value. */
321
415
  answerTimeHours: number;
@@ -461,6 +555,11 @@ export type PublicAvailabilityListItem = {
461
555
  left: number | null;
462
556
  currency: string | null;
463
557
  priceDisplay: string | null;
558
+ /**
559
+ * Owner-approved controlled on-request price label when there is no fixed
560
+ * public price. Optional: older responses omit it.
561
+ */
562
+ priceOnRequestLabel?: string | null;
464
563
  priceDisplayByCurrency: PublicPriceDisplayByCurrency;
465
564
  promoLabel?: string | null;
466
565
  publicNote?: string | null;
@@ -535,8 +634,20 @@ export type PublicDestinationListItem = {
535
634
  id: number;
536
635
  slug: string;
537
636
  name: string;
637
+ /** Display title. Optional: older responses omit it. */
638
+ title?: string;
538
639
  summary: string | null;
539
- heroImage: string | null;
640
+ /** Card excerpt. Optional: older responses omit it. */
641
+ excerpt?: string | null;
642
+ heroImage?: string | null;
643
+ /** Public hero image URL. Optional: older responses omit it. */
644
+ heroImageUrl?: string | null;
645
+ /** Display label. Optional: older responses omit it. */
646
+ destinationLabel?: string;
647
+ /** Canonical public page URL. Optional: older responses omit it. */
648
+ publicUrl?: string;
649
+ /** Call-to-action URL. Optional: older responses omit it. */
650
+ ctaUrl?: string;
540
651
  updatedAt: string | null;
541
652
  /** Optional so consumers stay compatible with servers predating the seo projection. */
542
653
  seo?: PublicSeo;
@@ -645,9 +756,9 @@ export type PublicCityDestinationContent = {
645
756
  closingBody?: string | null;
646
757
  };
647
758
  export type PublicDestinationDetail = PublicDestinationListItem & {
648
- description: string | null;
649
- seoTitle: string | null;
650
- seoDescription: string | null;
759
+ description?: string | null;
760
+ seoTitle?: string | null;
761
+ seoDescription?: string | null;
651
762
  /** Operator-authored JSON-LD overrides from the published place profile. */
652
763
  structuredDataOverrides?: Record<string, unknown> | null;
653
764
  /** Absent for a legacy row, an unpublished profile, or a database without the City schema. */
@@ -1711,6 +1822,12 @@ export type PublicSiteTrustPageContent = {
1711
1822
  cta: PublicSiteCta | null;
1712
1823
  sections: PublicSiteTrustSection[];
1713
1824
  seo: PublicSiteTrustPageSeo;
1825
+ /**
1826
+ * Published registry-driven block composition for this page, when one
1827
+ * exists. Additive and optional: a consumer that predates blocks is
1828
+ * unaffected, and a page with no composition is unchanged.
1829
+ */
1830
+ blocks?: PublicSiteBlock[];
1714
1831
  };
1715
1832
  export type PublicSiteTrustPageContentResponse = {
1716
1833
  data: PublicSiteTrustPageContent | null;
package/dist/types.js CHANGED
@@ -1,3 +1,7 @@
1
- // Hand-maintained public API types; generated contract types live in src/generated/public-api-v1.ts.
1
+ // Hand-maintained public API types. Generated from the contract snapshot:
2
+ // src/generated/public-api-v1.ts (path, operation and schema name lists, published)
3
+ // and src/generated/public-api-v1.schema.ts (full openapi-typescript types, not
4
+ // published). test/types-conformance.ts checks every type here whose name matches
5
+ // a contract schema against the generated one; known deviations are allowlisted there.
2
6
  // Zero runtime code. Types only.
3
7
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@surfiran/public-api-client",
3
- "version": "0.37.0",
3
+ "version": "0.38.0",
4
4
  "description": "TypeScript types and client for SURFIRAN CRM Public API V1",
5
5
  "type": "module",
6
6
  "engines": {
@@ -45,9 +45,9 @@
45
45
  ],
46
46
  "scripts": {
47
47
  "build": "node scripts/build.cjs",
48
- "generate": "node scripts/generate-public-api-v1.cjs",
48
+ "generate": "node scripts/generate-public-api-v1.cjs && node scripts/generate-public-api-v1-schema.cjs",
49
49
  "prepack": "npm run verify:contract && npm run verify:package && npm run verify:repository && npm run verify:generated && npm run typecheck && npm test && npm run build",
50
- "typecheck": "tsc --noEmit",
50
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.conformance.json",
51
51
  "test": "vitest run",
52
52
  "verify:contract": "node scripts/verify-contract.cjs",
53
53
  "verify:api-contract-artifact": "node scripts/verify-api-contract-artifact.cjs",
@@ -63,6 +63,7 @@
63
63
  },
64
64
  "dependencies": {},
65
65
  "devDependencies": {
66
+ "openapi-typescript": "7.13.0",
66
67
  "typescript": "^5.2.2",
67
68
  "vitest": "^4.1.8"
68
69
  }