@bacano/sdk 1.1.13 → 1.1.17

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/index.d.ts CHANGED
@@ -7,4 +7,4 @@ export type { MediaResponsiveSource, MediaResponsiveSourceOptions, MediaTransfor
7
7
  export { buildStorageContentMediaUrls, buildStorageFileUrl, buildStorageMediaUrls, buildStorageResponsiveSources, buildStorageTransformedFileUrl, formatMediaSrcSet, } from "./media";
8
8
  export { getWebsiteContextFromPublicConfig, getWebsitePublicConfig, } from "./modules/auth";
9
9
  export { getPrimaryProductImage, getProductDetailVariants, getProductImages, getProductMediaByKind, getProductMediaKind, getProductVideos, resolveProductPricing, } from "./modules/catalog";
10
- export type { AccessTokenProvider, AttributeFilter, BacanoClientConfig, BacanoClientContext, Brand, Cart, CartItem, CatalogAttributeList, CatalogAttributeListFilters, CatalogAttributeListItem, CatalogAttributeListValue, CatalogCategoryList, CatalogCategoryListItem, CatalogCategoryListItemSourceType, CheckoutResult, CheckoutSubmission, ContentEntry, ContentJson, ContentMedia, ContentTypeSummary, CustomerAddress, CustomerAddressType, CustomerProfile, CustomerProfileUpdate, GetAttributeListOptions, GetCategoryListOptions, GetContentOptions, GetOrdersOptions, GetProductFiltersOptions, GetProductSeoEntriesOptions, GetProductsOptions, GetSearchSuggestionsOptions, GetStaticCatalogBootstrapOptions, GetStaticCatalogSnapshotOptions, GetWebsitePublicConfigOptions, Order, OrderItem, Product, ProductAttribute, ProductAvailability, ProductCategory, ProductContentBlock, ProductFilters, ProductMedia, ProductMediaKind, ProductOptionGroup, ProductOptionGroupMember, ProductPrice, ProductPriceType, ProductRelatedProduct, ProductSeoEntriesResult, ProductSeoEntry, ProductSortBy, ProductSpecification, ProductSpecificationFacet, ProductSpecificationField, ProductSpecificationFilterSelection, ProductSpecificationMeasure, ProductSpecificationOption, ProductSpecificationSection, ProductSubcategory, ProductVariant, ProductVariantLiveState, ProductVariantPricing, PublicOrder, PublicOrderDeliverySummary, PublicOrderItem, PublicOrderLookupInput, PublicOrderPaymentSummary, ResolvedWebsitePublicConfig, SearchSuggestion, StartGuestCheckoutInput, StaticCatalogBootstrap, StaticCatalogSnapshot, VariantAttribute, WaitForPublicOrderPaymentInput, WebsiteBrandingMedia, WebsiteBrandingPublicConfig, WebsiteCampaignCaptureContact, WebsiteCampaignCaptureInput, WebsiteCampaignCaptureResult, WebsiteCampaignCaptureSource, WebsiteCheckoutAddress, WebsiteCheckoutItem, WebsiteCouponActionResult, WebsiteDeliveryOption, WebsiteDepartmentOption, WebsiteFreeShippingProgress, WebsiteFulfillmentFamily, WebsiteGuestContact, WebsiteLegalPublicConfig, WebsiteLocationOptions, WebsiteMunicipalityOption, WebsitePaymentMethod, WebsitePaymentMode, WebsitePaymentOption, WebsitePaymentOptionMethod, WebsitePaymentSessionResult, WebsitePickupInfo, WebsitePublicConfig, WebsiteShippingPolicy, WebsiteShippingQuote, } from "./types";
10
+ export type { AccessTokenProvider, AttributeFilter, BacanoClientConfig, BacanoClientContext, Brand, Cart, CartItem, CatalogAttributeList, CatalogAttributeListFilters, CatalogAttributeListItem, CatalogAttributeListValue, CatalogCategoryList, CatalogCategoryListItem, CatalogCategoryListItemSourceType, CheckoutResult, CheckoutSubmission, ContentEntry, ContentJson, ContentMedia, ContentTypeSummary, CustomerAddress, CustomerAddressType, CustomerProfile, CustomerProfileUpdate, GetAttributeListOptions, GetCategoryListOptions, GetContentOptions, GetOrdersOptions, GetProductFiltersOptions, GetProductSeoEntriesOptions, GetProductsOptions, GetSearchSuggestionsOptions, GetStaticCatalogBootstrapOptions, GetStaticCatalogSnapshotOptions, GetWebsitePublicConfigOptions, Order, OrderItem, Product, ProductAttribute, ProductAvailability, ProductCategory, ProductContentBlock, ProductFilters, ProductMedia, ProductMediaKind, ProductOptionGroup, ProductOptionGroupMember, ProductPrice, ProductPriceType, ProductRelatedProduct, ProductSeoEntriesResult, ProductSeoEntry, ProductSortBy, ProductSpecification, ProductSpecificationFacet, ProductSpecificationField, ProductSpecificationFilterSelection, ProductSpecificationMeasure, ProductSpecificationOption, ProductSpecificationSection, ProductSubcategory, ProductVariant, ProductVariantLiveState, ProductVariantPricing, PublicOrder, PublicOrderDeliverySummary, PublicOrderItem, PublicOrderLookupInput, PublicOrderPaymentSummary, ResolvedWebsitePublicConfig, SearchSuggestion, StartGuestCheckoutInput, StaticCatalogBootstrap, StaticCatalogSnapshot, VariantAttribute, WaitForPublicOrderPaymentInput, WebsiteBrandingMedia, WebsiteBrandingPublicConfig, WebsiteCampaignCaptureContact, WebsiteCampaignCaptureInput, WebsiteCampaignCaptureResult, WebsiteCampaignCaptureSource, WebsiteCheckoutAddress, WebsiteCheckoutItem, WebsiteCouponActionResult, WebsiteDeliveryOption, WebsiteDepartmentOption, WebsiteFormAction, WebsiteFormDefinition, WebsiteFormField, WebsiteFormFieldType, WebsiteFormOption, WebsiteFormOptionSource, WebsiteFormResponseInput, WebsiteFormResponseResult, WebsiteFormResponseSource, WebsiteFormResponseValue, WebsiteFormSection, WebsiteFormStatus, WebsiteFormStep, WebsiteFreeShippingProgress, WebsiteFulfillmentFamily, WebsiteGuestContact, WebsiteLegalPublicConfig, WebsiteLocationOptions, WebsiteMunicipalityOption, WebsitePaymentMethod, WebsitePaymentMode, WebsitePaymentOption, WebsitePaymentOptionMethod, WebsitePaymentSessionResult, WebsitePickupInfo, WebsitePublicConfig, WebsitePublishedForm, WebsiteShippingPolicy, WebsiteShippingQuote, } from "./types";
package/index.js CHANGED
@@ -12,10 +12,10 @@ var WebsiteError = class extends Error {
12
12
  }
13
13
  };
14
14
  var RateLimitError = class extends WebsiteError {
15
- constructor(message, retryAfterSeconds) {
15
+ constructor(message, retryAfterSeconds2) {
16
16
  super(message, "RATE_LIMITED", {
17
17
  status: 429,
18
- retryAfterSeconds
18
+ retryAfterSeconds: retryAfterSeconds2
19
19
  });
20
20
  this.name = "RateLimitError";
21
21
  }
@@ -6061,6 +6061,171 @@ function createWebsiteMarketingRuntime(options) {
6061
6061
  };
6062
6062
  }
6063
6063
 
6064
+ // libs/sdk/src/modules/forms.ts
6065
+ function retryAfterSeconds(response) {
6066
+ const value = Number(response.headers.get("Retry-After") ?? "1");
6067
+ return Number.isFinite(value) && value >= 0 ? Math.ceil(value) : 1;
6068
+ }
6069
+ function isFormKey(value) {
6070
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
6071
+ }
6072
+ function createFormsModule(apiUrl, websiteSlug, getContext) {
6073
+ const cache = /* @__PURE__ */ new Map();
6074
+ function getServerOrigin() {
6075
+ if (typeof window !== "undefined") return null;
6076
+ const domain = getContext().domain?.trim();
6077
+ if (!domain) return null;
6078
+ try {
6079
+ return new URL(domain.includes("://") ? domain : `https://${domain}`).origin;
6080
+ } catch {
6081
+ return null;
6082
+ }
6083
+ }
6084
+ return {
6085
+ async getDefinition(formKey, options) {
6086
+ const normalizedKey = formKey.trim();
6087
+ if (!isFormKey(normalizedKey)) {
6088
+ throw new WebsiteError(
6089
+ "formKey has an invalid format",
6090
+ "INVALID_FORM_KEY"
6091
+ );
6092
+ }
6093
+ const cached = cache.get(normalizedKey);
6094
+ if (!options?.force && cached && cached.expiresAt > Date.now()) {
6095
+ return cached.value;
6096
+ }
6097
+ const url = new URL(
6098
+ `${apiUrl.replace(/\/+$/, "")}/api/v1/website/forms/definition`
6099
+ );
6100
+ url.searchParams.set("websiteSlug", websiteSlug);
6101
+ url.searchParams.set("formKey", normalizedKey);
6102
+ let response;
6103
+ try {
6104
+ const serverOrigin = getServerOrigin();
6105
+ const headers = new Headers();
6106
+ if (serverOrigin) headers.set("Origin", serverOrigin);
6107
+ if (cached?.etag) headers.set("If-None-Match", cached.etag);
6108
+ response = await fetch(url, {
6109
+ ...serverOrigin || cached?.etag ? { headers } : {},
6110
+ ...options?.force ? { cache: "no-cache" } : {}
6111
+ });
6112
+ } catch (error) {
6113
+ throw new WebsiteError(
6114
+ `Could not load the website form: ${error instanceof Error ? error.message : String(error)}`,
6115
+ "NETWORK_ERROR"
6116
+ );
6117
+ }
6118
+ if (response.status === 304 && cached) {
6119
+ cache.set(normalizedKey, {
6120
+ ...cached,
6121
+ expiresAt: Date.now() + 3e4
6122
+ });
6123
+ return cached.value;
6124
+ }
6125
+ if (response.status === 404) {
6126
+ cache.set(normalizedKey, {
6127
+ etag: null,
6128
+ expiresAt: Date.now() + 15e3,
6129
+ value: null
6130
+ });
6131
+ return null;
6132
+ }
6133
+ if (response.status === 429) {
6134
+ throw new RateLimitError(
6135
+ "Rate limit exceeded. Please try again later.",
6136
+ retryAfterSeconds(response)
6137
+ );
6138
+ }
6139
+ if (!response.ok) {
6140
+ throw new WebsiteError(
6141
+ `Could not load the website form (status ${response.status})`,
6142
+ response.status === 403 ? "ORIGIN_NOT_ALLOWED" : "FORM_DEFINITION_ERROR",
6143
+ { status: response.status }
6144
+ );
6145
+ }
6146
+ const value = await response.json();
6147
+ if (value.schemaVersion !== 1 || value.key !== normalizedKey || !Array.isArray(value.definition?.fields) || !Array.isArray(value.definition?.sections) || !Array.isArray(value.definition?.steps)) {
6148
+ throw new WebsiteError(
6149
+ "Website form returned an invalid definition",
6150
+ "INVALID_FORM_DEFINITION"
6151
+ );
6152
+ }
6153
+ cache.set(normalizedKey, {
6154
+ etag: response.headers.get("ETag"),
6155
+ expiresAt: Date.now() + 3e4,
6156
+ value
6157
+ });
6158
+ return value;
6159
+ },
6160
+ async submitResponse(input) {
6161
+ const normalizedKey = input.formKey.trim();
6162
+ if (!isFormKey(normalizedKey)) {
6163
+ throw new WebsiteError(
6164
+ "formKey has an invalid format",
6165
+ "INVALID_FORM_KEY"
6166
+ );
6167
+ }
6168
+ const context = getContext();
6169
+ const attribution = getWebsiteMarketingAttribution(context.websiteId);
6170
+ const pageUrl = typeof window !== "undefined" ? window.location.href : void 0;
6171
+ const referrerUrl = typeof document !== "undefined" && document.referrer ? document.referrer : void 0;
6172
+ let response;
6173
+ try {
6174
+ response = await fetch(
6175
+ `${apiUrl.replace(/\/+$/, "")}/api/v1/website/forms/responses`,
6176
+ {
6177
+ method: "POST",
6178
+ headers: { "Content-Type": "application/json" },
6179
+ body: JSON.stringify({
6180
+ websiteSlug,
6181
+ formKey: normalizedKey,
6182
+ formVersion: input.formVersion,
6183
+ definitionHash: input.definitionHash,
6184
+ idempotencyKey: input.idempotencyKey,
6185
+ source: input.source ?? "WEBSITE",
6186
+ values: input.values,
6187
+ pageUrl,
6188
+ referrerUrl,
6189
+ attribution: attribution ? serializeWebsiteMarketingAttribution(attribution) : null,
6190
+ honeypot: input.honeypot ?? ""
6191
+ })
6192
+ }
6193
+ );
6194
+ } catch (error) {
6195
+ throw new WebsiteError(
6196
+ `Could not submit the website form: ${error instanceof Error ? error.message : String(error)}`,
6197
+ "NETWORK_ERROR"
6198
+ );
6199
+ }
6200
+ if (response.status === 429) {
6201
+ throw new RateLimitError(
6202
+ "Rate limit exceeded. Please try again later.",
6203
+ retryAfterSeconds(response)
6204
+ );
6205
+ }
6206
+ const result = await response.json().catch(() => null);
6207
+ if (!response.ok) {
6208
+ throw new WebsiteError(
6209
+ result?.message ?? `Could not submit the website form (status ${response.status})`,
6210
+ result?.code ?? (response.status === 403 ? "ORIGIN_NOT_ALLOWED" : "FORM_RESPONSE_ERROR"),
6211
+ { status: response.status }
6212
+ );
6213
+ }
6214
+ if (typeof result?.accepted !== "boolean" || typeof result.message !== "string") {
6215
+ throw new WebsiteError(
6216
+ "Website form returned an invalid response",
6217
+ "INVALID_RESPONSE"
6218
+ );
6219
+ }
6220
+ return {
6221
+ accepted: result.accepted,
6222
+ responseId: typeof result.responseId === "string" ? result.responseId : null,
6223
+ message: result.message
6224
+ };
6225
+ }
6226
+ };
6227
+ }
6228
+
6064
6229
  // libs/sdk/src/modules/marketing.ts
6065
6230
  function readRetryAfter(response) {
6066
6231
  const value = Number(response.headers.get("Retry-After") ?? "1");
@@ -6587,6 +6752,7 @@ function createBacanoClient(config) {
6587
6752
  config.websiteSlug,
6588
6753
  getContext
6589
6754
  );
6755
+ const formsModule = createFormsModule(apiUrl, config.websiteSlug, getContext);
6590
6756
  return {
6591
6757
  async init() {
6592
6758
  if (context) return;
@@ -6639,6 +6805,10 @@ function createBacanoClient(config) {
6639
6805
  getByTag: contentModule.getByTag.bind(contentModule),
6640
6806
  getForProduct: contentModule.getForProduct.bind(contentModule)
6641
6807
  },
6808
+ forms: {
6809
+ getDefinition: formsModule.getDefinition.bind(formsModule),
6810
+ submitResponse: formsModule.submitResponse.bind(formsModule)
6811
+ },
6642
6812
  cart: {
6643
6813
  getOrCreate: cartModule.getOrCreate.bind(cartModule),
6644
6814
  startCheckout: cartModule.startCheckout.bind(cartModule),
@@ -0,0 +1,9 @@
1
+ import type { BacanoClientContext, WebsiteFormResponseInput, WebsiteFormResponseResult, WebsitePublishedForm } from "../types";
2
+ export interface GetWebsiteFormOptions {
3
+ force?: boolean;
4
+ }
5
+ export interface FormsModule {
6
+ getDefinition(formKey: string, options?: GetWebsiteFormOptions): Promise<WebsitePublishedForm | null>;
7
+ submitResponse(input: WebsiteFormResponseInput): Promise<WebsiteFormResponseResult>;
8
+ }
9
+ export declare function createFormsModule(apiUrl: string, websiteSlug: string, getContext: () => BacanoClientContext): FormsModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacano/sdk",
3
- "version": "1.1.13",
3
+ "version": "1.1.17",
4
4
  "description": "TypeScript SDK for building websites on the Bacano ERP",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.js",
@@ -37,6 +37,11 @@
37
37
  "types": "./react/preview.d.ts",
38
38
  "import": "./react/preview.js",
39
39
  "require": "./react/preview.cjs"
40
+ },
41
+ "./react/forms": {
42
+ "types": "./react/forms.d.ts",
43
+ "import": "./react/forms.js",
44
+ "require": "./react/forms.cjs"
40
45
  }
41
46
  },
42
47
  "sideEffects": false,
@@ -0,0 +1,10 @@
1
+ import type { WebsiteFormField } from "../types";
2
+ import type { BacanoFormValues } from "./forms";
3
+ type BacanoFormValue = BacanoFormValues[string];
4
+ export declare function BacanoFormFieldControl(props: {
5
+ field: WebsiteFormField;
6
+ className?: string;
7
+ value: BacanoFormValue;
8
+ onChange(value: BacanoFormValue): void;
9
+ }): import("react").JSX.Element;
10
+ export {};