@altazion/commerce-sdk-htmx 26.409.7573 → 26.415.7675

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