@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.cjs
CHANGED
|
@@ -1412,6 +1412,9 @@ function validateSpec(spec) {
|
|
|
1412
1412
|
return;
|
|
1413
1413
|
}
|
|
1414
1414
|
seeId(b.id, `${p}.id`);
|
|
1415
|
+
if (b.explains && b.explains !== "freeSpins" && spec.facts?.modes?.length && !spec.facts.modes.some((m) => m.id === b.explains)) {
|
|
1416
|
+
add("warn", `${p}.explains`, "explains-unknown-mode", `explains "${b.explains}" matches no declared mode id (facts.modes)`);
|
|
1417
|
+
}
|
|
1415
1418
|
if (b.kind === "select" && (!b.options || b.options.length === 0)) {
|
|
1416
1419
|
add("error", `${p}.options`, "select-empty", "a select block needs at least one option");
|
|
1417
1420
|
}
|
|
@@ -1764,14 +1767,29 @@ function factsVars(facts, extra) {
|
|
|
1764
1767
|
return extra ? { ...vars, ...extra } : vars;
|
|
1765
1768
|
}
|
|
1766
1769
|
function scanBlocks(blocks, resolve = (s) => s) {
|
|
1767
|
-
const scan = { text: "", headings: "", covers: /* @__PURE__ */ new Set(), hasModeStats: false, hasLegal: false };
|
|
1770
|
+
const scan = { text: "", headings: "", covers: /* @__PURE__ */ new Set(), hasModeStats: false, hasLegal: false, sections: [] };
|
|
1771
|
+
let section = { heading: "", text: "", explains: /* @__PURE__ */ new Set(), covers: /* @__PURE__ */ new Set() };
|
|
1772
|
+
scan.sections.push(section);
|
|
1768
1773
|
const addText = (t) => {
|
|
1769
|
-
if (typeof t === "string" && t)
|
|
1770
|
-
|
|
1774
|
+
if (typeof t === "string" && t) {
|
|
1775
|
+
const r = resolve(t);
|
|
1776
|
+
scan.text += `
|
|
1777
|
+
${r}`;
|
|
1778
|
+
section.text += `
|
|
1779
|
+
${r}`;
|
|
1780
|
+
}
|
|
1771
1781
|
};
|
|
1772
1782
|
const walk = (list) => {
|
|
1773
1783
|
for (const b of list ?? []) {
|
|
1774
|
-
|
|
1784
|
+
if (b.kind === "heading" || b.kind === "subheading") {
|
|
1785
|
+
section = { heading: resolve(b.text), text: "", explains: /* @__PURE__ */ new Set(), covers: /* @__PURE__ */ new Set() };
|
|
1786
|
+
scan.sections.push(section);
|
|
1787
|
+
}
|
|
1788
|
+
for (const c of b.covers ?? []) {
|
|
1789
|
+
scan.covers.add(c);
|
|
1790
|
+
section.covers.add(c);
|
|
1791
|
+
}
|
|
1792
|
+
if (b.explains) section.explains.add(b.explains);
|
|
1775
1793
|
const anyB = b;
|
|
1776
1794
|
switch (b.kind) {
|
|
1777
1795
|
case "mode-stats":
|
|
@@ -1786,11 +1804,14 @@ ${resolve(t)}`;
|
|
|
1786
1804
|
addText(b.text);
|
|
1787
1805
|
break;
|
|
1788
1806
|
case "heading":
|
|
1789
|
-
case "subheading":
|
|
1807
|
+
case "subheading": {
|
|
1808
|
+
const r = resolve(b.text);
|
|
1790
1809
|
scan.headings += `
|
|
1791
|
-
${
|
|
1792
|
-
|
|
1810
|
+
${r}`;
|
|
1811
|
+
scan.text += `
|
|
1812
|
+
${r}`;
|
|
1793
1813
|
break;
|
|
1814
|
+
}
|
|
1794
1815
|
case "text":
|
|
1795
1816
|
addText(b.text);
|
|
1796
1817
|
break;
|
|
@@ -1859,6 +1880,17 @@ function mentionsTimes(text, x) {
|
|
|
1859
1880
|
return new RegExp(`\\b${grouped}${frac}\\s*[x\xD7]`, "i").test(text);
|
|
1860
1881
|
}
|
|
1861
1882
|
var mentionsName = (text, name) => text.toLowerCase().includes(name.toLowerCase());
|
|
1883
|
+
function mentionsCost(text, cost) {
|
|
1884
|
+
if (mentionsTimes(text, cost) || mentionsTimes(text, cost + 1)) return true;
|
|
1885
|
+
if (cost > 0 && cost < 1) return new RegExp(`\\b${Math.round(cost * 100)}\\s*%`).test(text);
|
|
1886
|
+
return false;
|
|
1887
|
+
}
|
|
1888
|
+
function findModeSection(sections, m) {
|
|
1889
|
+
return sections.find(
|
|
1890
|
+
(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)
|
|
1891
|
+
);
|
|
1892
|
+
}
|
|
1893
|
+
var MIN_SECTION_PROSE = 40;
|
|
1862
1894
|
function auditRules(facts, rules, opts = {}) {
|
|
1863
1895
|
const out = [];
|
|
1864
1896
|
try {
|
|
@@ -1879,8 +1911,17 @@ function auditRules(facts, rules, opts = {}) {
|
|
|
1879
1911
|
} else if (!(scan.hasModeStats || covered(`maxwin:${m.id}`) || nameIn && mentionsTimes(scan.text, m.maxWinX))) {
|
|
1880
1912
|
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.`);
|
|
1881
1913
|
}
|
|
1882
|
-
if (
|
|
1883
|
-
|
|
1914
|
+
if (!m.id) continue;
|
|
1915
|
+
const sec = findModeSection(scan.sections, m);
|
|
1916
|
+
if (!sec) {
|
|
1917
|
+
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.`);
|
|
1918
|
+
} else {
|
|
1919
|
+
if (sec.text.trim().length < MIN_SECTION_PROSE) {
|
|
1920
|
+
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.`);
|
|
1921
|
+
}
|
|
1922
|
+
if (m.kind && m.kind !== "base" && m.cost != null && !(sec.covers.has(`cost:${m.id}`) || mentionsCost(sec.text, m.cost))) {
|
|
1923
|
+
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.`);
|
|
1924
|
+
}
|
|
1884
1925
|
}
|
|
1885
1926
|
}
|
|
1886
1927
|
const fs = facts?.freeSpins;
|