@open-slot-ui/core 0.5.1 → 0.6.1

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
@@ -1249,6 +1249,9 @@ function validateSpec(spec) {
1249
1249
  if (spec.realityCheck != null && !(typeof spec.realityCheck.everyMinutes === "number" && spec.realityCheck.everyMinutes > 0)) {
1250
1250
  add("error", "realityCheck.everyMinutes", "bad-interval", "realityCheck.everyMinutes must be a number > 0");
1251
1251
  }
1252
+ if (spec.buyFeature?.confirmAboveCost != null && !(typeof spec.buyFeature.confirmAboveCost === "number" && Number.isFinite(spec.buyFeature.confirmAboveCost) && spec.buyFeature.confirmAboveCost >= 0)) {
1253
+ add("error", "buyFeature.confirmAboveCost", "bad-threshold", "buyFeature.confirmAboveCost must be a finite number >= 0");
1254
+ }
1252
1255
  if (spec.controls) {
1253
1256
  for (const [id, ov] of Object.entries(spec.controls)) {
1254
1257
  checkLayout(ov.layout?.anchor, `controls.${id}.layout.anchor`);
@@ -1401,6 +1404,9 @@ function applyJurisdiction(ui, jur) {
1401
1404
  if (typeof jur.minimumRoundDuration === "number" && Number.isFinite(jur.minimumRoundDuration)) {
1402
1405
  ui.minimumRoundDuration = Math.max(0, jur.minimumRoundDuration);
1403
1406
  }
1407
+ if (typeof jur.confirmBuyAboveCost === "number" && Number.isFinite(jur.confirmBuyAboveCost)) {
1408
+ ui.confirmBuyAboveCost = Math.max(0, jur.confirmBuyAboveCost);
1409
+ }
1404
1410
  }
1405
1411
 
1406
1412
  // src/notice.ts
@@ -1458,6 +1464,8 @@ var openuiDefaults = {
1458
1464
  "openui.replay": "Replay",
1459
1465
  "openui.buyFeature.title": "Buy feature",
1460
1466
  "openui.buyFeature.message": "Buy this feature now?",
1467
+ // Confirm step for a higher-cost play — {{name}} / {{price}} are interpolated.
1468
+ "openui.buyFeature.confirm": "Buy {{name}} for {{price}}?",
1461
1469
  "openui.freeSpins": "FS",
1462
1470
  // reality check (RTS 13) — {{minutes}} is interpolated by open-ui's scheduler
1463
1471
  "openui.realityCheck.title": "Reality check",
@@ -1493,6 +1501,7 @@ var openuiSocialDefaults = {
1493
1501
  "openui.err.insufficient.title": "Insufficient balance",
1494
1502
  "openui.buyFeature.title": "Play bonus",
1495
1503
  "openui.buyFeature.message": "Play this bonus now?",
1504
+ "openui.buyFeature.confirm": "Play {{name}} for {{price}}?",
1496
1505
  "openui.err.insufficient.message": "You don't have enough balance for this play.",
1497
1506
  "openui.err.limit.message": "A play limit has been reached. Please try again later."
1498
1507
  };
@@ -1555,8 +1564,18 @@ var CURRENCY_TABLE = Object.freeze({
1555
1564
  MXN: { symbol: "MX$", decimals: 2 },
1556
1565
  TRY: { symbol: "\u20BA", decimals: 2 },
1557
1566
  GBP: { symbol: "\xA3", decimals: 2 },
1567
+ NGN: { symbol: "\u20A6", decimals: 2 },
1568
+ TWD: { symbol: "NT$", decimals: 2 },
1569
+ SGD: { symbol: "SG$", decimals: 2 },
1570
+ MYR: { symbol: "RM", decimals: 2 },
1571
+ CRC: { symbol: "\u20A1", decimals: 2 },
1572
+ // three-decimal dinars (1 unit = 1000 fils) — the >2-decimal path must format these
1573
+ KWD: { symbol: "KD", decimals: 3 },
1574
+ JOD: { symbol: "JD", decimals: 3 },
1575
+ BHD: { symbol: "BD", decimals: 3 },
1558
1576
  // ── fiat — symbol AFTER the amount ───────────────────────────────────────────
1559
1577
  DKK: { symbol: "kr", decimals: 2, symbolAfter: true },
1578
+ NOK: { symbol: "kr", decimals: 2, symbolAfter: true },
1560
1579
  PLN: { symbol: "z\u0142", decimals: 2, symbolAfter: true },
1561
1580
  VND: { symbol: "\u20AB", decimals: 0, symbolAfter: true },
1562
1581
  CLP: { symbol: "CLP", decimals: 0, symbolAfter: true },
@@ -1673,6 +1692,11 @@ var OpenUI = class {
1673
1692
  gameInfo = {};
1674
1693
  /** Minimum round duration (ms) from jurisdiction — stored for the GAME to enforce. */
1675
1694
  minimumRoundDuration = 0;
1695
+ /** Buy-feature confirm threshold, as a multiple of the base bet. A play (buy or
1696
+ * bet-mode activation) that costs STRICTLY MORE than this must show a confirm step
1697
+ * — Stake requires that bet modes above 2× never activate on a single click. `0`
1698
+ * (the default) = off. Set via `spec.buyFeature.confirmAboveCost` or a jurisdiction. */
1699
+ confirmBuyAboveCost = 0;
1676
1700
  sessionNet = 0;
1677
1701
  prevVolumes = null;
1678
1702
  /** The frozen UISpec `createUI` built this from, if any — authored = run = tested. */
@@ -2022,6 +2046,13 @@ var OpenUI = class {
2022
2046
  const j = this.jurisdiction;
2023
2047
  return feature === "autoplay" && !!j.disabledAutoplay || feature === "buyFeature" && !!j.disabledBuyFeature || feature === "turbo" && !!j.disabledTurbo || feature === "fullscreen" && !!j.disabledFullscreen || feature === "slamstop" && !!j.disabledSlamstop || feature === "spacebar" && !!j.disabledSpacebar;
2024
2048
  }
2049
+ /** True when a buy / bet-mode activation of `cost`× the base bet must be confirmed
2050
+ * before it commits (Stake: no one-click activation above `confirmBuyAboveCost`×).
2051
+ * Always false when the threshold is 0/unset or `cost` isn't a finite number. */
2052
+ shouldConfirmBuy(cost) {
2053
+ const t = this.confirmBuyAboveCost;
2054
+ return t > 0 && Number.isFinite(cost) && cost > t;
2055
+ }
2025
2056
  /**
2026
2057
  * Start a reality-check reminder (RTS 13). Every `everyMinutes` (WALL-CLOCK, so a
2027
2058
  * backgrounded tab can't cheat it) it emits a `realityCheck` event, stops autoplay,
@@ -2355,6 +2386,9 @@ function createUI(spec = {}, hooks = {}) {
2355
2386
  }
2356
2387
  }
2357
2388
  }
2389
+ if (spec.buyFeature?.confirmAboveCost != null && Number.isFinite(spec.buyFeature.confirmAboveCost)) {
2390
+ ui.confirmBuyAboveCost = Math.max(0, spec.buyFeature.confirmAboveCost);
2391
+ }
2358
2392
  if (spec.jurisdiction) ui.applyJurisdiction(spec.jurisdiction);
2359
2393
  const portraitControls = {};
2360
2394
  for (const [id, layout] of Object.entries(portraitDefaultLayouts)) {