@open-slot-ui/core 0.9.0 → 0.10.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.cjs +50 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +50 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1652,6 +1652,10 @@ interface ResponsiveOverride {
|
|
|
1652
1652
|
type BlockSpec = {
|
|
1653
1653
|
/** Rules-audit topics this block covers (see `auditRules`). */
|
|
1654
1654
|
covers?: string[];
|
|
1655
|
+
/** The declared mode id this block EXPLAINS (e.g. `'bonus'`). Every declared mode
|
|
1656
|
+
* must have its own rules section — a heading naming it, or blocks tagged with
|
|
1657
|
+
* `explains` — with real prose; the audit enforces it (see `auditRules`). */
|
|
1658
|
+
explains?: string;
|
|
1655
1659
|
} & ({
|
|
1656
1660
|
kind: 'slider';
|
|
1657
1661
|
id: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1652,6 +1652,10 @@ interface ResponsiveOverride {
|
|
|
1652
1652
|
type BlockSpec = {
|
|
1653
1653
|
/** Rules-audit topics this block covers (see `auditRules`). */
|
|
1654
1654
|
covers?: string[];
|
|
1655
|
+
/** The declared mode id this block EXPLAINS (e.g. `'bonus'`). Every declared mode
|
|
1656
|
+
* must have its own rules section — a heading naming it, or blocks tagged with
|
|
1657
|
+
* `explains` — with real prose; the audit enforces it (see `auditRules`). */
|
|
1658
|
+
explains?: string;
|
|
1655
1659
|
} & ({
|
|
1656
1660
|
kind: 'slider';
|
|
1657
1661
|
id: string;
|
package/dist/index.js
CHANGED
|
@@ -1410,6 +1410,9 @@ function validateSpec(spec) {
|
|
|
1410
1410
|
return;
|
|
1411
1411
|
}
|
|
1412
1412
|
seeId(b.id, `${p}.id`);
|
|
1413
|
+
if (b.explains && b.explains !== "freeSpins" && spec.facts?.modes?.length && !spec.facts.modes.some((m) => m.id === b.explains)) {
|
|
1414
|
+
add("warn", `${p}.explains`, "explains-unknown-mode", `explains "${b.explains}" matches no declared mode id (facts.modes)`);
|
|
1415
|
+
}
|
|
1413
1416
|
if (b.kind === "select" && (!b.options || b.options.length === 0)) {
|
|
1414
1417
|
add("error", `${p}.options`, "select-empty", "a select block needs at least one option");
|
|
1415
1418
|
}
|
|
@@ -1762,14 +1765,29 @@ function factsVars(facts, extra) {
|
|
|
1762
1765
|
return extra ? { ...vars, ...extra } : vars;
|
|
1763
1766
|
}
|
|
1764
1767
|
function scanBlocks(blocks, resolve = (s) => s) {
|
|
1765
|
-
const scan = { text: "", headings: "", covers: /* @__PURE__ */ new Set(), hasModeStats: false, hasLegal: false };
|
|
1768
|
+
const scan = { text: "", headings: "", covers: /* @__PURE__ */ new Set(), hasModeStats: false, hasLegal: false, sections: [] };
|
|
1769
|
+
let section = { heading: "", text: "", explains: /* @__PURE__ */ new Set(), covers: /* @__PURE__ */ new Set() };
|
|
1770
|
+
scan.sections.push(section);
|
|
1766
1771
|
const addText = (t) => {
|
|
1767
|
-
if (typeof t === "string" && t)
|
|
1768
|
-
|
|
1772
|
+
if (typeof t === "string" && t) {
|
|
1773
|
+
const r = resolve(t);
|
|
1774
|
+
scan.text += `
|
|
1775
|
+
${r}`;
|
|
1776
|
+
section.text += `
|
|
1777
|
+
${r}`;
|
|
1778
|
+
}
|
|
1769
1779
|
};
|
|
1770
1780
|
const walk = (list) => {
|
|
1771
1781
|
for (const b of list ?? []) {
|
|
1772
|
-
|
|
1782
|
+
if (b.kind === "heading" || b.kind === "subheading") {
|
|
1783
|
+
section = { heading: resolve(b.text), text: "", explains: /* @__PURE__ */ new Set(), covers: /* @__PURE__ */ new Set() };
|
|
1784
|
+
scan.sections.push(section);
|
|
1785
|
+
}
|
|
1786
|
+
for (const c of b.covers ?? []) {
|
|
1787
|
+
scan.covers.add(c);
|
|
1788
|
+
section.covers.add(c);
|
|
1789
|
+
}
|
|
1790
|
+
if (b.explains) section.explains.add(b.explains);
|
|
1773
1791
|
const anyB = b;
|
|
1774
1792
|
switch (b.kind) {
|
|
1775
1793
|
case "mode-stats":
|
|
@@ -1784,11 +1802,14 @@ ${resolve(t)}`;
|
|
|
1784
1802
|
addText(b.text);
|
|
1785
1803
|
break;
|
|
1786
1804
|
case "heading":
|
|
1787
|
-
case "subheading":
|
|
1805
|
+
case "subheading": {
|
|
1806
|
+
const r = resolve(b.text);
|
|
1788
1807
|
scan.headings += `
|
|
1789
|
-
${
|
|
1790
|
-
|
|
1808
|
+
${r}`;
|
|
1809
|
+
scan.text += `
|
|
1810
|
+
${r}`;
|
|
1791
1811
|
break;
|
|
1812
|
+
}
|
|
1792
1813
|
case "text":
|
|
1793
1814
|
addText(b.text);
|
|
1794
1815
|
break;
|
|
@@ -1857,6 +1878,17 @@ function mentionsTimes(text, x) {
|
|
|
1857
1878
|
return new RegExp(`\\b${grouped}${frac}\\s*[x\xD7]`, "i").test(text);
|
|
1858
1879
|
}
|
|
1859
1880
|
var mentionsName = (text, name) => text.toLowerCase().includes(name.toLowerCase());
|
|
1881
|
+
function mentionsCost(text, cost) {
|
|
1882
|
+
if (mentionsTimes(text, cost) || mentionsTimes(text, cost + 1)) return true;
|
|
1883
|
+
if (cost > 0 && cost < 1) return new RegExp(`\\b${Math.round(cost * 100)}\\s*%`).test(text);
|
|
1884
|
+
return false;
|
|
1885
|
+
}
|
|
1886
|
+
function findModeSection(sections, m) {
|
|
1887
|
+
return sections.find(
|
|
1888
|
+
(s) => s.explains.has(m.id) || s.covers.has(`feature:${m.id}`) || s.heading !== "" && m.name !== "" && mentionsName(s.heading, m.name) || (m.kind === "base" || !m.kind) && /how to play|about the game/i.test(s.heading)
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
var MIN_SECTION_PROSE = 40;
|
|
1860
1892
|
function auditRules(facts, rules, opts = {}) {
|
|
1861
1893
|
const out = [];
|
|
1862
1894
|
try {
|
|
@@ -1877,8 +1909,17 @@ function auditRules(facts, rules, opts = {}) {
|
|
|
1877
1909
|
} else if (!(scan.hasModeStats || covered(`maxwin:${m.id}`) || nameIn && mentionsTimes(scan.text, m.maxWinX))) {
|
|
1878
1910
|
add("required", "rules-missing-maxwin", `maxwin:${m.id}`, `The max win of \u201C${m.name}\u201D (${formatTimes(m.maxWinX)}) is not stated in the rules.`);
|
|
1879
1911
|
}
|
|
1880
|
-
if (
|
|
1881
|
-
|
|
1912
|
+
if (!m.id) continue;
|
|
1913
|
+
const sec = findModeSection(scan.sections, m);
|
|
1914
|
+
if (!sec) {
|
|
1915
|
+
add("required", "rules-missing-mode-section", `section:${m.id}`, `\u201C${m.name}\u201D has no rules section of its own \u2014 add a heading naming it (or tag its blocks with explains: "${m.id}") and explain how the mode plays.`);
|
|
1916
|
+
} else {
|
|
1917
|
+
if (sec.text.trim().length < MIN_SECTION_PROSE) {
|
|
1918
|
+
add("required", "rules-mode-section-empty", `section:${m.id}`, `The \u201C${m.name}\u201D section has a heading but no real explanation \u2014 describe how the mode actually plays.`);
|
|
1919
|
+
}
|
|
1920
|
+
if (m.kind && m.kind !== "base" && m.cost != null && !(sec.covers.has(`cost:${m.id}`) || mentionsCost(sec.text, m.cost))) {
|
|
1921
|
+
add("required", "rules-mode-missing-cost", `cost:${m.id}`, `State what \u201C${m.name}\u201D costs (${formatTimes(m.cost)} the bet \u2014 e.g. via {{cost.${m.id}}}) inside its own section.`);
|
|
1922
|
+
}
|
|
1882
1923
|
}
|
|
1883
1924
|
}
|
|
1884
1925
|
const fs = facts?.freeSpins;
|