@seatlayer/core 0.1.3 → 0.2.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.
package/dist/index.js CHANGED
@@ -364,16 +364,35 @@ function chartBounds(doc) {
364
364
  };
365
365
  }
366
366
 
367
+ // src/core/ga.ts
368
+ var PREFIX = "__sl_ga__";
369
+ var MAX_GA_CAPACITY = 1e5;
370
+ var MAX_EVENT_INVENTORY = 1e5;
371
+ function gaUnitLabel(areaId, index) {
372
+ return `${PREFIX}${encodeURIComponent(areaId)}__${index + 1}`;
373
+ }
374
+ function gaUnitLabels(area) {
375
+ const capacity = Math.min(MAX_GA_CAPACITY, Math.max(0, Math.floor(area.capacity)));
376
+ return Array.from({ length: capacity }, (_, i) => gaUnitLabel(area.id, i));
377
+ }
378
+ function gaAreasOf(doc) {
379
+ return allObjects(doc).filter((o) => o.type === "gaArea");
380
+ }
381
+ function isGaUnitLabel(label) {
382
+ return label.startsWith(PREFIX);
383
+ }
384
+
367
385
  // src/core/sections.ts
368
386
  var UNGROUPED_ID = "__ungrouped__";
369
387
  function objectSeatLabels(o) {
370
388
  if (o.type === "row") return expandRow(o).map((s) => s.label);
371
389
  if (o.type === "table") return expandTable(o).map((s) => s.label);
372
390
  if (o.type === "booth") return expandBooth(o).map((s) => s.label);
391
+ if (o.type === "gaArea") return gaUnitLabels(o);
373
392
  return [];
374
393
  }
375
394
  function isSeatObject(o) {
376
- return o.type === "row" || o.type === "table" || o.type === "booth";
395
+ return o.type === "row" || o.type === "table" || o.type === "booth" || o.type === "gaArea";
377
396
  }
378
397
  function computeSections(doc) {
379
398
  const objs = allObjects(doc);
@@ -500,7 +519,10 @@ var en = {
500
519
  "picker.seatsHeld": "Seats held \u2014 {time}",
501
520
  "picker.holdExpired": "Your hold expired \u2014 the seats were released. Pick again.",
502
521
  "picker.seatTaken": "Seat {label} was just taken by another buyer.",
503
- "picker.poweredBy": "Powered by SeatMap",
522
+ "picker.poweredBy": "Powered by SeatLayer",
523
+ "picker.testMode": "TEST MODE",
524
+ "picker.colorblind": "Colorblind-friendly colors",
525
+ "picker.orphanHint": "This leaves a single seat stranded \u2014 consider shifting one seat over.",
504
526
  "picker.seats.one": "{count} seat",
505
527
  "picker.seats.other": "{count} seats",
506
528
  // renderer (drawn on the Konva map — shared by the embed SDK)
@@ -617,6 +639,7 @@ var ZONE_SUB_PX = 12;
617
639
  var HELD_FILL = "#6b7280";
618
640
  var TAKEN_FILL = "#374151";
619
641
  var NFS_STROKE = "#4b5563";
642
+ var CB_PALETTE = ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"];
620
643
  var ACCESS_RING = {
621
644
  wheelchair: "#3b82f6",
622
645
  companion: "#8b5cf6",
@@ -719,6 +742,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
719
742
  this.statusById = /* @__PURE__ */ new Map();
720
743
  this.catColor = /* @__PURE__ */ new Map();
721
744
  this.theme = {};
745
+ /** Colorblind-safe mode (Okabe-Ito hues + hollow booked seats). */
746
+ this.colorblind = false;
747
+ /** Category order from the doc — the stable index into the CB palette. */
748
+ this.catOrder = [];
722
749
  this.selection = /* @__PURE__ */ new Set();
723
750
  this.selectionRings = /* @__PURE__ */ new Map();
724
751
  this.focusedId = null;
@@ -969,6 +996,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
969
996
  this.hoverRing.radius(this.seatR + 2);
970
997
  this.catColor.clear();
971
998
  this.catPrice.clear();
999
+ this.catOrder = doc.categories.map((c) => c.key);
972
1000
  for (const c of doc.categories) {
973
1001
  this.catColor.set(c.key, c.color);
974
1002
  if (typeof c.price === "number") this.catPrice.set(c.key, c.price);
@@ -1492,12 +1520,18 @@ var _SeatmapRenderer = class _SeatmapRenderer {
1492
1520
  this.hasBoothText = true;
1493
1521
  target.add(t2);
1494
1522
  }
1523
+ /** The category's display color — Okabe-Ito hue when colorblind-safe is on. */
1524
+ seatBaseColor(categoryKey) {
1525
+ if (!this.colorblind) return this.catColor.get(categoryKey) ?? "#6e7bff";
1526
+ const idx = this.catOrder.indexOf(categoryKey);
1527
+ return CB_PALETTE[(idx >= 0 ? idx : 0) % CB_PALETTE.length];
1528
+ }
1495
1529
  /** Apply fill/stroke/opacity for a seat's current status + selection. */
1496
1530
  paintSeat(c, id) {
1497
1531
  const seat = this.seatById.get(id);
1498
1532
  const status = this.statusById.get(id) ?? "free";
1499
1533
  const selected = this.selection.has(id);
1500
- const base = this.catColor.get(seat.categoryKey) ?? "#6e7bff";
1534
+ const base = this.seatBaseColor(seat.categoryKey);
1501
1535
  c.dash([]);
1502
1536
  c.strokeWidth(0);
1503
1537
  c.stroke("");
@@ -1510,8 +1544,15 @@ var _SeatmapRenderer = class _SeatmapRenderer {
1510
1544
  c.fill(HELD_FILL);
1511
1545
  break;
1512
1546
  case "booked":
1513
- c.fill(TAKEN_FILL);
1514
- c.opacity(0.45);
1547
+ if (this.colorblind) {
1548
+ c.fill("rgba(0,0,0,0)");
1549
+ c.stroke(TAKEN_FILL);
1550
+ c.strokeWidth(1.5);
1551
+ c.opacity(0.9);
1552
+ } else {
1553
+ c.fill(TAKEN_FILL);
1554
+ c.opacity(0.45);
1555
+ }
1515
1556
  break;
1516
1557
  case "not_for_sale":
1517
1558
  c.fill(TAKEN_FILL);
@@ -1533,6 +1574,24 @@ var _SeatmapRenderer = class _SeatmapRenderer {
1533
1574
  }
1534
1575
  }
1535
1576
  }
1577
+ /**
1578
+ * Colorblind-safe mode: swap category hues for the Okabe-Ito palette and
1579
+ * render booked seats hollow. Off restores the exact default rendering.
1580
+ */
1581
+ setColorblindSafe(on) {
1582
+ if (on === this.colorblind) return;
1583
+ this.colorblind = on;
1584
+ for (const seat of this.seats) {
1585
+ const c = this.circleById.get(seat.id);
1586
+ if (c) this.paintSeat(c, seat.id);
1587
+ }
1588
+ if (this.cached) {
1589
+ this.seatLayer.clearCache();
1590
+ this.cacheSeatLayer();
1591
+ } else {
1592
+ this.seatLayer.batchDraw();
1593
+ }
1594
+ }
1536
1595
  /**
1537
1596
  * Dim the seats of these section/zone ids (organizer manager use) so held-back
1538
1597
  * inventory is visually distinct on the canvas without hiding it. `null`/empty
@@ -2471,6 +2530,38 @@ function createRenderer(container, opts) {
2471
2530
  return new SeatmapRenderer(container, opts);
2472
2531
  }
2473
2532
 
2533
+ // src/core/orphans.ts
2534
+ function seatIndex(id) {
2535
+ const n = Number(id.slice(id.lastIndexOf(":") + 1));
2536
+ return Number.isFinite(n) ? n : -1;
2537
+ }
2538
+ function strandedSingles(seats, statusOf, selectedIds) {
2539
+ const rows = /* @__PURE__ */ new Map();
2540
+ for (const s of seats) {
2541
+ if (s.kind === "booth") continue;
2542
+ const list = rows.get(s.rowId);
2543
+ if (list) list.push(s);
2544
+ else rows.set(s.rowId, [s]);
2545
+ }
2546
+ const unavailable = (s) => selectedIds.has(s.id) || statusOf(s.id) !== "free";
2547
+ const out = [];
2548
+ for (const list of rows.values()) {
2549
+ if (list.length < 3) continue;
2550
+ list.sort((a, b) => seatIndex(a.id) - seatIndex(b.id));
2551
+ for (let i = 1; i < list.length - 1; i++) {
2552
+ const seat = list[i];
2553
+ if (unavailable(seat)) continue;
2554
+ const left = list[i - 1];
2555
+ const right = list[i + 1];
2556
+ if (seatIndex(seat.id) - seatIndex(left.id) !== 1 || seatIndex(right.id) - seatIndex(seat.id) !== 1) continue;
2557
+ if (!unavailable(left) || !unavailable(right)) continue;
2558
+ if (!selectedIds.has(left.id) && !selectedIds.has(right.id)) continue;
2559
+ out.push(seat);
2560
+ }
2561
+ }
2562
+ return out;
2563
+ }
2564
+
2474
2565
  // src/picker/PickerController.ts
2475
2566
  var DEFAULT_MAX_SELECTION = 10;
2476
2567
  var MAX_BACKOFF_MS = 15e3;
@@ -2504,7 +2595,11 @@ var PickerController = class {
2504
2595
  this.closed = false;
2505
2596
  // hold state
2506
2597
  this.hold_ = null;
2598
+ this.liveStatuses = /* @__PURE__ */ new Map();
2599
+ this.currency = "USD";
2507
2600
  this.expiryTimer = null;
2601
+ /** Whether the last emitted hint was non-null (avoids clearing repeatedly). */
2602
+ this.hintShown = false;
2508
2603
  this.opts = options;
2509
2604
  this.api = options.transport;
2510
2605
  this.key = options.eventKey;
@@ -2561,6 +2656,7 @@ var PickerController = class {
2561
2656
  this.allIds.push(s.id);
2562
2657
  }
2563
2658
  const currency = res.event.currency ?? this.opts.currency;
2659
+ this.currency = currency ?? "USD";
2564
2660
  const renderer = createRenderer(host, {
2565
2661
  maxSelection: this.maxSelection,
2566
2662
  confirmSelection: this.opts.confirmSelection,
@@ -2600,6 +2696,7 @@ var PickerController = class {
2600
2696
  return null;
2601
2697
  }
2602
2698
  renderer.setChart(this.visibleDoc());
2699
+ if (this.opts.colorblindSafe) renderer.setColorblindSafe?.(true);
2603
2700
  if (seats) this.applySeatsMap(seats);
2604
2701
  this.connect();
2605
2702
  return {
@@ -2608,7 +2705,9 @@ var PickerController = class {
2608
2705
  eventName: res.event.name,
2609
2706
  venue: res.event.venue,
2610
2707
  startsAt: res.event.startsAt,
2611
- currency
2708
+ currency,
2709
+ // 'test' ⇒ sandbox event: hosts render a TEST MODE ribbon (Phase 11).
2710
+ mode: res.event.mode ?? "live"
2612
2711
  };
2613
2712
  }
2614
2713
  // ---- selection ------------------------------------------------------------
@@ -2631,21 +2730,72 @@ var PickerController = class {
2631
2730
  * re-throws so the caller can choose the banner copy. Returns null only when
2632
2731
  * there's nothing to hold.
2633
2732
  */
2634
- async hold(labelsArg) {
2733
+ async hold(labelsArg, ttlMs) {
2635
2734
  const r = this.renderer;
2636
2735
  if (!r) return null;
2637
2736
  const labels = labelsArg ?? r.getSelection().map((s) => s.label);
2638
- if (!labels.length) return null;
2639
- if (this.hold_ && this.holdCovers(labels)) return this.hold_;
2737
+ const existingGA = (this.hold_?.items ?? []).filter((item) => item.objectType === "ga");
2738
+ const combinedLabels = [.../* @__PURE__ */ new Set([...existingGA.map((item) => item.label), ...labels])];
2739
+ if (!combinedLabels.length) return null;
2740
+ if (this.hold_ && this.holdCovers(combinedLabels)) return this.hold_;
2640
2741
  try {
2641
- const result = await this.api.hold(this.key, labels);
2642
- this.setHold({ holdId: result.holdId, labels: [...labels], expiresAt: result.expiresAt });
2742
+ const selections = combinedLabels.map((label) => {
2743
+ const ga = existingGA.find((item) => item.label === label);
2744
+ if (ga) return { label, tierId: ga.tierId };
2745
+ const seat = this.labelToSeat.get(label);
2746
+ const resolved = seat ? this.toSeat(seat) : null;
2747
+ return { label, ...resolved?.tierId ? { tierId: resolved.tierId } : {} };
2748
+ });
2749
+ const result = await this.api.hold(this.key, selections, ttlMs, this.hold_?.holdId);
2750
+ this.setHold({ holdId: result.holdId, labels: combinedLabels, expiresAt: result.expiresAt, items: result.items });
2643
2751
  return this.hold_;
2644
2752
  } catch (err) {
2645
2753
  this.handle409Conflicts(err);
2646
2754
  throw err;
2647
2755
  }
2648
2756
  }
2757
+ /** Public GA inventory derived from the live synthetic-unit status stream. */
2758
+ getGAAreas() {
2759
+ const doc = this.visibleDoc();
2760
+ if (!doc) return [];
2761
+ return gaAreasOf(doc).map((area) => {
2762
+ const category = doc.categories.find((candidate) => candidate.key === area.categoryKey);
2763
+ return {
2764
+ id: area.id,
2765
+ label: area.label,
2766
+ capacity: Math.max(0, Math.floor(area.capacity)),
2767
+ available: gaUnitLabels(area).filter((label) => (this.liveStatuses.get(label) ?? "free") === "free").length,
2768
+ categoryKey: area.categoryKey,
2769
+ tiers: category?.tiers,
2770
+ price: category?.tiers?.[0]?.price ?? category?.price ?? 0,
2771
+ currency: this.currency
2772
+ };
2773
+ });
2774
+ }
2775
+ /** Select a quantity from one GA area and hold it atomically. */
2776
+ async holdGA(areaId, qty, options = {}) {
2777
+ const doc = this.visibleDoc();
2778
+ if (!doc || !Number.isFinite(qty) || qty < 1) return null;
2779
+ const area = gaAreasOf(doc).find((candidate) => candidate.id === areaId);
2780
+ if (!area) return null;
2781
+ const labels = gaUnitLabels(area).filter((label) => !this.hold_?.labels.includes(label) && (this.liveStatuses.get(label) ?? "free") === "free").slice(0, Math.floor(qty));
2782
+ if (labels.length !== Math.floor(qty)) return null;
2783
+ const selections = /* @__PURE__ */ new Map();
2784
+ for (const item of this.hold_?.items ?? []) selections.set(item.label, { label: item.label, tierId: item.tierId });
2785
+ if (this.hold_ && !this.hold_?.items?.length) {
2786
+ for (const seat of this.hold_.seats) selections.set(seat.label, { label: seat.label, ...seat.tierId ? { tierId: seat.tierId } : {} });
2787
+ }
2788
+ for (const seat of this.renderer?.getSelection() ?? []) {
2789
+ const resolved = this.labelToSeat.get(seat.label);
2790
+ const chosen = resolved ? this.toSeat(resolved) : null;
2791
+ selections.set(seat.label, { label: seat.label, ...chosen?.tierId ? { tierId: chosen.tierId } : {} });
2792
+ }
2793
+ for (const label of labels) selections.set(label, { label, ...options.tierId ? { tierId: options.tierId } : {} });
2794
+ const combined = [...selections.values()];
2795
+ const result = await this.api.hold(this.key, combined, options.ttlMs, this.hold_?.holdId);
2796
+ this.setHold({ holdId: result.holdId, labels: combined.map((s) => s.label), expiresAt: result.expiresAt, items: result.items });
2797
+ return this.hold_;
2798
+ }
2649
2799
  /** Server-picks `qty` best free seats and holds them atomically. Throws on failure. */
2650
2800
  async bestAvailable(qty, categoryKey) {
2651
2801
  const r = this.renderer;
@@ -2656,7 +2806,7 @@ var PickerController = class {
2656
2806
  r.clearSelection();
2657
2807
  const ids = result.labels.map((l) => this.labelToId.get(l)).filter((v) => !!v);
2658
2808
  if (ids.length) r.setStatus(ids, "held");
2659
- this.setHold({ holdId: result.holdId, labels: [...result.labels], expiresAt: result.expiresAt });
2809
+ this.setHold({ holdId: result.holdId, labels: [...result.labels], expiresAt: result.expiresAt, items: result.items });
2660
2810
  const seats = result.labels.map((l) => this.labelToSeat.get(l)).filter((s) => !!s).map((s) => this.toSeat(s));
2661
2811
  this.opts.onSelectionChange?.(seats);
2662
2812
  return this.hold_;
@@ -2685,9 +2835,19 @@ var PickerController = class {
2685
2835
  if (this.hold_ && this.holdCovers(labels)) {
2686
2836
  holdId = this.hold_.holdId;
2687
2837
  } else {
2688
- const h = await this.api.hold(this.key, labels);
2838
+ const replaceHoldId = this.hold_?.holdId;
2839
+ const h = await this.api.hold(
2840
+ this.key,
2841
+ labels.map((label) => {
2842
+ const seat = this.labelToSeat.get(label);
2843
+ const resolved = seat ? this.toSeat(seat) : null;
2844
+ return { label, ...resolved?.tierId ? { tierId: resolved.tierId } : {} };
2845
+ }),
2846
+ void 0,
2847
+ replaceHoldId
2848
+ );
2689
2849
  holdId = h.holdId;
2690
- this.setHold({ holdId, labels: [...labels], expiresAt: h.expiresAt });
2850
+ this.setHold({ holdId, labels: [...labels], expiresAt: h.expiresAt, items: h.items });
2691
2851
  }
2692
2852
  await this.api.book(this.key, labels, holdId, bookingRef);
2693
2853
  } catch (err) {
@@ -2971,6 +3131,31 @@ var PickerController = class {
2971
3131
  }
2972
3132
  emitSelectionChange() {
2973
3133
  this.opts.onSelectionChange?.(this.getSelection());
3134
+ this.emitOrphanHint();
3135
+ }
3136
+ /**
3137
+ * Orphan-seat advice on manual selection: when the buyer's current picks
3138
+ * strand one (or more) single free seats between unavailable same-row
3139
+ * neighbors, surface a localized, non-blocking hint. Cleared (null) as soon
3140
+ * as the selection stops stranding anyone.
3141
+ */
3142
+ emitOrphanHint() {
3143
+ if (!this.opts.onHint) return;
3144
+ const r = this.renderer;
3145
+ if (!r) return;
3146
+ const selected = new Set(r.getSelection().map((s) => s.id));
3147
+ const stranded = selected.size ? strandedSingles(this.seatById.values(), (id) => r.getStatus(id), selected) : [];
3148
+ if (stranded.length > 0) {
3149
+ this.hintShown = true;
3150
+ this.opts.onHint(t("picker.orphanHint"));
3151
+ } else if (this.hintShown) {
3152
+ this.hintShown = false;
3153
+ this.opts.onHint(null);
3154
+ }
3155
+ }
3156
+ /** Toggle colorblind-safe rendering at runtime (see PickerOptions.colorblindSafe). */
3157
+ setColorblindSafe(on) {
3158
+ this.renderer?.setColorblindSafe?.(on);
2974
3159
  }
2975
3160
  emitError(err) {
2976
3161
  if (this.opts.onError) this.opts.onError(err);
@@ -2978,7 +3163,13 @@ var PickerController = class {
2978
3163
  }
2979
3164
  holdCovers(labels) {
2980
3165
  const h = this.hold_;
2981
- return !!h && h.labels.length === labels.length && labels.every((l) => h.labels.includes(l));
3166
+ if (!h || h.labels.length !== labels.length || !labels.every((l) => h.labels.includes(l))) return false;
3167
+ const tiers = new Map((h.items ?? []).map((item) => [item.label, item.tierId]));
3168
+ return labels.every((label) => {
3169
+ const seat = this.labelToSeat.get(label);
3170
+ const currentTier = seat ? this.toSeat(seat).tierId ?? null : null;
3171
+ return !tiers.has(label) || tiers.get(label) === currentTier;
3172
+ });
2982
3173
  }
2983
3174
  handle409Conflicts(err) {
2984
3175
  const { status, conflicts } = errInfo(err);
@@ -2998,7 +3189,7 @@ var PickerController = class {
2998
3189
  this.hold_ = full;
2999
3190
  if (this.expiryTimer) clearTimeout(this.expiryTimer);
3000
3191
  const ms = Math.max(0, full.expiresAt - Date.now());
3001
- this.expiryTimer = setTimeout(() => this.onHoldExpired(), ms);
3192
+ this.expiryTimer = setTimeout(() => void this.expireActiveHold(), ms);
3002
3193
  this.opts.onHold?.(full);
3003
3194
  }
3004
3195
  clearHold() {
@@ -3008,18 +3199,31 @@ var PickerController = class {
3008
3199
  this.expiryTimer = null;
3009
3200
  }
3010
3201
  }
3011
- onHoldExpired() {
3202
+ async expireActiveHold() {
3012
3203
  const hold = this.hold_;
3013
- this.clearHold();
3014
- if (hold) {
3015
- const ids = hold.labels.map((l) => this.labelToId.get(l)).filter((v) => !!v);
3016
- if (ids.length) this.renderer?.setStatus(ids, "free");
3204
+ if (!hold) return;
3205
+ try {
3206
+ const snap = await this.api.objects(this.key);
3207
+ if (this.hold_?.holdId !== hold.holdId) return;
3208
+ this.applySeatsMap(snap.seats);
3209
+ } catch {
3210
+ if (this.hold_?.holdId === hold.holdId) {
3211
+ this.expiryTimer = setTimeout(() => void this.expireActiveHold(), 2e3);
3212
+ }
3213
+ return;
3017
3214
  }
3215
+ if (this.hold_?.holdId !== hold.holdId) return;
3216
+ if (hold.labels.some((label) => this.liveStatuses.get(label) === "held")) {
3217
+ this.expiryTimer = setTimeout(() => void this.expireActiveHold(), 1e3);
3218
+ return;
3219
+ }
3220
+ this.clearHold();
3018
3221
  this.opts.onHoldExpired?.();
3019
3222
  }
3020
3223
  applySeatsMap(seats) {
3021
3224
  const r = this.renderer;
3022
3225
  if (!r) return;
3226
+ this.liveStatuses = new Map(Object.entries(seats));
3023
3227
  if (this.allIds.length) r.setStatus(this.allIds, "free");
3024
3228
  const byStatus = { free: [], held: [], booked: [], not_for_sale: [] };
3025
3229
  for (const [label, st] of Object.entries(seats)) {
@@ -3030,6 +3234,10 @@ var PickerController = class {
3030
3234
  if (byStatus[st].length) r.setStatus(byStatus[st], st);
3031
3235
  });
3032
3236
  this.opts.onStatusChange?.();
3237
+ this.clearBookedHoldIfSettled();
3238
+ }
3239
+ clearBookedHoldIfSettled() {
3240
+ if (this.hold_?.labels.every((label) => this.liveStatuses.get(label) === "booked")) this.clearHold();
3033
3241
  }
3034
3242
  async resnapshot() {
3035
3243
  try {
@@ -3072,6 +3280,7 @@ var PickerController = class {
3072
3280
  this.applySeatsMap(m.seats);
3073
3281
  } else if (Array.isArray(m.changes)) {
3074
3282
  for (const ch of m.changes) {
3283
+ this.liveStatuses.set(ch.label, ch.status);
3075
3284
  const id = this.labelToId.get(ch.label);
3076
3285
  if (!id) continue;
3077
3286
  const next = mapStatus(ch.status);
@@ -3080,6 +3289,7 @@ var PickerController = class {
3080
3289
  }
3081
3290
  r.setStatus([id], next);
3082
3291
  }
3292
+ this.clearBookedHoldIfSettled();
3083
3293
  this.opts.onStatusChange?.();
3084
3294
  }
3085
3295
  };
@@ -3107,9 +3317,9 @@ var PickerController = class {
3107
3317
 
3108
3318
  // src/i18n/bundles.ts
3109
3319
  var LOADERS = {
3110
- es: () => import("./es-UM4ZS357.js").then((m) => ({ default: m.es })),
3111
- de: () => import("./de-Y3E6YTGP.js").then((m) => ({ default: m.de })),
3112
- fr: () => import("./fr-IGRD2M4W.js").then((m) => ({ default: m.fr }))
3320
+ es: () => import("./es-MLM3IFIY.js").then((m) => ({ default: m.es })),
3321
+ de: () => import("./de-YMAVF5S2.js").then((m) => ({ default: m.de })),
3322
+ fr: () => import("./fr-N7AAVATM.js").then((m) => ({ default: m.fr }))
3113
3323
  };
3114
3324
  var loaded = /* @__PURE__ */ new Set(["en"]);
3115
3325
  async function loadLocale(code) {
@@ -3132,6 +3342,8 @@ export {
3132
3342
  ACCESSIBILITY_TYPES,
3133
3343
  CHART_STORAGE_KEY,
3134
3344
  DEFAULT_CURRENCY,
3345
+ MAX_EVENT_INVENTORY,
3346
+ MAX_GA_CAPACITY,
3135
3347
  PickerController,
3136
3348
  SUPPORTED_LOCALES,
3137
3349
  SeatmapRenderer,
@@ -3152,8 +3364,12 @@ export {
3152
3364
  floorsOf,
3153
3365
  formatDate,
3154
3366
  formatMoney,
3367
+ gaAreasOf,
3368
+ gaUnitLabel,
3369
+ gaUnitLabels,
3155
3370
  getLocale,
3156
3371
  hiddenObjectIds,
3372
+ isGaUnitLabel,
3157
3373
  isSectionHidden,
3158
3374
  layerOf,
3159
3375
  loadLocale,