@cimplify/sdk 0.16.3 → 0.17.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.
@@ -0,0 +1,687 @@
1
+ import { CatalogueQueries, CartOperations, CheckoutService, OrderQueries, LinkService, AuthService, BusinessService, InventoryService, SchedulingService, LiteService, FxService, createElements } from './chunk-GFKBH7R2.mjs';
2
+ import { CimplifyError, ErrorCode, enrichError } from './chunk-OVM5YWM5.mjs';
3
+
4
+ // src/activity.ts
5
+ var ActivityService = class {
6
+ constructor(client) {
7
+ this.client = client;
8
+ }
9
+ trackProductView(productId, options = {}) {
10
+ const event = {
11
+ type: "product_view",
12
+ product_id: productId,
13
+ product_name: options.productName,
14
+ category_id: options.categoryId,
15
+ price: options.price,
16
+ page_path: options.pagePath ?? currentPath()
17
+ };
18
+ this.sendEvents([event]);
19
+ }
20
+ trackCategoryView(categoryId, options = {}) {
21
+ const event = {
22
+ type: "category_view",
23
+ category_id: categoryId,
24
+ category_name: options.categoryName,
25
+ page_path: options.pagePath ?? currentPath()
26
+ };
27
+ this.sendEvents([event]);
28
+ }
29
+ sendEvents(events) {
30
+ try {
31
+ this.client.post("/api/v1/activity/events", { events }).catch(() => {
32
+ });
33
+ } catch {
34
+ }
35
+ }
36
+ };
37
+ function currentPath() {
38
+ if (typeof window !== "undefined") {
39
+ return window.location.pathname;
40
+ }
41
+ return void 0;
42
+ }
43
+
44
+ // src/client.ts
45
+ var ACCESS_TOKEN_STORAGE_KEY = "cimplify_access_token";
46
+ var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
47
+ var ORDER_TOKEN_PREFIX = "cimplify_ot_";
48
+ var SESSION_TOKEN_HEADER = "x-session-token";
49
+ var ORDER_TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
50
+ var DEFAULT_TIMEOUT_MS = 3e4;
51
+ var DEFAULT_MAX_RETRIES = 3;
52
+ var DEFAULT_RETRY_DELAY_MS = 1e3;
53
+ function sleep(ms) {
54
+ return new Promise((resolve) => setTimeout(resolve, ms));
55
+ }
56
+ function isRetryable(error) {
57
+ if (error instanceof TypeError && error.message.includes("fetch")) {
58
+ return true;
59
+ }
60
+ if (error instanceof DOMException && error.name === "AbortError") {
61
+ return true;
62
+ }
63
+ if (error instanceof CimplifyError) {
64
+ return error.retryable;
65
+ }
66
+ return false;
67
+ }
68
+ function toNetworkError(error, isTestMode) {
69
+ if (error instanceof DOMException && error.name === "AbortError") {
70
+ return enrichError(
71
+ new CimplifyError(
72
+ ErrorCode.TIMEOUT,
73
+ "Request timed out. Please check your connection and try again.",
74
+ true
75
+ ),
76
+ { isTestMode }
77
+ );
78
+ }
79
+ if (error instanceof TypeError && error.message.includes("fetch")) {
80
+ return enrichError(
81
+ new CimplifyError(
82
+ ErrorCode.NETWORK_ERROR,
83
+ "Network error. Please check your internet connection.",
84
+ true
85
+ ),
86
+ { isTestMode }
87
+ );
88
+ }
89
+ if (error instanceof CimplifyError) {
90
+ return enrichError(error, { isTestMode });
91
+ }
92
+ return enrichError(
93
+ new CimplifyError(
94
+ ErrorCode.UNKNOWN_ERROR,
95
+ error instanceof Error ? error.message : "An unknown error occurred",
96
+ false
97
+ ),
98
+ { isTestMode }
99
+ );
100
+ }
101
+ function deriveUrls() {
102
+ const hostname = typeof window !== "undefined" ? window.location.hostname : "";
103
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
104
+ const protocol = window.location.protocol;
105
+ return {
106
+ baseUrl: `${protocol}//${hostname}:8082`,
107
+ linkApiUrl: `${protocol}//${hostname}:8080`
108
+ };
109
+ }
110
+ return {
111
+ baseUrl: "https://storefronts.cimplify.io",
112
+ linkApiUrl: "https://api.cimplify.io"
113
+ };
114
+ }
115
+ function normalizeUrl(url) {
116
+ const trimmed = url.trim();
117
+ if (!trimmed) return trimmed;
118
+ return trimmed.replace(/\/+$/, "");
119
+ }
120
+ function getEnvPublicKey() {
121
+ try {
122
+ const env = globalThis.process;
123
+ return env?.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || void 0;
124
+ } catch {
125
+ return void 0;
126
+ }
127
+ }
128
+ var CimplifyClient = class {
129
+ constructor(config = {}) {
130
+ this.accessToken = null;
131
+ this.sessionToken = null;
132
+ this.context = {};
133
+ this.businessId = null;
134
+ this.businessIdResolvePromise = null;
135
+ this.inflightRequests = /* @__PURE__ */ new Map();
136
+ this.publicKey = config.publicKey || getEnvPublicKey() || "";
137
+ const urls = deriveUrls();
138
+ this.baseUrl = config.baseUrl ? normalizeUrl(config.baseUrl) : urls.baseUrl;
139
+ this.linkApiUrl = config.linkApiUrl ? normalizeUrl(config.linkApiUrl) : urls.linkApiUrl;
140
+ this.credentials = config.credentials || "include";
141
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
142
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
143
+ this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
144
+ this.hooks = config.hooks ?? {};
145
+ this.accessToken = this.loadAccessToken();
146
+ this.sessionToken = this.loadSessionToken();
147
+ if (!this.publicKey && !config.suppressPublicKeyWarning) {
148
+ console.warn(
149
+ '[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "pk_..." } to createCimplifyClient().'
150
+ );
151
+ }
152
+ }
153
+ buildUrl(base, path) {
154
+ const url = `${base}${path}`;
155
+ const locationId = this.context.location_id;
156
+ if (!locationId) return url;
157
+ const separator = url.includes("?") ? "&" : "?";
158
+ return `${url}${separator}location_id=${encodeURIComponent(locationId)}`;
159
+ }
160
+ getAccessToken() {
161
+ return this.accessToken;
162
+ }
163
+ getPublicKey() {
164
+ return this.publicKey;
165
+ }
166
+ isTestMode() {
167
+ return this.publicKey.trim().startsWith("pk_test_");
168
+ }
169
+ setAccessToken(token) {
170
+ const previous = this.accessToken;
171
+ this.accessToken = token;
172
+ this.saveAccessToken(token);
173
+ this.hooks.onSessionChange?.({
174
+ previousToken: previous,
175
+ newToken: token,
176
+ source: "manual"
177
+ });
178
+ }
179
+ clearSession() {
180
+ const previous = this.accessToken;
181
+ this.accessToken = null;
182
+ this.sessionToken = null;
183
+ this.saveAccessToken(null);
184
+ if (typeof window !== "undefined" && window.localStorage) {
185
+ localStorage.removeItem(SESSION_TOKEN_STORAGE_KEY);
186
+ }
187
+ this.hooks.onSessionChange?.({
188
+ previousToken: previous,
189
+ newToken: null,
190
+ source: "clear"
191
+ });
192
+ }
193
+ setOrderToken(orderId, token) {
194
+ if (typeof window !== "undefined" && window.localStorage) {
195
+ try {
196
+ const entry = JSON.stringify({ token, storedAt: Date.now() });
197
+ localStorage.setItem(`${ORDER_TOKEN_PREFIX}${orderId}`, entry);
198
+ } catch {
199
+ }
200
+ }
201
+ }
202
+ getOrderToken(orderId) {
203
+ if (typeof window !== "undefined" && window.localStorage) {
204
+ try {
205
+ const raw = localStorage.getItem(`${ORDER_TOKEN_PREFIX}${orderId}`);
206
+ if (!raw) return null;
207
+ const entry = JSON.parse(raw);
208
+ if (Date.now() - entry.storedAt > ORDER_TOKEN_TTL_MS) {
209
+ localStorage.removeItem(`${ORDER_TOKEN_PREFIX}${orderId}`);
210
+ return null;
211
+ }
212
+ return entry.token;
213
+ } catch {
214
+ return null;
215
+ }
216
+ }
217
+ return null;
218
+ }
219
+ clearOrderTokens() {
220
+ if (typeof window !== "undefined" && window.localStorage) {
221
+ const keysToRemove = [];
222
+ for (let i = 0; i < localStorage.length; i++) {
223
+ const key = localStorage.key(i);
224
+ if (key?.startsWith(ORDER_TOKEN_PREFIX)) {
225
+ keysToRemove.push(key);
226
+ }
227
+ }
228
+ keysToRemove.forEach((k) => localStorage.removeItem(k));
229
+ }
230
+ }
231
+ setLocationId(locationId) {
232
+ if (locationId) {
233
+ this.context.location_id = locationId;
234
+ } else {
235
+ delete this.context.location_id;
236
+ }
237
+ }
238
+ getLocationId() {
239
+ return this.context.location_id ?? null;
240
+ }
241
+ setBusinessId(businessId) {
242
+ const normalized = businessId.trim();
243
+ if (!normalized) {
244
+ return;
245
+ }
246
+ this.businessId = normalized;
247
+ }
248
+ getBusinessId() {
249
+ return this.businessId;
250
+ }
251
+ async resolveBusinessId() {
252
+ if (this.businessId) {
253
+ return this.businessId;
254
+ }
255
+ if (this.businessIdResolvePromise) {
256
+ return this.businessIdResolvePromise;
257
+ }
258
+ this.businessIdResolvePromise = (async () => {
259
+ const result = await this.business.getInfo();
260
+ if (!result.ok || !result.value?.id) {
261
+ throw new CimplifyError(
262
+ ErrorCode.NOT_FOUND,
263
+ "Unable to resolve business ID from the current public key.",
264
+ true
265
+ );
266
+ }
267
+ this.businessId = result.value.id;
268
+ return result.value.id;
269
+ })();
270
+ try {
271
+ return await this.businessIdResolvePromise;
272
+ } finally {
273
+ this.businessIdResolvePromise = null;
274
+ }
275
+ }
276
+ loadAccessToken() {
277
+ if (typeof window !== "undefined" && window.localStorage) {
278
+ return localStorage.getItem(ACCESS_TOKEN_STORAGE_KEY);
279
+ }
280
+ return null;
281
+ }
282
+ saveAccessToken(token) {
283
+ if (typeof window !== "undefined" && window.localStorage) {
284
+ try {
285
+ if (token) {
286
+ localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, token);
287
+ } else {
288
+ localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);
289
+ }
290
+ } catch {
291
+ }
292
+ }
293
+ }
294
+ loadSessionToken() {
295
+ if (typeof window !== "undefined" && window.localStorage) {
296
+ return localStorage.getItem(SESSION_TOKEN_STORAGE_KEY);
297
+ }
298
+ return null;
299
+ }
300
+ saveSessionToken(token) {
301
+ if (typeof window !== "undefined" && window.localStorage) {
302
+ try {
303
+ localStorage.setItem(SESSION_TOKEN_STORAGE_KEY, token);
304
+ } catch {
305
+ }
306
+ }
307
+ }
308
+ captureSessionToken(response) {
309
+ const token = response.headers.get(SESSION_TOKEN_HEADER);
310
+ if (token && token !== this.sessionToken) {
311
+ this.sessionToken = token;
312
+ this.saveSessionToken(token);
313
+ }
314
+ }
315
+ getHeaders() {
316
+ const headers = {
317
+ "Content-Type": "application/json"
318
+ };
319
+ const trimmedPublicKey = this.publicKey.trim();
320
+ if (trimmedPublicKey) {
321
+ headers["X-API-Key"] = trimmedPublicKey;
322
+ }
323
+ if (this.accessToken) {
324
+ headers["Authorization"] = `Bearer ${this.accessToken}`;
325
+ }
326
+ if (this.sessionToken) {
327
+ headers[SESSION_TOKEN_HEADER] = this.sessionToken;
328
+ }
329
+ return headers;
330
+ }
331
+ async resilientFetch(url, options) {
332
+ const method = options.method || "GET";
333
+ const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
334
+ const startTime = Date.now();
335
+ let retryCount = 0;
336
+ const context = {
337
+ method,
338
+ path,
339
+ url,
340
+ body: options.body ? JSON.parse(options.body) : void 0,
341
+ startTime
342
+ };
343
+ this.hooks.onRequestStart?.(context);
344
+ let lastError;
345
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
346
+ try {
347
+ const controller = new AbortController();
348
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
349
+ const response = await fetch(url, {
350
+ ...options,
351
+ signal: controller.signal
352
+ });
353
+ clearTimeout(timeoutId);
354
+ this.captureSessionToken(response);
355
+ if (response.ok) {
356
+ this.hooks.onRequestSuccess?.({
357
+ ...context,
358
+ status: response.status,
359
+ durationMs: Date.now() - startTime
360
+ });
361
+ return response;
362
+ }
363
+ if (response.status === 429 && attempt < this.maxRetries) {
364
+ retryCount++;
365
+ const retryAfter = response.headers.get("Retry-After");
366
+ const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3, 3e4) || this.retryDelay * Math.pow(2, attempt) : this.retryDelay * Math.pow(2, attempt);
367
+ this.hooks.onRetry?.({
368
+ ...context,
369
+ attempt: retryCount,
370
+ delayMs: delay,
371
+ error: new Error(`Rate limited: ${response.status}`)
372
+ });
373
+ await sleep(delay);
374
+ continue;
375
+ }
376
+ if (response.status >= 400 && response.status < 500) {
377
+ this.hooks.onRequestError?.({
378
+ ...context,
379
+ error: new CimplifyError(
380
+ `HTTP_${response.status}`,
381
+ `Request failed with status ${response.status}`,
382
+ false
383
+ ),
384
+ status: response.status,
385
+ durationMs: Date.now() - startTime,
386
+ retryCount,
387
+ retryable: false
388
+ });
389
+ return response;
390
+ }
391
+ if (response.status >= 500 && attempt < this.maxRetries) {
392
+ retryCount++;
393
+ const delay = this.retryDelay * Math.pow(2, attempt);
394
+ this.hooks.onRetry?.({
395
+ ...context,
396
+ attempt: retryCount,
397
+ delayMs: delay,
398
+ error: new Error(`Server error: ${response.status}`)
399
+ });
400
+ await sleep(delay);
401
+ continue;
402
+ }
403
+ this.hooks.onRequestError?.({
404
+ ...context,
405
+ error: new CimplifyError(
406
+ "SERVER_ERROR",
407
+ `Server error ${response.status} after ${retryCount} retries`,
408
+ false
409
+ ),
410
+ status: response.status,
411
+ durationMs: Date.now() - startTime,
412
+ retryCount,
413
+ retryable: false
414
+ });
415
+ return response;
416
+ } catch (error) {
417
+ lastError = error;
418
+ const networkError = toNetworkError(error, this.isTestMode());
419
+ const errorRetryable = isRetryable(error);
420
+ if (!errorRetryable || attempt >= this.maxRetries) {
421
+ this.hooks.onRequestError?.({
422
+ ...context,
423
+ error: networkError,
424
+ durationMs: Date.now() - startTime,
425
+ retryCount,
426
+ retryable: errorRetryable
427
+ });
428
+ throw networkError;
429
+ }
430
+ retryCount++;
431
+ const delay = this.retryDelay * Math.pow(2, attempt);
432
+ this.hooks.onRetry?.({
433
+ ...context,
434
+ attempt: retryCount,
435
+ delayMs: delay,
436
+ error: networkError
437
+ });
438
+ await sleep(delay);
439
+ }
440
+ }
441
+ const finalError = toNetworkError(lastError, this.isTestMode());
442
+ this.hooks.onRequestError?.({
443
+ ...context,
444
+ error: finalError,
445
+ durationMs: Date.now() - startTime,
446
+ retryCount,
447
+ retryable: false
448
+ });
449
+ throw finalError;
450
+ }
451
+ getDedupeKey(type, payload) {
452
+ return `${type}:${JSON.stringify(payload)}`;
453
+ }
454
+ async deduplicatedRequest(key, requestFn) {
455
+ const existing = this.inflightRequests.get(key);
456
+ if (existing) {
457
+ return existing;
458
+ }
459
+ const request = requestFn().finally(() => {
460
+ this.inflightRequests.delete(key);
461
+ });
462
+ this.inflightRequests.set(key, request);
463
+ return request;
464
+ }
465
+ async get(path) {
466
+ const key = this.getDedupeKey("get", path);
467
+ return this.deduplicatedRequest(key, async () => {
468
+ const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
469
+ method: "GET",
470
+ credentials: this.credentials,
471
+ headers: this.getHeaders()
472
+ });
473
+ return this.handleRestResponse(response);
474
+ });
475
+ }
476
+ async post(path, body) {
477
+ const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
478
+ method: "POST",
479
+ credentials: this.credentials,
480
+ headers: this.getHeaders(),
481
+ body: body ? JSON.stringify(body) : void 0
482
+ });
483
+ return this.handleRestResponse(response);
484
+ }
485
+ async patch(path, body) {
486
+ const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
487
+ method: "PATCH",
488
+ credentials: this.credentials,
489
+ headers: this.getHeaders(),
490
+ body: body ? JSON.stringify(body) : void 0
491
+ });
492
+ return this.handleRestResponse(response);
493
+ }
494
+ async delete(path) {
495
+ const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
496
+ method: "DELETE",
497
+ credentials: this.credentials,
498
+ headers: this.getHeaders()
499
+ });
500
+ return this.handleRestResponse(response);
501
+ }
502
+ async linkGet(path) {
503
+ const key = this.getDedupeKey("linkGet", path);
504
+ return this.deduplicatedRequest(key, async () => {
505
+ const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
506
+ method: "GET",
507
+ credentials: this.credentials,
508
+ headers: this.getHeaders()
509
+ });
510
+ return this.handleRestResponse(response);
511
+ });
512
+ }
513
+ async linkPost(path, body) {
514
+ const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
515
+ method: "POST",
516
+ credentials: this.credentials,
517
+ headers: this.getHeaders(),
518
+ body: body ? JSON.stringify(body) : void 0
519
+ });
520
+ return this.handleRestResponse(response);
521
+ }
522
+ async linkDelete(path) {
523
+ const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
524
+ method: "DELETE",
525
+ credentials: this.credentials,
526
+ headers: this.getHeaders()
527
+ });
528
+ return this.handleRestResponse(response);
529
+ }
530
+ async handleRestResponse(response) {
531
+ const json = await response.json();
532
+ if (!response.ok) {
533
+ const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code || "API_ERROR";
534
+ const errorMessage = typeof json.error === "object" && json.error?.error_message || typeof json.error === "object" && json.error?.message || typeof json.error === "string" && json.error || typeof json.message === "string" && json.message || "An error occurred";
535
+ const retryable = typeof json.error === "object" && typeof json.error?.retryable === "boolean" ? json.error.retryable : false;
536
+ const error = enrichError(
537
+ new CimplifyError(
538
+ errorCode,
539
+ errorMessage,
540
+ retryable
541
+ ),
542
+ { isTestMode: this.isTestMode() }
543
+ );
544
+ if (response.status === 401 || error.code === ErrorCode.UNAUTHORIZED) {
545
+ console.warn(
546
+ "[Cimplify] Received 401 Unauthorized. Access token may be missing/expired. Refresh authentication and retry the request."
547
+ );
548
+ }
549
+ throw error;
550
+ }
551
+ if (json?.success === false || json?.error?.code || json?.error?.message) {
552
+ const error = enrichError(
553
+ new CimplifyError(
554
+ json.error?.code || "API_ERROR",
555
+ json.error?.message || "An error occurred",
556
+ json.error?.retryable || false
557
+ ),
558
+ { isTestMode: this.isTestMode() }
559
+ );
560
+ throw error;
561
+ }
562
+ if (json?.data !== void 0) {
563
+ return json.data;
564
+ }
565
+ return json;
566
+ }
567
+ get catalogue() {
568
+ if (!this._catalogue) {
569
+ this._catalogue = new CatalogueQueries(this);
570
+ }
571
+ return this._catalogue;
572
+ }
573
+ get cart() {
574
+ if (!this._cart) {
575
+ this._cart = new CartOperations(this);
576
+ }
577
+ return this._cart;
578
+ }
579
+ get checkout() {
580
+ if (!this._checkout) {
581
+ this._checkout = new CheckoutService(this);
582
+ }
583
+ return this._checkout;
584
+ }
585
+ get orders() {
586
+ if (!this._orders) {
587
+ this._orders = new OrderQueries(this);
588
+ }
589
+ return this._orders;
590
+ }
591
+ get link() {
592
+ if (!this._link) {
593
+ this._link = new LinkService(this);
594
+ }
595
+ return this._link;
596
+ }
597
+ get auth() {
598
+ if (!this._auth) {
599
+ this._auth = new AuthService(this);
600
+ }
601
+ return this._auth;
602
+ }
603
+ get business() {
604
+ if (!this._business) {
605
+ this._business = new BusinessService(this);
606
+ }
607
+ return this._business;
608
+ }
609
+ get inventory() {
610
+ if (!this._inventory) {
611
+ this._inventory = new InventoryService(this);
612
+ }
613
+ return this._inventory;
614
+ }
615
+ get scheduling() {
616
+ if (!this._scheduling) {
617
+ this._scheduling = new SchedulingService(this);
618
+ }
619
+ return this._scheduling;
620
+ }
621
+ get lite() {
622
+ if (!this._lite) {
623
+ this._lite = new LiteService(this);
624
+ }
625
+ return this._lite;
626
+ }
627
+ get fx() {
628
+ if (!this._fx) {
629
+ this._fx = new FxService(this);
630
+ }
631
+ return this._fx;
632
+ }
633
+ get activity() {
634
+ if (!this._activity) {
635
+ this._activity = new ActivityService(this);
636
+ }
637
+ return this._activity;
638
+ }
639
+ elements(businessId, options) {
640
+ if (businessId) {
641
+ this.setBusinessId(businessId);
642
+ }
643
+ return createElements(this, businessId ?? this.businessId ?? void 0, options);
644
+ }
645
+ };
646
+ function createCimplifyClient(config = {}) {
647
+ return new CimplifyClient(config);
648
+ }
649
+
650
+ // src/utils/variant.ts
651
+ function stripProductPrefix(variantName, productName) {
652
+ if (!variantName || !productName) return variantName || productName;
653
+ const lower = variantName.toLowerCase().trim();
654
+ if (lower === "default" || lower === "standard") return productName;
655
+ if (lower.endsWith("-default") || lower.endsWith("-standard")) return productName;
656
+ if (variantName.startsWith(productName)) {
657
+ const rest = variantName.slice(productName.length);
658
+ const match = rest.match(/^\s*[-–—]\s*(.*)/);
659
+ if (match && match[1].length > 0) return match[1];
660
+ }
661
+ const lowerProduct = productName.toLowerCase().trim();
662
+ if (lower.startsWith(lowerProduct)) {
663
+ const rest = variantName.slice(productName.length);
664
+ const match = rest.match(/^\s*[-–—]\s*(.*)/);
665
+ if (match && match[1].length > 0) return match[1];
666
+ }
667
+ const lastDash = variantName.lastIndexOf("-");
668
+ if (lastDash > 0 && lastDash < variantName.length - 1) {
669
+ const prefix = variantName.slice(0, lastDash).trim();
670
+ const suffix = variantName.slice(lastDash + 1).trim();
671
+ if (suffix.length > 0 && prefix.length > suffix.length * 2) {
672
+ return suffix;
673
+ }
674
+ }
675
+ return variantName;
676
+ }
677
+ function getVariantDisplayName(variant, productName) {
678
+ if (variant.display_attributes?.length) {
679
+ return variant.display_attributes.map((a) => a.value_name).join(" / ");
680
+ }
681
+ if (variant.name) {
682
+ return productName ? stripProductPrefix(variant.name, productName) : variant.name;
683
+ }
684
+ return productName || (variant.is_default ? "Default" : "Option");
685
+ }
686
+
687
+ export { ActivityService, CimplifyClient, createCimplifyClient, getVariantDisplayName };