@altazion/commerce-sdk-htmx 26.409.7573 → 26.415.7673

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/dist/index.cjs CHANGED
@@ -1,17 +1,697 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- function registerAltazionAuthExtension(client) {
4
- if (typeof window === "undefined" || !window.htmx) {
5
- console.warn("[AltazionHtmx] htmx non disponible sur window — extension non enregistrée");
6
- return;
7
- }
8
- const handler = (event) => {
3
+ const commerceSdkCore = require("@altazion/commerce-sdk-core");
4
+ const EXTENSION_NAME = "altazion";
5
+ const JSON_VALUES_ATTRIBUTE = "hx-vals";
6
+ const REFRESH_ATTRIBUTE = "hx-altazion-refresh";
7
+ const REFRESH_EVENT_NAME = "altazion:refresh";
8
+ const REQUEST_ATTRIBUTES = [
9
+ { attribute: "hx-get", method: "GET" },
10
+ { attribute: "hx-post", method: "POST" },
11
+ { attribute: "hx-put", method: "PUT" },
12
+ { attribute: "hx-patch", method: "PATCH" },
13
+ { attribute: "hx-delete", method: "DELETE" }
14
+ ];
15
+ function createAltazionExtension(client, modules) {
16
+ const requestHandler = (event) => {
9
17
  const e = event;
10
18
  e.detail.headers["Accept-Language"] = client.context.locale;
11
19
  e.detail.headers["X-Altazion-Currency"] = client.context.currency;
12
20
  };
13
- window.htmx.on("htmx:configRequest", handler);
21
+ if (typeof window !== "undefined" && window.htmx) {
22
+ window.htmx.on("htmx:configRequest", requestHandler);
23
+ }
24
+ if (typeof document === "undefined") {
25
+ return {
26
+ dispose() {
27
+ if (typeof window !== "undefined" && window.htmx) {
28
+ window.htmx.off("htmx:configRequest", requestHandler);
29
+ }
30
+ }
31
+ };
32
+ }
33
+ const handleClick = (event) => {
34
+ const mouseEvent = event;
35
+ if (mouseEvent.defaultPrevented || mouseEvent.button !== 0 || mouseEvent.metaKey || mouseEvent.ctrlKey || mouseEvent.shiftKey || mouseEvent.altKey) {
36
+ return;
37
+ }
38
+ const target = event.target;
39
+ if (!(target instanceof Element)) {
40
+ return;
41
+ }
42
+ const matchedAction = findMatchedAction(target, modules);
43
+ if (!matchedAction || matchedAction.element instanceof HTMLFormElement || !hasAltazionExtension(matchedAction.element)) {
44
+ return;
45
+ }
46
+ event.preventDefault();
47
+ void runModuleAction(client, matchedAction);
48
+ };
49
+ const handleSubmit = (event) => {
50
+ const target = event.target;
51
+ if (!(target instanceof HTMLFormElement)) {
52
+ return;
53
+ }
54
+ const matchedAction = findMatchedAction(target, modules);
55
+ if (!matchedAction || matchedAction.element !== target || !hasAltazionExtension(target)) {
56
+ return;
57
+ }
58
+ event.preventDefault();
59
+ void runModuleAction(client, matchedAction);
60
+ };
61
+ document.addEventListener("click", handleClick);
62
+ document.addEventListener("submit", handleSubmit);
63
+ return {
64
+ dispose() {
65
+ if (typeof window !== "undefined" && window.htmx) {
66
+ window.htmx.off("htmx:configRequest", requestHandler);
67
+ }
68
+ document.removeEventListener("click", handleClick);
69
+ document.removeEventListener("submit", handleSubmit);
70
+ }
71
+ };
72
+ }
73
+ function requireString(value, fieldName) {
74
+ if (typeof value === "string" && value.trim().length > 0) {
75
+ return value.trim();
76
+ }
77
+ throw new Error(`Missing payload field: ${fieldName}`);
78
+ }
79
+ function requireNumber(value, fieldName) {
80
+ if (typeof value === "number" && Number.isFinite(value)) {
81
+ return value;
82
+ }
83
+ if (typeof value === "string" && value.trim().length > 0) {
84
+ const parsed = Number(value);
85
+ if (Number.isFinite(parsed)) {
86
+ return parsed;
87
+ }
88
+ }
89
+ throw new Error(`Invalid payload field: ${fieldName}`);
90
+ }
91
+ function asRecord(value) {
92
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
93
+ return void 0;
94
+ }
95
+ return value;
96
+ }
97
+ async function runModuleAction(client, matchedAction) {
98
+ var _a;
99
+ const { definition, element } = matchedAction;
100
+ const action = readActionName(element, definition);
101
+ const payload = collectPayload(element, definition);
102
+ const releasePending = markPending(element);
103
+ dispatchModuleEvent(element, buildModuleEventName(definition.moduleName, "before"), {
104
+ module: definition.moduleName,
105
+ action,
106
+ element,
107
+ payload
108
+ });
109
+ try {
110
+ const result = await definition.execute(client, action, payload);
111
+ dispatchModuleEvent(element, buildModuleEventName(definition.moduleName, "after"), {
112
+ module: definition.moduleName,
113
+ action,
114
+ element,
115
+ payload,
116
+ result
117
+ });
118
+ const successEventKind = resolveSuccessEventKind(definition, action, result, payload);
119
+ const emittedSuffixes = /* @__PURE__ */ new Set();
120
+ if (successEventKind !== "none") {
121
+ emittedSuffixes.add(successEventKind);
122
+ dispatchModuleEvent(element, buildModuleEventName(definition.moduleName, successEventKind), {
123
+ module: definition.moduleName,
124
+ action,
125
+ element,
126
+ payload,
127
+ result
128
+ });
129
+ }
130
+ for (const suffix of ((_a = definition.getResultEventSuffixes) == null ? void 0 : _a.call(definition, action, result, payload)) ?? []) {
131
+ if (emittedSuffixes.has(suffix)) {
132
+ continue;
133
+ }
134
+ dispatchModuleEvent(element, buildModuleEventName(definition.moduleName, suffix), {
135
+ module: definition.moduleName,
136
+ action,
137
+ element,
138
+ payload,
139
+ result
140
+ });
141
+ }
142
+ await refreshTargets(element);
143
+ } catch (error) {
144
+ const errorDetail = normalizeError(error);
145
+ dispatchModuleEvent(element, buildModuleEventName(definition.moduleName, "error"), {
146
+ module: definition.moduleName,
147
+ action,
148
+ element,
149
+ payload,
150
+ error,
151
+ errorDetail
152
+ });
153
+ console.error(`[AltazionHtmx] ${definition.moduleName} action failed`, error);
154
+ } finally {
155
+ releasePending();
156
+ }
157
+ }
158
+ function findMatchedAction(target, modules) {
159
+ let current = target;
160
+ while (current) {
161
+ for (const definition of modules) {
162
+ if (current.hasAttribute(definition.actionAttribute)) {
163
+ return { definition, element: current };
164
+ }
165
+ }
166
+ current = current.parentElement;
167
+ }
168
+ return null;
169
+ }
170
+ function readActionName(element, definition) {
171
+ const action = element.getAttribute(definition.actionAttribute);
172
+ if (!action) {
173
+ throw new Error(`Missing ${definition.actionAttribute} attribute`);
174
+ }
175
+ return definition.parseAction(action);
176
+ }
177
+ function collectPayload(element, definition) {
178
+ var _a;
179
+ const payload = {};
180
+ const form = element instanceof HTMLFormElement ? element : element.closest("form");
181
+ if (form) {
182
+ Object.assign(payload, formDataToObject(new FormData(form)));
183
+ mergeJsonValues(payload, form);
184
+ applyAttributePayload(payload, form, definition.payloadAttributes);
185
+ applyJsonAttributePayload(payload, form, definition.jsonPayloadAttributes);
186
+ }
187
+ mergeJsonValues(payload, element);
188
+ applyAttributePayload(payload, element, definition.payloadAttributes);
189
+ applyJsonAttributePayload(payload, element, definition.jsonPayloadAttributes);
190
+ applyPayloadAliases(payload, definition.payloadAliases);
191
+ (_a = definition.normalizePayload) == null ? void 0 : _a.call(definition, payload);
192
+ return payload;
193
+ }
194
+ function mergeJsonValues(payload, element) {
195
+ const values = readJsonValues(element, JSON_VALUES_ATTRIBUTE);
196
+ if (values) {
197
+ Object.assign(payload, values);
198
+ }
199
+ }
200
+ function applyAttributePayload(payload, element, attributes) {
201
+ if (!attributes) {
202
+ return;
203
+ }
204
+ for (const [attributeName, payloadKey] of Object.entries(attributes)) {
205
+ const value = element.getAttribute(attributeName);
206
+ if (value !== null) {
207
+ payload[payloadKey] = value;
208
+ }
209
+ }
210
+ }
211
+ function applyJsonAttributePayload(payload, element, attributes) {
212
+ if (!attributes) {
213
+ return;
214
+ }
215
+ for (const [attributeName, payloadKey] of Object.entries(attributes)) {
216
+ const value = readJsonValues(element, attributeName);
217
+ if (value !== void 0) {
218
+ payload[payloadKey] = value;
219
+ }
220
+ }
221
+ }
222
+ function applyPayloadAliases(payload, aliases) {
223
+ if (!aliases) {
224
+ return;
225
+ }
226
+ for (const [alias, canonicalKey] of Object.entries(aliases)) {
227
+ if (payload[alias] !== void 0 && payload[canonicalKey] === void 0) {
228
+ payload[canonicalKey] = payload[alias];
229
+ }
230
+ }
231
+ }
232
+ function formDataToObject(formData) {
233
+ const payload = {};
234
+ for (const [key, value] of formData.entries()) {
235
+ const normalizedValue = typeof value === "string" ? value : value.name;
236
+ const previous = payload[key];
237
+ if (previous === void 0) {
238
+ payload[key] = normalizedValue;
239
+ } else if (Array.isArray(previous)) {
240
+ previous.push(normalizedValue);
241
+ } else {
242
+ payload[key] = [previous, normalizedValue];
243
+ }
244
+ }
245
+ return payload;
246
+ }
247
+ function dispatchModuleEvent(element, eventName, detail) {
248
+ element.dispatchEvent(new CustomEvent(eventName, {
249
+ bubbles: true,
250
+ detail
251
+ }));
252
+ }
253
+ function buildModuleEventName(moduleName, suffix) {
254
+ return `altazion:${moduleName}:${suffix}`;
255
+ }
256
+ function resolveSuccessEventKind(definition, action, result, payload) {
257
+ const configuredKind = definition.successEventKind;
258
+ if (typeof configuredKind === "function") {
259
+ return configuredKind(action, result, payload);
260
+ }
261
+ if (configuredKind) {
262
+ return configuredKind;
263
+ }
264
+ return inferSuccessEventKind(action, result);
265
+ }
266
+ function inferSuccessEventKind(action, result) {
267
+ if (result === void 0) {
268
+ return "changed";
269
+ }
270
+ const normalizedAction = action.toLowerCase();
271
+ if (normalizedAction.startsWith("get") || normalizedAction.startsWith("find") || normalizedAction.startsWith("search") || normalizedAction.startsWith("suggest") || normalizedAction.startsWith("load") || normalizedAction.startsWith("list")) {
272
+ return "loaded";
273
+ }
274
+ return "updated";
275
+ }
276
+ function normalizeError(error) {
277
+ if (error instanceof commerceSdkCore.AltazionApiError) {
278
+ return {
279
+ name: error.name,
280
+ message: error.message,
281
+ status: error.status,
282
+ problem: error.problem,
283
+ isOffline: false,
284
+ isConflict: error.status === 409
285
+ };
286
+ }
287
+ if (error instanceof commerceSdkCore.OfflineError) {
288
+ return {
289
+ name: error.name,
290
+ message: error.message,
291
+ isOffline: true,
292
+ isConflict: false
293
+ };
294
+ }
295
+ if (error instanceof Error) {
296
+ return {
297
+ name: error.name,
298
+ message: error.message,
299
+ isOffline: false,
300
+ isConflict: false
301
+ };
302
+ }
303
+ return {
304
+ name: "UnknownError",
305
+ message: "Unknown error",
306
+ isOffline: false,
307
+ isConflict: false
308
+ };
309
+ }
310
+ async function refreshTargets(element) {
311
+ const targets = resolveRefreshTargets(element);
312
+ if (targets.length === 0) {
313
+ return;
314
+ }
315
+ await Promise.all(targets.map((target) => refreshTargetElement(element, target)));
316
+ }
317
+ function resolveRefreshTargets(element) {
318
+ if (typeof document === "undefined") {
319
+ return [];
320
+ }
321
+ const selectors = collectRefreshSelectors(element);
322
+ if (selectors.length === 0) {
323
+ return [];
324
+ }
325
+ const targets = /* @__PURE__ */ new Set();
326
+ for (const selector of selectors) {
327
+ try {
328
+ for (const target of document.querySelectorAll(selector)) {
329
+ targets.add(target);
330
+ }
331
+ } catch (error) {
332
+ console.error(`[AltazionHtmx] invalid refresh selector: ${selector}`, error);
333
+ }
334
+ }
335
+ return [...targets];
336
+ }
337
+ function collectRefreshSelectors(element) {
338
+ const values = /* @__PURE__ */ new Set();
339
+ const form = element instanceof HTMLFormElement ? element : element.closest("form");
340
+ addRefreshSelectors(values, form);
341
+ addRefreshSelectors(values, element);
342
+ return [...values];
343
+ }
344
+ function addRefreshSelectors(values, element) {
345
+ const rawValue = element == null ? void 0 : element.getAttribute(REFRESH_ATTRIBUTE);
346
+ if (!rawValue) {
347
+ return;
348
+ }
349
+ for (const selector of rawValue.split(";")) {
350
+ const trimmed = selector.trim();
351
+ if (trimmed.length > 0) {
352
+ values.add(trimmed);
353
+ }
354
+ }
355
+ }
356
+ async function refreshTargetElement(sourceElement, targetElement) {
357
+ var _a;
358
+ const request = readRefreshRequest(targetElement);
359
+ if (request && typeof window !== "undefined" && ((_a = window.htmx) == null ? void 0 : _a.ajax)) {
360
+ const context = {
361
+ source: sourceElement,
362
+ target: targetElement
363
+ };
364
+ const swap = targetElement.getAttribute("hx-swap");
365
+ if (swap) {
366
+ context.swap = swap;
367
+ }
368
+ const select = targetElement.getAttribute("hx-select");
369
+ if (select) {
370
+ context.select = select;
371
+ }
372
+ const values = readJsonValues(targetElement, JSON_VALUES_ATTRIBUTE);
373
+ if (values) {
374
+ context.values = values;
375
+ }
376
+ await Promise.resolve(window.htmx.ajax(request.method, request.path, context));
377
+ return;
378
+ }
379
+ targetElement.dispatchEvent(new CustomEvent(REFRESH_EVENT_NAME, {
380
+ bubbles: true,
381
+ detail: {
382
+ source: sourceElement,
383
+ target: targetElement
384
+ }
385
+ }));
386
+ }
387
+ function readRefreshRequest(element) {
388
+ for (const requestAttribute of REQUEST_ATTRIBUTES) {
389
+ const path = element.getAttribute(requestAttribute.attribute);
390
+ if (path) {
391
+ return {
392
+ method: requestAttribute.method,
393
+ path
394
+ };
395
+ }
396
+ }
397
+ return null;
398
+ }
399
+ function readJsonValues(element, attributeName) {
400
+ const raw = element.getAttribute(attributeName);
401
+ if (!raw) {
402
+ return void 0;
403
+ }
404
+ try {
405
+ const parsed = JSON.parse(raw);
406
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
407
+ return parsed;
408
+ }
409
+ } catch {
410
+ throw new Error(`Invalid JSON in ${attributeName}`);
411
+ }
412
+ return void 0;
413
+ }
414
+ function markPending(element) {
415
+ const previousBusy = element.getAttribute("aria-busy");
416
+ element.setAttribute("aria-busy", "true");
417
+ const disableable = isDisableableElement(element) ? element : null;
418
+ const previousDisabled = (disableable == null ? void 0 : disableable.disabled) ?? false;
419
+ if (disableable) {
420
+ disableable.disabled = true;
421
+ }
422
+ return () => {
423
+ if (previousBusy === null) {
424
+ element.removeAttribute("aria-busy");
425
+ } else {
426
+ element.setAttribute("aria-busy", previousBusy);
427
+ }
428
+ if (disableable) {
429
+ disableable.disabled = previousDisabled;
430
+ }
431
+ };
432
+ }
433
+ function isDisableableElement(element) {
434
+ return "disabled" in element;
435
+ }
436
+ function hasAltazionExtension(element) {
437
+ let current = element;
438
+ while (current) {
439
+ const extensions = current.getAttribute("hx-ext");
440
+ if (extensions && parseExtensions(extensions).includes(EXTENSION_NAME)) {
441
+ return true;
442
+ }
443
+ current = current.parentElement;
444
+ }
445
+ return false;
446
+ }
447
+ function parseExtensions(value) {
448
+ return value.split(/[\s,]+/).map((extension) => extension.trim()).filter((extension) => extension.length > 0);
449
+ }
450
+ const cartModuleDefinition = {
451
+ moduleName: "cart",
452
+ actionAttribute: "hx-altazion-cart-action",
453
+ payloadAttributes: {
454
+ "hx-altazion-cart-reference": "reference",
455
+ "hx-altazion-cart-quantity": "quantity",
456
+ "hx-altazion-cart-line-id": "lineId",
457
+ "hx-altazion-cart-code": "code"
458
+ },
459
+ jsonPayloadAttributes: {
460
+ "hx-altazion-cart-options": "options"
461
+ },
462
+ payloadAliases: {
463
+ ref: "reference",
464
+ productReference: "reference",
465
+ qty: "quantity",
466
+ line: "lineId",
467
+ coupon: "code"
468
+ },
469
+ parseAction(actionValue) {
470
+ switch (actionValue) {
471
+ case "getCart":
472
+ case "getValidationStatus":
473
+ case "addItem":
474
+ case "updateItem":
475
+ case "removeItem":
476
+ case "applyCoupon":
477
+ case "removeCoupon":
478
+ return actionValue;
479
+ default:
480
+ throw new Error(`Unsupported cart action: ${actionValue}`);
481
+ }
482
+ },
483
+ execute(client, action, payload) {
484
+ switch (action) {
485
+ case "getCart":
486
+ return client.cart.getCart();
487
+ case "getValidationStatus":
488
+ return client.cart.getValidationStatus();
489
+ case "addItem": {
490
+ const reference = requireString(payload.reference, "reference");
491
+ const quantity = requireNumber(payload.quantity, "quantity");
492
+ const options = buildAddItemOptions(payload);
493
+ return client.cart.addItem(reference, quantity, options);
494
+ }
495
+ case "updateItem": {
496
+ const lineId = requireString(payload.lineId, "lineId");
497
+ const quantity = requireNumber(payload.quantity, "quantity");
498
+ return client.cart.updateItem(lineId, quantity);
499
+ }
500
+ case "removeItem":
501
+ return client.cart.removeItem(requireString(payload.lineId, "lineId"));
502
+ case "applyCoupon":
503
+ return client.cart.applyCoupon(requireString(payload.code, "code"));
504
+ case "removeCoupon":
505
+ return client.cart.removeCoupon(requireString(payload.code, "code"));
506
+ }
507
+ },
508
+ successEventKind(action) {
509
+ switch (action) {
510
+ case "getCart":
511
+ return "loaded";
512
+ case "getValidationStatus":
513
+ return "none";
514
+ case "addItem":
515
+ case "updateItem":
516
+ case "removeItem":
517
+ case "applyCoupon":
518
+ case "removeCoupon":
519
+ return "updated";
520
+ }
521
+ },
522
+ getResultEventSuffixes(_action, result) {
523
+ return isCart(result) ? ["updated"] : ["status"];
524
+ }
525
+ };
526
+ function buildAddItemOptions(payload) {
527
+ const options = asRecord(payload.options);
528
+ const extraEntries = Object.entries(payload).filter(([key]) => ![
529
+ "reference",
530
+ "ref",
531
+ "productReference",
532
+ "quantity",
533
+ "qty",
534
+ "options"
535
+ ].includes(key));
536
+ if (extraEntries.length === 0 && !options) {
537
+ return void 0;
538
+ }
539
+ return {
540
+ ...Object.fromEntries(extraEntries),
541
+ ...options
542
+ };
543
+ }
544
+ function isCart(result) {
545
+ return "guid" in result;
14
546
  }
547
+ const sessionModuleDefinition = {
548
+ moduleName: "session",
549
+ actionAttribute: "hx-altazion-session-action",
550
+ parseAction(actionValue) {
551
+ switch (actionValue) {
552
+ case "getSession":
553
+ return actionValue;
554
+ default:
555
+ throw new Error(`Unsupported session action: ${actionValue}`);
556
+ }
557
+ },
558
+ execute(client, action) {
559
+ switch (action) {
560
+ case "getSession":
561
+ return client.session.getSession();
562
+ }
563
+ },
564
+ successEventKind() {
565
+ return "loaded";
566
+ }
567
+ };
568
+ const marketingModuleDefinition = {
569
+ moduleName: "marketing",
570
+ actionAttribute: "hx-altazion-marketing-action",
571
+ payloadAttributes: {
572
+ "hx-altazion-marketing-code": "code"
573
+ },
574
+ jsonPayloadAttributes: {
575
+ "hx-altazion-marketing-codes": "codes"
576
+ },
577
+ parseAction(actionValue) {
578
+ switch (actionValue) {
579
+ case "getItem":
580
+ case "getItems":
581
+ return actionValue;
582
+ default:
583
+ throw new Error(`Unsupported marketing action: ${actionValue}`);
584
+ }
585
+ },
586
+ execute(client, action, payload) {
587
+ switch (action) {
588
+ case "getItem":
589
+ return client.marketing.getItem(requireString(payload.code, "code"));
590
+ case "getItems":
591
+ return client.marketing.getItems(readCodes(payload));
592
+ }
593
+ },
594
+ successEventKind() {
595
+ return "loaded";
596
+ },
597
+ getResultEventSuffixes(action) {
598
+ switch (action) {
599
+ case "getItem":
600
+ return ["item-loaded"];
601
+ case "getItems":
602
+ return ["items-loaded"];
603
+ }
604
+ }
605
+ };
606
+ function readCodes(payload) {
607
+ const value = payload.codes;
608
+ if (Array.isArray(value)) {
609
+ const codes = value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
610
+ if (codes.length > 0) {
611
+ return codes;
612
+ }
613
+ }
614
+ if (typeof value === "string") {
615
+ const codes = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
616
+ if (codes.length > 0) {
617
+ return codes;
618
+ }
619
+ }
620
+ if (typeof payload.code === "string" && payload.code.trim().length > 0) {
621
+ return [payload.code.trim()];
622
+ }
623
+ throw new Error("Missing payload field: codes");
624
+ }
625
+ const storesModuleDefinition = {
626
+ moduleName: "stores",
627
+ actionAttribute: "hx-altazion-stores-action",
628
+ payloadAttributes: {
629
+ "hx-altazion-stores-store-guid": "storeGuid",
630
+ "hx-altazion-stores-latitude": "latitude",
631
+ "hx-altazion-stores-longitude": "longitude",
632
+ "hx-altazion-stores-radius-km": "radiusKm",
633
+ "hx-altazion-stores-postal-code": "postalCode",
634
+ "hx-altazion-stores-country-code": "countryCode"
635
+ },
636
+ payloadAliases: {
637
+ lat: "latitude",
638
+ lon: "longitude",
639
+ lng: "longitude",
640
+ radius: "radiusKm",
641
+ storeId: "storeGuid",
642
+ id: "storeGuid",
643
+ postal: "postalCode",
644
+ country: "countryCode"
645
+ },
646
+ parseAction(actionValue) {
647
+ switch (actionValue) {
648
+ case "findByLocation":
649
+ case "findByPostalCode":
650
+ case "getStore":
651
+ return actionValue;
652
+ default:
653
+ throw new Error(`Unsupported stores action: ${actionValue}`);
654
+ }
655
+ },
656
+ execute(client, action, payload) {
657
+ switch (action) {
658
+ case "findByLocation": {
659
+ const latitude = requireNumber(payload.latitude, "latitude");
660
+ const longitude = requireNumber(payload.longitude, "longitude");
661
+ const radiusKm = payload.radiusKm === void 0 ? 50 : requireNumber(payload.radiusKm, "radiusKm");
662
+ return client.stores.findByLocation(latitude, longitude, radiusKm);
663
+ }
664
+ case "findByPostalCode": {
665
+ const postalCode = requireString(payload.postalCode, "postalCode");
666
+ const countryCode = payload.countryCode === void 0 ? "FR" : requireString(payload.countryCode, "countryCode");
667
+ return client.stores.findByPostalCode(postalCode, countryCode);
668
+ }
669
+ case "getStore":
670
+ return client.stores.getStore(requireString(payload.storeGuid, "storeGuid"));
671
+ }
672
+ },
673
+ successEventKind() {
674
+ return "loaded";
675
+ },
676
+ getResultEventSuffixes(action) {
677
+ switch (action) {
678
+ case "getStore":
679
+ return ["store-loaded"];
680
+ case "findByLocation":
681
+ case "findByPostalCode":
682
+ return ["stores-loaded"];
683
+ }
684
+ }
685
+ };
686
+ function registerAltazionExtension(client) {
687
+ return createAltazionExtension(client, [
688
+ cartModuleDefinition,
689
+ sessionModuleDefinition,
690
+ marketingModuleDefinition,
691
+ storesModuleDefinition
692
+ ]);
693
+ }
694
+ const registerAltazionAuthExtension = registerAltazionExtension;
15
695
  function registerPriceHelpers(hbs, defaults) {
16
696
  hbs.registerHelper(
17
697
  "formatPrice",
@@ -141,133 +821,153 @@ function registerCartHelpers(hbs, getCart, defaults) {
141
821
  }, 0);
142
822
  });
143
823
  }
144
- const productCardTemplate = `<article class="altz-product-card{{#unless (isAvailable availability)}} altz-product-card--unavailable{{/unless}}">
145
- {{#if imageUrl}}
146
- <a href="{{productUrl reference}}" class="altz-product-card__image-link" aria-label="{{name}}">
147
- <img
148
- src="{{thumbnailUrl imageUrl 400}}"
149
- alt="{{name}}"
150
- class="altz-product-card__image"
151
- loading="lazy"
152
- width="400"
153
- />
154
- </a>
155
- {{/if}}
156
-
157
- <div class="altz-product-card__body">
158
- <a href="{{productUrl reference}}" class="altz-product-card__title">
159
- {{name}}
160
- </a>
161
-
162
- <div class="altz-product-card__pricing">
163
- {{#if discount}}
164
- <span class="altz-product-card__price altz-product-card__price--discounted">
165
- {{formatPrice price}}
166
- </span>
167
- <span class="altz-product-card__price altz-product-card__price--original">
168
- {{formatPrice originalPrice}}
169
- </span>
170
- <span class="altz-product-card__discount-badge">
171
- {{discountPercent originalPrice price}}
172
- </span>
173
- {{else}}
174
- <span class="altz-product-card__price">
175
- {{formatPrice price}}
176
- </span>
177
- {{/if}}
178
- </div>
179
-
180
- {{#unless (isAvailable availability)}}
181
- <p class="altz-product-card__unavailable">{{unavailableLabel}}</p>
182
- {{/unless}}
183
-
184
- {{#if (isAvailable availability)}}
185
- <button
186
- class="altz-product-card__add-to-cart"
187
- hx-post="/commerce/api/process/cart/lines"
188
- hx-vals='{"productReference":"{{reference}}","quantity":1}'
189
- hx-target="#altz-cart-summary"
190
- hx-swap="outerHTML"
191
- hx-indicator=".altz-spinner"
192
- >
193
- {{addToCartLabel}}
194
- </button>
195
- {{/if}}
196
- </div>
197
- </article>
198
- `;
824
+ const DATA_SOURCE_ATTRIBUTE = "hx-altazion-data-source";
825
+ const DATA_STATUS_ATTRIBUTE = "hx-altazion-data-status";
826
+ const RENDER_TEMPLATE_ATTRIBUTE = "hx-render-template";
827
+ const DATA_SOURCE_ALIASES = {
828
+ cart: "cart",
829
+ session: "session",
830
+ store: "store",
831
+ stores: "stores",
832
+ "marketing-item": "marketingItem",
833
+ "marketing-items": "marketingItems"
834
+ };
835
+ function createAltazionDeclarativeRenderer(handlebars) {
836
+ const state = {
837
+ cart: null,
838
+ session: null,
839
+ store: null,
840
+ stores: [],
841
+ marketingItem: null,
842
+ marketingItems: [],
843
+ slots: {}
844
+ };
845
+ const templateCache = /* @__PURE__ */ new Map();
846
+ const render = () => {
847
+ if (typeof document === "undefined") {
848
+ return;
849
+ }
850
+ for (const element of document.querySelectorAll(`[${DATA_SOURCE_ATTRIBUTE}]`)) {
851
+ const rawSource = element.getAttribute(DATA_SOURCE_ATTRIBUTE);
852
+ const source = resolveDataSource(rawSource);
853
+ if (!source) {
854
+ continue;
855
+ }
856
+ const rawStatus = element.getAttribute(DATA_STATUS_ATTRIBUTE);
857
+ const sourceValue = resolveSourceValue(state, source);
858
+ const isVisible = matchesStatus(rawStatus, sourceValue);
859
+ applyVisibility(element, isVisible);
860
+ renderElementTemplate(element, isVisible, state, sourceValue, handlebars, templateCache);
861
+ }
862
+ };
863
+ return {
864
+ update(key, value) {
865
+ state[key] = value;
866
+ render();
867
+ },
868
+ updateSlot(name, value) {
869
+ state.slots[name] = value;
870
+ render();
871
+ },
872
+ render,
873
+ dispose() {
874
+ templateCache.clear();
875
+ }
876
+ };
877
+ }
878
+ function matchesStatus(status, value) {
879
+ if (Array.isArray(value)) {
880
+ switch (status) {
881
+ case "empty":
882
+ return value.length === 0;
883
+ case "not-empty":
884
+ return value.length > 0;
885
+ default:
886
+ return true;
887
+ }
888
+ }
889
+ switch (status) {
890
+ case "null":
891
+ return value == null;
892
+ case "not-null":
893
+ return value != null;
894
+ default:
895
+ return true;
896
+ }
897
+ }
898
+ function resolveDataSource(source) {
899
+ if (!source) {
900
+ return null;
901
+ }
902
+ if (source.startsWith("slot:")) {
903
+ const slotKey = source.slice(5).trim();
904
+ return slotKey.length > 0 ? { kind: "slot", key: slotKey } : null;
905
+ }
906
+ const resolvedKey = DATA_SOURCE_ALIASES[source];
907
+ return resolvedKey ? { kind: "state", key: resolvedKey } : null;
908
+ }
909
+ function resolveSourceValue(state, source) {
910
+ if (source.kind === "slot") {
911
+ return state.slots[source.key] ?? null;
912
+ }
913
+ return state[source.key];
914
+ }
915
+ function applyVisibility(element, isVisible) {
916
+ element.setAttribute("aria-hidden", isVisible ? "false" : "true");
917
+ if (element instanceof HTMLElement) {
918
+ element.hidden = !isVisible;
919
+ } else if (isVisible) {
920
+ element.removeAttribute("hidden");
921
+ } else {
922
+ element.setAttribute("hidden", "");
923
+ }
924
+ }
925
+ function renderElementTemplate(element, isVisible, state, sourceValue, handlebars, templateCache) {
926
+ if (!isVisible || !element.hasAttribute(RENDER_TEMPLATE_ATTRIBUTE) || !handlebars) {
927
+ return;
928
+ }
929
+ const templateId = element.getAttribute(RENDER_TEMPLATE_ATTRIBUTE);
930
+ if (!templateId) {
931
+ return;
932
+ }
933
+ const template = resolveTemplate(templateId, handlebars, templateCache);
934
+ if (!template) {
935
+ return;
936
+ }
937
+ element.innerHTML = template({
938
+ ...state,
939
+ slot: sourceValue
940
+ });
941
+ }
942
+ function resolveTemplate(templateId, handlebars, templateCache) {
943
+ const cached = templateCache.get(templateId);
944
+ if (cached) {
945
+ return cached;
946
+ }
947
+ if (typeof document === "undefined") {
948
+ return null;
949
+ }
950
+ const templateElement = document.getElementById(templateId);
951
+ if (!templateElement) {
952
+ console.warn(`[AltazionHtmx] Template not found: ${templateId}`);
953
+ return null;
954
+ }
955
+ const compiled = handlebars.compile(templateElement.innerHTML);
956
+ templateCache.set(templateId, compiled);
957
+ return compiled;
958
+ }
959
+ const productCardTemplate = '<article class="altz-product-card{{#unless (isAvailable availability)}} altz-product-card--unavailable{{/unless}}">\n {{#if imageUrl}}\n <a href="{{productUrl reference}}" class="altz-product-card__image-link" aria-label="{{name}}">\n <img\n src="{{thumbnailUrl imageUrl 400}}"\n alt="{{name}}"\n class="altz-product-card__image"\n loading="lazy"\n width="400"\n />\n </a>\n {{/if}}\n\n <div class="altz-product-card__body">\n <a href="{{productUrl reference}}" class="altz-product-card__title">\n {{name}}\n </a>\n\n <div class="altz-product-card__pricing">\n {{#if discount}}\n <span class="altz-product-card__price altz-product-card__price--discounted">\n {{formatPrice price}}\n </span>\n <span class="altz-product-card__price altz-product-card__price--original">\n {{formatPrice originalPrice}}\n </span>\n <span class="altz-product-card__discount-badge">\n {{discountPercent originalPrice price}}\n </span>\n {{else}}\n <span class="altz-product-card__price">\n {{formatPrice price}}\n </span>\n {{/if}}\n </div>\n\n {{#unless (isAvailable availability)}}\n <p class="altz-product-card__unavailable">{{unavailableLabel}}</p>\n {{/unless}}\n\n {{#if (isAvailable availability)}}\n <button\n class="altz-product-card__add-to-cart"\n hx-ext="altazion"\n hx-altazion-cart-action="addItem"\n hx-altazion-cart-reference="{{reference}}"\n hx-altazion-cart-quantity="1"\n hx-altazion-refresh="#altz-cart-mini"\n hx-indicator=".altz-spinner"\n >\n {{addToCartLabel}}\n </button>\n {{/if}}\n </div>\n</article>\n';
199
960
  const cartMiniTemplate = '<div id="altz-cart-mini" class="altz-cart-mini">\n <div class="altz-cart-mini__header">\n <span class="altz-cart-mini__label">{{cartLabel}}</span>\n <span class="altz-cart-mini__count">{{cartCount}}</span>\n </div>\n\n {{#cartHasItems}}\n <ul class="altz-cart-mini__lines">\n {{#each lines}}\n <li class="altz-cart-mini__line">\n <span class="altz-cart-mini__line-name">{{this.productName}}</span>\n <span class="altz-cart-mini__line-qty">× {{this.quantity}}</span>\n <span class="altz-cart-mini__line-price">{{formatPrice this.unitPriceWithTax}}</span>\n </li>\n {{/each}}\n </ul>\n\n <div class="altz-cart-mini__total">\n <span>{{totalLabel}}</span>\n <strong>{{cartTotal}}</strong>\n </div>\n\n <a href="{{cartUrl}}" class="altz-cart-mini__cta">\n {{viewCartLabel}}\n </a>\n {{/cartHasItems}}\n\n {{^cartHasItems}}\n <p class="altz-cart-mini__empty">{{emptyLabel}}</p>\n {{/cartHasItems}}\n</div>\n';
200
- const productListTemplate = `<section class="altz-product-list">
201
- {{#if title}}
202
- <h2 class="altz-product-list__title">{{title}}</h2>
203
- {{/if}}
204
-
205
- {{#if products.length}}
206
- <ul class="altz-product-list__grid" role="list">
207
- {{#each products}}
208
- <li class="altz-product-list__item">
209
- <article class="altz-product-card{{#unless (isAvailable this.availability)}} altz-product-card--unavailable{{/unless}}">
210
- {{#if this.imageUrl}}
211
- <a href="{{productUrl this.reference}}" class="altz-product-card__image-link" aria-label="{{this.name}}">
212
- <img
213
- src="{{thumbnailUrl this.imageUrl 300}}"
214
- alt="{{this.name}}"
215
- class="altz-product-card__image"
216
- loading="lazy"
217
- width="300"
218
- />
219
- </a>
220
- {{/if}}
221
-
222
- <div class="altz-product-card__body">
223
- <a href="{{productUrl this.reference}}" class="altz-product-card__title">
224
- {{this.name}}
225
- </a>
226
-
227
- <div class="altz-product-card__pricing">
228
- {{#if this.discount}}
229
- <span class="altz-product-card__price altz-product-card__price--discounted">
230
- {{formatPrice this.price}}
231
- </span>
232
- <span class="altz-product-card__price altz-product-card__price--original">
233
- {{formatPrice this.originalPrice}}
234
- </span>
235
- <span class="altz-product-card__discount-badge">
236
- {{discountPercent this.originalPrice this.price}}
237
- </span>
238
- {{else}}
239
- <span class="altz-product-card__price">
240
- {{formatPrice this.price}}
241
- </span>
242
- {{/if}}
243
- </div>
244
-
245
- {{#if (isAvailable this.availability)}}
246
- <button
247
- class="altz-product-card__add-to-cart"
248
- hx-post="/commerce/api/process/cart/lines"
249
- hx-vals='{"productReference":"{{this.reference}}","quantity":1}'
250
- hx-target="#altz-cart-summary"
251
- hx-swap="outerHTML"
252
- hx-indicator=".altz-spinner"
253
- >
254
- {{../addToCartLabel}}
255
- </button>
256
- {{/if}}
257
- </div>
258
- </article>
259
- </li>
260
- {{/each}}
261
- </ul>
262
- {{else}}
263
- <p class="altz-product-list__empty">{{emptyLabel}}</p>
264
- {{/if}}
265
- </section>
266
- `;
267
- function init(config) {
961
+ const productListTemplate = '<section class="altz-product-list">\n {{#if title}}\n <h2 class="altz-product-list__title">{{title}}</h2>\n {{/if}}\n\n {{#if products.length}}\n <ul class="altz-product-list__grid" role="list">\n {{#each products}}\n <li class="altz-product-list__item">\n <article class="altz-product-card{{#unless (isAvailable this.availability)}} altz-product-card--unavailable{{/unless}}">\n {{#if this.imageUrl}}\n <a href="{{productUrl this.reference}}" class="altz-product-card__image-link" aria-label="{{this.name}}">\n <img\n src="{{thumbnailUrl this.imageUrl 300}}"\n alt="{{this.name}}"\n class="altz-product-card__image"\n loading="lazy"\n width="300"\n />\n </a>\n {{/if}}\n\n <div class="altz-product-card__body">\n <a href="{{productUrl this.reference}}" class="altz-product-card__title">\n {{this.name}}\n </a>\n\n <div class="altz-product-card__pricing">\n {{#if this.discount}}\n <span class="altz-product-card__price altz-product-card__price--discounted">\n {{formatPrice this.price}}\n </span>\n <span class="altz-product-card__price altz-product-card__price--original">\n {{formatPrice this.originalPrice}}\n </span>\n <span class="altz-product-card__discount-badge">\n {{discountPercent this.originalPrice this.price}}\n </span>\n {{else}}\n <span class="altz-product-card__price">\n {{formatPrice this.price}}\n </span>\n {{/if}}\n </div>\n\n {{#if (isAvailable this.availability)}}\n <button\n class="altz-product-card__add-to-cart"\n hx-ext="altazion"\n hx-altazion-cart-action="addItem"\n hx-altazion-cart-reference="{{this.reference}}"\n hx-altazion-cart-quantity="1"\n hx-altazion-refresh="#altz-cart-mini"\n hx-indicator=".altz-spinner"\n >\n {{../addToCartLabel}}\n </button>\n {{/if}}\n </div>\n </article>\n </li>\n {{/each}}\n </ul>\n {{else}}\n <p class="altz-product-list__empty">{{emptyLabel}}</p>\n {{/if}}\n</section>\n';
962
+ function initAltazionHtmx(config) {
268
963
  var _a;
269
- const { client, handlebars, offlineSelector = "body" } = config;
270
- registerAltazionAuthExtension(client);
964
+ const {
965
+ client,
966
+ handlebars,
967
+ offlineSelector = "body",
968
+ terminalMode
969
+ } = config;
970
+ const extensionHandle = registerAltazionExtension(client);
271
971
  if (typeof window !== "undefined" && window.htmx) {
272
972
  window.htmx.config = window.htmx.config ?? {};
273
973
  window.htmx.config.withCredentials = true;
@@ -276,24 +976,91 @@ function init(config) {
276
976
  locale: client.context.locale,
277
977
  currency: client.context.currency
278
978
  };
979
+ const handlebarsRuntime = handlebars ?? (typeof window !== "undefined" ? window.Handlebars : void 0);
980
+ const declarativeRenderer = createAltazionDeclarativeRenderer(handlebarsRuntime);
981
+ const syncSlotFromEvent = (detail) => {
982
+ var _a2;
983
+ const slotName = (_a2 = detail.element.getAttribute("hx-altazion-data-slot")) == null ? void 0 : _a2.trim();
984
+ if (!slotName) {
985
+ return;
986
+ }
987
+ declarativeRenderer.updateSlot(slotName, detail.result ?? null);
988
+ };
279
989
  let currentCart = null;
280
990
  const getCart = () => currentCart;
281
- client.onQueueEvent(async (event) => {
282
- if (event.type === "flushed") {
283
- try {
284
- currentCart = await client.cart.getCart();
285
- } catch {
286
- }
991
+ const syncCartFromEvent = (event) => {
992
+ const customEvent = event;
993
+ if (customEvent.detail.result) {
994
+ currentCart = customEvent.detail.result;
995
+ declarativeRenderer.update("cart", currentCart);
287
996
  }
997
+ syncSlotFromEvent(customEvent.detail);
998
+ };
999
+ const syncSessionFromEvent = (event) => {
1000
+ const customEvent = event;
1001
+ declarativeRenderer.update("session", customEvent.detail.result ?? null);
1002
+ syncSlotFromEvent(customEvent.detail);
1003
+ };
1004
+ const syncStoreFromEvent = (event) => {
1005
+ const customEvent = event;
1006
+ declarativeRenderer.update("store", customEvent.detail.result ?? null);
1007
+ syncSlotFromEvent(customEvent.detail);
1008
+ };
1009
+ const syncStoresFromEvent = (event) => {
1010
+ const customEvent = event;
1011
+ declarativeRenderer.update("stores", Array.isArray(customEvent.detail.result) ? customEvent.detail.result : []);
1012
+ syncSlotFromEvent(customEvent.detail);
1013
+ };
1014
+ const syncMarketingItemFromEvent = (event) => {
1015
+ const customEvent = event;
1016
+ declarativeRenderer.update("marketingItem", Array.isArray(customEvent.detail.result) ? null : customEvent.detail.result ?? null);
1017
+ syncSlotFromEvent(customEvent.detail);
1018
+ };
1019
+ const syncMarketingItemsFromEvent = (event) => {
1020
+ const customEvent = event;
1021
+ declarativeRenderer.update("marketingItems", Array.isArray(customEvent.detail.result) ? customEvent.detail.result : []);
1022
+ syncSlotFromEvent(customEvent.detail);
1023
+ };
1024
+ if (typeof document !== "undefined") {
1025
+ document.addEventListener("altazion:cart:loaded", syncCartFromEvent);
1026
+ document.addEventListener("altazion:cart:updated", syncCartFromEvent);
1027
+ document.addEventListener("altazion:session:loaded", syncSessionFromEvent);
1028
+ document.addEventListener("altazion:stores:store-loaded", syncStoreFromEvent);
1029
+ document.addEventListener("altazion:stores:stores-loaded", syncStoresFromEvent);
1030
+ document.addEventListener("altazion:marketing:item-loaded", syncMarketingItemFromEvent);
1031
+ document.addEventListener("altazion:marketing:items-loaded", syncMarketingItemsFromEvent);
1032
+ }
1033
+ void client.cart.getCart().then((cart) => {
1034
+ currentCart = cart;
1035
+ declarativeRenderer.update("cart", cart);
1036
+ }).catch(() => {
288
1037
  });
289
1038
  if (handlebars) {
290
1039
  registerPriceHelpers(handlebars, defaults);
291
1040
  registerProductHelpers(handlebars, client.context.siteUrl ?? "");
292
1041
  registerCartHelpers(handlebars, getCart, defaults);
293
1042
  }
294
- const offlineEl = typeof document !== "undefined" ? document.querySelector(offlineSelector) : null;
1043
+ declarativeRenderer.render();
1044
+ const offlineEl = typeof document !== "undefined" ? document.querySelector(offlineSelector) ?? document.body : null;
1045
+ let lastConnectivityStatus = null;
295
1046
  const updateOfflineClass = () => {
296
- offlineEl == null ? void 0 : offlineEl.classList.toggle("altz-offline", client.isOffline);
1047
+ if (!offlineEl) {
1048
+ return;
1049
+ }
1050
+ const status = client.isOffline ? "offline" : "online";
1051
+ const isOffline = status === "offline";
1052
+ offlineEl.classList.toggle("altz-offline", isOffline);
1053
+ offlineEl.classList.toggle("altz-terminal-offline", isOffline);
1054
+ offlineEl.classList.toggle("altz-terminal-online", !isOffline);
1055
+ offlineEl.setAttribute("data-altazion-terminal-state", status);
1056
+ updateManagedVisibility(resolveManagedElements(terminalMode == null ? void 0 : terminalMode.interactiveSelectors), isOffline, "altz-terminal-interactive-hidden");
1057
+ updateManagedVisibility(resolveManagedElements(terminalMode == null ? void 0 : terminalMode.offlineScreenSelectors), !isOffline, "altz-terminal-offline-screen-hidden");
1058
+ if (lastConnectivityStatus === status) {
1059
+ return;
1060
+ }
1061
+ lastConnectivityStatus = status;
1062
+ dispatchConnectivityEvent(offlineEl, "altazion:connectivity:changed", status);
1063
+ dispatchConnectivityEvent(offlineEl, isOffline ? "altazion:offline" : "altazion:online", status);
297
1064
  };
298
1065
  updateOfflineClass();
299
1066
  const unsubscribeConnectivity = (_a = client.connectivity) == null ? void 0 : _a.subscribe(() => updateOfflineClass());
@@ -310,12 +1077,70 @@ function init(config) {
310
1077
  return {
311
1078
  templates,
312
1079
  dispose() {
1080
+ extensionHandle.dispose();
1081
+ if (typeof document !== "undefined") {
1082
+ document.removeEventListener("altazion:cart:loaded", syncCartFromEvent);
1083
+ document.removeEventListener("altazion:cart:updated", syncCartFromEvent);
1084
+ document.removeEventListener("altazion:session:loaded", syncSessionFromEvent);
1085
+ document.removeEventListener("altazion:stores:store-loaded", syncStoreFromEvent);
1086
+ document.removeEventListener("altazion:stores:stores-loaded", syncStoresFromEvent);
1087
+ document.removeEventListener("altazion:marketing:item-loaded", syncMarketingItemFromEvent);
1088
+ document.removeEventListener("altazion:marketing:items-loaded", syncMarketingItemsFromEvent);
1089
+ }
1090
+ declarativeRenderer.dispose();
313
1091
  unsubscribeConnectivity == null ? void 0 : unsubscribeConnectivity();
314
1092
  }
315
1093
  };
316
1094
  }
1095
+ const init = initAltazionHtmx;
1096
+ function resolveManagedElements(selectorInput) {
1097
+ if (typeof document === "undefined" || !selectorInput) {
1098
+ return [];
1099
+ }
1100
+ const selectors = Array.isArray(selectorInput) ? selectorInput : [selectorInput];
1101
+ const elements = /* @__PURE__ */ new Set();
1102
+ for (const selector of selectors) {
1103
+ if (!selector.trim()) {
1104
+ continue;
1105
+ }
1106
+ try {
1107
+ for (const element of document.querySelectorAll(selector)) {
1108
+ elements.add(element);
1109
+ }
1110
+ } catch {
1111
+ }
1112
+ }
1113
+ return [...elements];
1114
+ }
1115
+ function updateManagedVisibility(elements, hidden, hiddenClassName) {
1116
+ for (const element of elements) {
1117
+ element.classList.toggle(hiddenClassName, hidden);
1118
+ element.setAttribute("aria-hidden", hidden ? "true" : "false");
1119
+ if (element instanceof HTMLElement) {
1120
+ element.hidden = hidden;
1121
+ } else if (hidden) {
1122
+ element.setAttribute("hidden", "");
1123
+ } else {
1124
+ element.removeAttribute("hidden");
1125
+ }
1126
+ }
1127
+ }
1128
+ function dispatchConnectivityEvent(root, eventName, status) {
1129
+ const detail = {
1130
+ status,
1131
+ isOnline: status === "online",
1132
+ isOffline: status === "offline",
1133
+ root
1134
+ };
1135
+ root.dispatchEvent(new CustomEvent(eventName, {
1136
+ bubbles: true,
1137
+ detail
1138
+ }));
1139
+ }
317
1140
  exports.init = init;
1141
+ exports.initAltazionHtmx = initAltazionHtmx;
318
1142
  exports.registerAltazionAuthExtension = registerAltazionAuthExtension;
1143
+ exports.registerAltazionExtension = registerAltazionExtension;
319
1144
  exports.registerCartHelpers = registerCartHelpers;
320
1145
  exports.registerPriceHelpers = registerPriceHelpers;
321
1146
  exports.registerProductHelpers = registerProductHelpers;