@bacano/sdk 1.2.2 → 1.2.4

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.js CHANGED
@@ -53,6 +53,52 @@ var GraphQLRequestError = class extends WebsiteError {
53
53
  }
54
54
  };
55
55
 
56
+ // libs/sdk/src/flows.ts
57
+ function createFlowsClient(config) {
58
+ const base = config.apiUrl.replace(/\/+$/, "") + "/api/v1/website/flows";
59
+ async function request(path, body, binary = false) {
60
+ const token = await config.getAccessToken?.();
61
+ const response = await fetch(base + path, {
62
+ method: body === void 0 ? "GET" : "POST",
63
+ cache: "no-store",
64
+ headers: {
65
+ ...token ? { Authorization: `Bearer ${token}` } : {},
66
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {},
67
+ ...config.websiteSlug ? { "x-bacano-website-slug": config.websiteSlug } : {}
68
+ },
69
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
70
+ });
71
+ if (!response.ok) {
72
+ const result = await response.json().catch(() => ({ error: "FLOW_REQUEST_FAILED" }));
73
+ throw new WebsiteError(
74
+ result.error ?? "FLOW_REQUEST_FAILED",
75
+ result.error ?? "FLOW_REQUEST_FAILED"
76
+ );
77
+ }
78
+ return binary ? await response.blob() : await response.json();
79
+ }
80
+ const stepPath = (caseId, stepId) => `/cases/${encodeURIComponent(caseId)}/steps/${encodeURIComponent(stepId)}`;
81
+ return {
82
+ /** Only the explicitly enabled local demo gateway accepts these credentials. */
83
+ localDemoLogin: (email, password) => request("/local-session", { email, password }),
84
+ logout: () => request("/logout", {}),
85
+ listCases: () => request("/cases"),
86
+ getJourney: (caseId) => request(`/cases/${encodeURIComponent(caseId)}`),
87
+ getContent: (caseId, stepId) => request(stepPath(caseId, stepId) + "/content"),
88
+ getForm: (caseId, stepId) => request(stepPath(caseId, stepId) + "/form"),
89
+ getResponse: (caseId, stepId) => request(stepPath(caseId, stepId) + "/response"),
90
+ getFile: (caseId, stepId, fileId) => request(
91
+ stepPath(caseId, stepId) + `/files/${encodeURIComponent(fileId)}`,
92
+ void 0,
93
+ true
94
+ ),
95
+ act: (caseId, command) => request(
96
+ `/cases/${encodeURIComponent(caseId)}/actions`,
97
+ command
98
+ )
99
+ };
100
+ }
101
+
56
102
  // libs/sdk/src/media.ts
57
103
  var DEFAULT_RESPONSIVE_WIDTHS = [240, 384, 640, 960, 1400];
58
104
  var CONTENT_HERO_WIDTHS = [640, 960, 1400, 1920, 2560];
@@ -1307,6 +1353,35 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
1307
1353
  }
1308
1354
  `
1309
1355
  );
1356
+ var WEBSITE_GET_PRODUCT_CONTENT_BATCH = (
1357
+ /* GraphQL */
1358
+ `
1359
+ mutation WebsiteGetProductContentBatch(
1360
+ $websiteId: uuid!
1361
+ $productIds: [uuid!]!
1362
+ $locale: String
1363
+ ) {
1364
+ result: websiteGetProductContentBatch(
1365
+ website_id: $websiteId
1366
+ product_ids: $productIds
1367
+ locale: $locale
1368
+ ) {
1369
+ success
1370
+ errors
1371
+ blocks {
1372
+ id
1373
+ product_id
1374
+ locale
1375
+ content_json
1376
+ resolved_fields
1377
+ sort_order
1378
+ updated_at
1379
+ content_type { id key name kind }
1380
+ }
1381
+ }
1382
+ }
1383
+ `
1384
+ );
1310
1385
  var CHECK_AVAILABILITY = (
1311
1386
  /* GraphQL */
1312
1387
  `
@@ -2183,7 +2258,7 @@ var RECONCILE_PAYMENT_SESSION = (
2183
2258
  $orderId: uuid!
2184
2259
  $publicOrderToken: String!
2185
2260
  $paymentSessionId: uuid!
2186
- $gatewayTransactionId: String!
2261
+ $gatewayTransactionId: String
2187
2262
  ) {
2188
2263
  websiteReconcilePaymentSession(
2189
2264
  order_id: $orderId
@@ -5675,6 +5750,33 @@ function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
5675
5750
  return (data.result.blocks ?? []).map(
5676
5751
  (block) => mapProductContentBlock(block, storageUrl)
5677
5752
  );
5753
+ },
5754
+ async getForProducts(productIds, opts) {
5755
+ const uniqueIds = [...new Set(productIds.map((id) => id.toLowerCase()))];
5756
+ const blocksByProduct = Object.fromEntries(uniqueIds.map((id) => [id, []]));
5757
+ if (uniqueIds.length === 0) return blocksByProduct;
5758
+ const ctx = getContext();
5759
+ for (let start = 0; start < uniqueIds.length; start += 50) {
5760
+ const batch = uniqueIds.slice(start, start + 50);
5761
+ const data = await executeGraphQL(
5762
+ graphqlUrl,
5763
+ WEBSITE_GET_PRODUCT_CONTENT_BATCH,
5764
+ {
5765
+ websiteId: ctx.websiteId,
5766
+ productIds: batch,
5767
+ locale: opts?.locale ?? DEFAULT_LOCALE
5768
+ },
5769
+ getHeaders
5770
+ );
5771
+ if (!data.result?.success) continue;
5772
+ for (const block of data.result.blocks ?? []) {
5773
+ const id = block.product_id.toLowerCase();
5774
+ if (Object.prototype.hasOwnProperty.call(blocksByProduct, id)) {
5775
+ blocksByProduct[id].push(mapProductContentBlock(block, storageUrl));
5776
+ }
5777
+ }
5778
+ }
5779
+ return blocksByProduct;
5678
5780
  }
5679
5781
  };
5680
5782
  }
@@ -7007,6 +7109,7 @@ function createBacanoClient(config) {
7007
7109
  await initialization;
7008
7110
  },
7009
7111
  getContext,
7112
+ flows: createFlowsClient(config),
7010
7113
  auth: {
7011
7114
  async getAccessToken() {
7012
7115
  return config.getAccessToken ? await config.getAccessToken() : null;
@@ -7039,7 +7142,8 @@ function createBacanoClient(config) {
7039
7142
  getByKey: contentModule.getByKey.bind(contentModule),
7040
7143
  getByCategory: contentModule.getByCategory.bind(contentModule),
7041
7144
  getByTag: contentModule.getByTag.bind(contentModule),
7042
- getForProduct: contentModule.getForProduct.bind(contentModule)
7145
+ getForProduct: contentModule.getForProduct.bind(contentModule),
7146
+ getForProducts: contentModule.getForProducts.bind(contentModule)
7043
7147
  },
7044
7148
  forms: {
7045
7149
  getDefinition: formsModule.getDefinition.bind(formsModule),
@@ -7106,6 +7210,37 @@ function getContentEntryLocatorAttributes(entry) {
7106
7210
  "data-bacano-entry-name": entry.name.trim() || key
7107
7211
  };
7108
7212
  }
7213
+
7214
+ // libs/sdk/src/form-conditions.ts
7215
+ function formRootFields(definition) {
7216
+ return [
7217
+ ...definition.fields,
7218
+ ...definition.sections.flatMap((section) => section.fields),
7219
+ ...definition.steps.flatMap((step) => [
7220
+ ...step.fields,
7221
+ ...step.sections.flatMap((section) => section.fields)
7222
+ ])
7223
+ ];
7224
+ }
7225
+ function visibleFormFields(fields, values) {
7226
+ return fields.flatMap((field) => [
7227
+ field,
7228
+ ...visibleFormFields(
7229
+ field.branches?.find((branch) => branch.value === values[field.key])?.fields ?? [],
7230
+ values
7231
+ )
7232
+ ]);
7233
+ }
7234
+ function activeFormValues(definition, values) {
7235
+ const keys = new Set(
7236
+ visibleFormFields(formRootFields(definition), values).map(
7237
+ (field) => field.key
7238
+ )
7239
+ );
7240
+ return Object.fromEntries(
7241
+ Object.entries(values).filter(([key]) => keys.has(key))
7242
+ );
7243
+ }
7109
7244
  export {
7110
7245
  AuthError,
7111
7246
  CONTENT_ENTRY_LOCATOR_SELECTOR,
@@ -7117,6 +7252,7 @@ export {
7117
7252
  RateLimitError,
7118
7253
  WEBSITE_MARKETING_CONTACT_PLACEMENTS,
7119
7254
  WebsiteError,
7255
+ activeFormValues,
7120
7256
  buildStorageContentMediaUrls,
7121
7257
  buildStorageFileUrl,
7122
7258
  buildStorageMediaUrls,
@@ -7124,7 +7260,9 @@ export {
7124
7260
  buildStorageTransformedFileUrl,
7125
7261
  captureWebsiteMarketingAttribution,
7126
7262
  createBacanoClient,
7263
+ createFlowsClient,
7127
7264
  createWebsiteMarketingRuntime,
7265
+ formRootFields,
7128
7266
  formatMediaSrcSet,
7129
7267
  getAllowedMarketingParameters,
7130
7268
  getContentEntryLocatorAttributes,
@@ -7138,5 +7276,6 @@ export {
7138
7276
  getWebsiteMarketingAttribution,
7139
7277
  getWebsitePublicConfig,
7140
7278
  resolveProductPricing,
7141
- sanitizeMarketingUrl
7279
+ sanitizeMarketingUrl,
7280
+ visibleFormFields
7142
7281
  };
@@ -36,7 +36,7 @@ export interface CheckoutModule {
36
36
  orderId: string;
37
37
  publicOrderToken: string;
38
38
  paymentSessionId: string;
39
- gatewayTransactionId: string;
39
+ gatewayTransactionId?: string;
40
40
  }): Promise<WebsitePaymentSessionResult>;
41
41
  }
42
42
  export declare function createCheckoutModule(graphqlUrl: string, getHeaders: RequestHeadersProvider, getContext: () => BacanoClientContext, cartSessionStorage: CartSessionStorage): CheckoutModule;
@@ -5,5 +5,6 @@ export interface ContentModule {
5
5
  getByCategory(categoryKey: string, opts?: GetContentOptions): Promise<ContentEntry[]>;
6
6
  getByTag(tag: string, opts?: GetContentOptions): Promise<ContentEntry[]>;
7
7
  getForProduct(productId: string, opts?: GetContentOptions): Promise<ProductContentBlock[]>;
8
+ getForProducts(productIds: string[], opts?: GetContentOptions): Promise<Record<string, ProductContentBlock[]>>;
8
9
  }
9
10
  export declare function createContentModule(graphqlUrl: string, getHeaders: RequestHeadersProvider, getContext: () => BacanoClientContext, storageUrl?: string): ContentModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacano/sdk",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "TypeScript SDK for building websites on the Bacano ERP",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.js",
@@ -0,0 +1,9 @@
1
+ import type { FlowPublishedForm } from "../flows";
2
+ import type { BacanoFormClassNames, BacanoFormValues } from "./forms";
3
+ /** Reuses the Forms controls but never calls a website form-entry endpoint. */
4
+ export declare function BacanoFlowForm({ form, onSubmit, onRefresh, classNames, }: {
5
+ form: FlowPublishedForm;
6
+ onSubmit(values: BacanoFormValues, form: FlowPublishedForm): Promise<void>;
7
+ onRefresh(): Promise<void>;
8
+ classNames?: BacanoFormClassNames;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import type { WebsiteFormField } from "../types";
2
+ /** Discrete radios preserve keyboard support, required validation and an empty initial answer. */
3
+ export declare function FormScaleControl({ field, id, value, onChange, className, }: {
4
+ field: WebsiteFormField;
5
+ id: string;
6
+ value: string | string[] | boolean;
7
+ onChange(value: string): void;
8
+ className?: string;
9
+ }): import("react").JSX.Element | null;