@bitmagic/cli 0.1.83-dev.1 → 0.1.83-dev.3
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/README.md +69 -0
- package/assets/skills/publishing-to-steam/SKILL.md +218 -0
- package/dist/assets/vxl-summary.d.ts +6 -20
- package/dist/assets/vxl-summary.js +9 -100
- package/dist/assets/vxl-summary.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/check.js +5 -0
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/forge.js +1 -1
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/ios.js +88 -136
- package/dist/commands/ios.js.map +1 -1
- package/dist/commands/steam-build.d.ts +67 -0
- package/dist/commands/steam-build.js +286 -0
- package/dist/commands/steam-build.js.map +1 -0
- package/dist/commands/steam-portal.d.ts +1 -0
- package/dist/commands/steam-portal.js +319 -0
- package/dist/commands/steam-portal.js.map +1 -0
- package/dist/commands/steam-upload.d.ts +37 -0
- package/dist/commands/steam-upload.js +174 -0
- package/dist/commands/steam-upload.js.map +1 -0
- package/dist/commands/steam.d.ts +1 -0
- package/dist/commands/steam.js +504 -0
- package/dist/commands/steam.js.map +1 -0
- package/dist/commands/verify.js +6 -0
- package/dist/commands/verify.js.map +1 -1
- package/dist/editor/asset-viewer.js +5 -4
- package/dist/editor/asset-viewer.js.map +1 -1
- package/dist/editor/server.js +1 -1
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.js +3 -3
- package/dist/forge/browser-host.d.ts +1 -1
- package/dist/forge/browser-host.js +1 -1
- package/dist/forge/browser-host.js.map +1 -1
- package/dist/ios/native-overrides.d.ts +27 -0
- package/dist/ios/native-overrides.js +87 -0
- package/dist/ios/native-overrides.js.map +1 -0
- package/dist/ios/record-validate.d.ts +1 -33
- package/dist/ios/record-validate.js +2 -100
- package/dist/ios/record-validate.js.map +1 -1
- package/dist/ios/store-bridge.d.ts +26 -0
- package/dist/ios/store-bridge.js +224 -0
- package/dist/ios/store-bridge.js.map +1 -0
- package/dist/ios/storekit-config.d.ts +15 -0
- package/dist/ios/storekit-config.js +106 -0
- package/dist/ios/storekit-config.js.map +1 -0
- package/dist/ios/xcode-template.d.ts +12 -3
- package/dist/ios/xcode-template.js +158 -27
- package/dist/ios/xcode-template.js.map +1 -1
- package/dist/package/collect-urls.d.ts +1 -1
- package/dist/package/collect-urls.js +10 -2
- package/dist/package/collect-urls.js.map +1 -1
- package/dist/package/offline-payload.d.ts +35 -0
- package/dist/package/offline-payload.js +142 -0
- package/dist/package/offline-payload.js.map +1 -0
- package/dist/package/packaged-check.d.ts +39 -0
- package/dist/package/packaged-check.js +104 -0
- package/dist/package/packaged-check.js.map +1 -0
- package/dist/package/transform.d.ts +4 -4
- package/dist/package/transform.js +3 -3
- package/dist/project/published-three.d.ts +14 -0
- package/dist/project/published-three.js +91 -0
- package/dist/project/published-three.js.map +1 -0
- package/dist/publish/bundle.js +6 -0
- package/dist/publish/bundle.js.map +1 -1
- package/dist/scaffold/project-files.js +7 -4
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.js +2 -0
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/skill-files.js +3 -1
- package/dist/scaffold/skill-files.js.map +1 -1
- package/dist/scaffold/steam-skill-files.d.ts +2 -0
- package/dist/scaffold/steam-skill-files.js +7 -0
- package/dist/scaffold/steam-skill-files.js.map +1 -0
- package/dist/steam/art-generate.d.ts +55 -0
- package/dist/steam/art-generate.js +174 -0
- package/dist/steam/art-generate.js.map +1 -0
- package/dist/steam/art-render.d.ts +25 -0
- package/dist/steam/art-render.js +157 -0
- package/dist/steam/art-render.js.map +1 -0
- package/dist/steam/art-use.d.ts +20 -0
- package/dist/steam/art-use.js +84 -0
- package/dist/steam/art-use.js.map +1 -0
- package/dist/steam/art.d.ts +98 -0
- package/dist/steam/art.js +243 -0
- package/dist/steam/art.js.map +1 -0
- package/dist/steam/check.d.ts +21 -0
- package/dist/steam/check.js +113 -0
- package/dist/steam/check.js.map +1 -0
- package/dist/steam/config.d.ts +57 -0
- package/dist/steam/config.js +145 -0
- package/dist/steam/config.js.map +1 -0
- package/dist/steam/electron-cache.d.ts +11 -0
- package/dist/steam/electron-cache.js +105 -0
- package/dist/steam/electron-cache.js.map +1 -0
- package/dist/steam/fuses.d.ts +16 -0
- package/dist/steam/fuses.js +75 -0
- package/dist/steam/fuses.js.map +1 -0
- package/dist/steam/icon-files.d.ts +16 -0
- package/dist/steam/icon-files.js +70 -0
- package/dist/steam/icon-files.js.map +1 -0
- package/dist/steam/image-info.d.ts +16 -0
- package/dist/steam/image-info.js +89 -0
- package/dist/steam/image-info.js.map +1 -0
- package/dist/steam/launch-check.d.ts +12 -0
- package/dist/steam/launch-check.js +101 -0
- package/dist/steam/launch-check.js.map +1 -0
- package/dist/steam/media.d.ts +48 -0
- package/dist/steam/media.js +155 -0
- package/dist/steam/media.js.map +1 -0
- package/dist/steam/package-macos.d.ts +52 -0
- package/dist/steam/package-macos.js +268 -0
- package/dist/steam/package-macos.js.map +1 -0
- package/dist/steam/package-windows.d.ts +23 -0
- package/dist/steam/package-windows.js +83 -0
- package/dist/steam/package-windows.js.map +1 -0
- package/dist/steam/paths.d.ts +35 -0
- package/dist/steam/paths.js +44 -0
- package/dist/steam/paths.js.map +1 -0
- package/dist/steam/playtest-shots.d.ts +18 -0
- package/dist/steam/playtest-shots.js +83 -0
- package/dist/steam/playtest-shots.js.map +1 -0
- package/dist/steam/portal-recipes.d.ts +25 -0
- package/dist/steam/portal-recipes.js +61 -0
- package/dist/steam/portal-recipes.js.map +1 -0
- package/dist/steam/portal.d.ts +58 -0
- package/dist/steam/portal.js +340 -0
- package/dist/steam/portal.js.map +1 -0
- package/dist/steam/raster.d.ts +58 -0
- package/dist/steam/raster.js +285 -0
- package/dist/steam/raster.js.map +1 -0
- package/dist/steam/shell-files.d.ts +17 -0
- package/dist/steam/shell-files.js +268 -0
- package/dist/steam/shell-files.js.map +1 -0
- package/dist/steam/specs.d.ts +61 -0
- package/dist/steam/specs.js +103 -0
- package/dist/steam/specs.js.map +1 -0
- package/dist/steam/steampipe.d.ts +46 -0
- package/dist/steam/steampipe.js +170 -0
- package/dist/steam/steampipe.js.map +1 -0
- package/dist/steam/store-copy.d.ts +88 -0
- package/dist/steam/store-copy.js +214 -0
- package/dist/steam/store-copy.js.map +1 -0
- package/dist/steam/tags.d.ts +9 -0
- package/dist/steam/tags.js +52 -0
- package/dist/steam/tags.js.map +1 -0
- package/dist/steam/trailer.d.ts +34 -0
- package/dist/steam/trailer.js +141 -0
- package/dist/steam/trailer.js.map +1 -0
- package/dist/theme/kernel.d.ts +14 -0
- package/dist/theme/kernel.js +15 -0
- package/dist/theme/kernel.js.map +1 -0
- package/dist/theme/theme-io.js +11 -16
- package/dist/theme/theme-io.js.map +1 -1
- package/dist/vendor/hud-theme/catalog.js +71 -0
- package/dist/vendor/hud-theme/engine/hud/ThemeTokens.js +115 -0
- package/dist/{theme/theme-color-math.js → vendor/hud-theme/engine/hud/colorMath.js} +13 -14
- package/dist/vendor/hud-theme/engine/hud/decorations.js +179 -0
- package/dist/vendor/hud-theme/engine/hud/fonts.js +147 -0
- package/dist/vendor/hud-theme/engine/hud/presets.js +80 -0
- package/dist/vendor/hud-theme/engine/hud/resolveWorldTheme.js +66 -0
- package/dist/vendor/hud-theme/engine/hud/themes/bitmagic.js +29 -0
- package/dist/vendor/hud-theme/engine/hud/themes/nexus.js +69 -0
- package/dist/vendor/hud-theme/engine/hud/themes/racing.js +57 -0
- package/dist/vendor/hud-theme/engine/hud/themes/rift-raider.js +63 -0
- package/dist/vendor/hud-theme/engine/hud/themes/simulator.js +55 -0
- package/dist/vendor/hud-theme/engine/hud/themes/village-keep.js +108 -0
- package/dist/vendor/hud-theme/engine/hud/validateTheme.js +535 -0
- package/dist/vendor/hud-theme/index.js +26 -0
- package/dist/{theme/theme-merge.js → vendor/hud-theme/merge.js} +0 -1
- package/dist/{theme/theme-report.js → vendor/hud-theme/report.js} +8 -9
- package/dist/{theme/theme-stored.js → vendor/hud-theme/stored.js} +6 -9
- package/dist/{theme/theme-validate.js → vendor/hud-theme/validate-strict.js} +102 -164
- package/dist/verify/browser.d.ts +11 -0
- package/dist/verify/browser.js +222 -155
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/categories.d.ts +1 -1
- package/dist/verify/categories.js +1 -0
- package/dist/verify/categories.js.map +1 -1
- package/dist/verify/classify.d.ts +14 -2
- package/dist/verify/classify.js +23 -4
- package/dist/verify/classify.js.map +1 -1
- package/dist/verify/hud-theme.d.ts +19 -14
- package/dist/verify/hud-theme.js +23 -17
- package/dist/verify/hud-theme.js.map +1 -1
- package/dist/verify/mobile-parity.d.ts +1 -1
- package/dist/verify/mobile-parity.js +1 -1
- package/dist/verify/start.d.ts +3 -2
- package/dist/verify/start.js +1 -1
- package/dist/verify/start.js.map +1 -1
- package/package.json +8 -6
- package/dist/theme/hud-theme-catalog.d.ts +0 -235
- package/dist/theme/hud-theme-catalog.js +0 -389
- package/dist/theme/hud-theme-catalog.js.map +0 -1
- package/dist/theme/theme-color-math.d.ts +0 -21
- package/dist/theme/theme-color-math.js.map +0 -1
- package/dist/theme/theme-merge.d.ts +0 -36
- package/dist/theme/theme-merge.js.map +0 -1
- package/dist/theme/theme-report.d.ts +0 -28
- package/dist/theme/theme-report.js.map +0 -1
- package/dist/theme/theme-stored.d.ts +0 -21
- package/dist/theme/theme-stored.js.map +0 -1
- package/dist/theme/theme-validate.d.ts +0 -41
- package/dist/theme/theme-validate.js.map +0 -1
|
@@ -1,77 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The strict write-time HUD theme validator.
|
|
3
3
|
*
|
|
4
|
-
* STRICTER than the engine's load-time validator on
|
|
5
|
-
* is the design: at WRITE time a bad per-element leaf
|
|
6
|
-
* fix in the same turn, while at LOAD time the engine
|
|
7
|
-
* already-published themes, so it warns and drops the leaf
|
|
8
|
-
* this validator accepts, the engine accepts; the reverse is
|
|
4
|
+
* STRICTER than the engine's load-time validator (`validateAndResolveTheme`) on
|
|
5
|
+
* purpose, and that asymmetry is the design: at WRITE time a bad per-element leaf
|
|
6
|
+
* is an error the agent can fix in the same turn, while at LOAD time the engine
|
|
7
|
+
* must keep rendering already-published themes, so it warns and drops the leaf
|
|
8
|
+
* instead. Everything this validator accepts, the engine accepts; the reverse is
|
|
9
|
+
* not required. This package's tests hold that rule over a fixture corpus.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* is broken" rather than "the value was wrong". Parity with the engine is
|
|
15
|
-
* enforced by scripts/check-hud-catalog.ts, which runs both validators over a
|
|
16
|
-
* fixture corpus under tsx and compares verdicts.
|
|
17
|
-
*
|
|
18
|
-
* Validation rules mirror game/src/engine/hud/validateTheme.ts; vocabulary
|
|
19
|
-
* comes from the drift-checked catalog next door.
|
|
11
|
+
* Both modes read the same vocabulary and ranges from the engine's ThemeTokens.ts
|
|
12
|
+
* and the same catalogues (fonts, decorations), and share the engine's
|
|
13
|
+
* `suggestClosest`, so they cannot disagree about what a key, a range or a
|
|
14
|
+
* "Closest match" is — only about how loudly to fail.
|
|
20
15
|
*/
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'top', 'top-right', 'right', 'bottom-right', 'bottom', 'bottom-left', 'left', 'top-left', 'center',
|
|
27
|
-
];
|
|
28
|
-
const REPEAT_VALUES = ['no-repeat', 'repeat', 'repeat-x', 'repeat-y'];
|
|
29
|
-
const BORDER_REPEAT_VALUES = ['stretch', 'repeat', 'round', 'space'];
|
|
30
|
-
const COLOR_KEYS = [
|
|
31
|
-
'primary', 'danger', 'warning', 'success', 'background', 'surface', 'text', 'textMuted', 'outline',
|
|
32
|
-
];
|
|
33
|
-
const REQUIRED_COLOR_KEYS = ['primary', 'danger', 'background', 'surface', 'text'];
|
|
34
|
-
// Levenshtein, matching the engine: suggest only when the distance is small
|
|
35
|
-
// relative to the input. A wild guess gets the full candidate list from the
|
|
36
|
-
// error message, never false confidence in one arbitrary name.
|
|
37
|
-
function editDistance(a, b) {
|
|
38
|
-
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
39
|
-
for (let i = 1; i <= a.length; i++) {
|
|
40
|
-
const cur = [i];
|
|
41
|
-
for (let j = 1; j <= b.length; j++) {
|
|
42
|
-
const deletion = (prev[j] ?? 0) + 1;
|
|
43
|
-
const insertion = (cur[j - 1] ?? 0) + 1;
|
|
44
|
-
const substitution = (prev[j - 1] ?? 0) + (a[i - 1] === b[j - 1] ? 0 : 1);
|
|
45
|
-
cur[j] = Math.min(deletion, insertion, substitution);
|
|
46
|
-
}
|
|
47
|
-
prev = cur;
|
|
48
|
-
}
|
|
49
|
-
return prev[b.length] ?? 0;
|
|
50
|
-
}
|
|
51
|
-
export function suggestClosest(value, candidates) {
|
|
52
|
-
const v = value.toLowerCase();
|
|
53
|
-
let best;
|
|
54
|
-
let bestScore = Infinity;
|
|
55
|
-
for (const c of candidates) {
|
|
56
|
-
const score = editDistance(v, c.toLowerCase());
|
|
57
|
-
if (score < bestScore) {
|
|
58
|
-
bestScore = score;
|
|
59
|
-
best = c;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const maxDistance = Math.max(2, Math.ceil(v.length / 3));
|
|
63
|
-
return bestScore <= maxDistance ? best : undefined;
|
|
64
|
-
}
|
|
16
|
+
import { BACKGROUND_POSITIONS, BACKGROUND_REPEATS, BACKGROUND_SIZE_KEYWORDS, BACKGROUND_TILE_SIZE, BORDER_IMAGE_REPEATS, DECORATION_SLOT_KINDS, FONT_BODY_WEIGHTS, FONT_HEADING_WEIGHTS, GLOW_KEYS, HUD_ELEMENT_CLASSES, IMAGE_RENDERINGS, LABEL_CASES, MAX_INLINE_SVG_LENGTH, REQUIRED_THEME_COLOR_KEYS, THEME_COLOR_KEYS, THEME_ICON_KEYS, THEME_NUMBER_RANGES, isHexColor, } from './engine/hud/ThemeTokens.js';
|
|
17
|
+
import { HUD_FONTS } from './engine/hud/fonts.js';
|
|
18
|
+
import { HUD_DECORATIONS } from './engine/hud/decorations.js';
|
|
19
|
+
import { suggestClosest } from './engine/hud/validateTheme.js';
|
|
20
|
+
export { suggestClosest };
|
|
65
21
|
function isObject(v) {
|
|
66
22
|
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
67
23
|
}
|
|
68
|
-
function
|
|
24
|
+
function includes(list, value) {
|
|
25
|
+
return list.includes(value);
|
|
26
|
+
}
|
|
27
|
+
function checkNumber(ctx, path, value, token) {
|
|
28
|
+
const [min, max] = THEME_NUMBER_RANGES[token];
|
|
69
29
|
if (typeof value !== 'number' || !Number.isFinite(value) || value < min || value > max) {
|
|
70
30
|
ctx.errors.push({ path, message: `${JSON.stringify(value)} is not a number in [${min}, ${max}]` });
|
|
71
31
|
}
|
|
72
32
|
}
|
|
73
33
|
function checkHexAt(ctx, path, value) {
|
|
74
|
-
if (
|
|
34
|
+
if (!isHexColor(value)) {
|
|
75
35
|
ctx.errors.push({
|
|
76
36
|
path,
|
|
77
37
|
message: `${JSON.stringify(value)} is not a 6-digit hex color`,
|
|
@@ -89,6 +49,14 @@ function checkKeyed(ctx, path, value, allowed) {
|
|
|
89
49
|
...(close ? { suggestion: `Closest match: "${close}"` } : {}),
|
|
90
50
|
});
|
|
91
51
|
}
|
|
52
|
+
function unknownKey(ctx, path, message, value, known) {
|
|
53
|
+
const close = suggestClosest(value, known);
|
|
54
|
+
ctx.errors.push({
|
|
55
|
+
path,
|
|
56
|
+
message,
|
|
57
|
+
suggestion: close ? `Closest match: "${close}". Known: ${known.join(', ')}` : `Known: ${known.join(', ')}`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
92
60
|
function checkFontBlock(ctx, path, raw, required) {
|
|
93
61
|
if (raw === undefined) {
|
|
94
62
|
if (required)
|
|
@@ -119,15 +87,11 @@ function checkFontBlock(ctx, path, raw, required) {
|
|
|
119
87
|
// corrected object is what gets written, and the warning explains the
|
|
120
88
|
// swap so the agent can mention it to the user.
|
|
121
89
|
//
|
|
122
|
-
// Only a VALID case value is eligible:
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
// the kit passed with a warning where the engine hard-rejects the whole
|
|
126
|
-
// theme — kit-ok/engine-reject, the exact parity direction the gate
|
|
127
|
-
// exists to forbid.
|
|
90
|
+
// Only a VALID case value is eligible: correcting any string (including
|
|
91
|
+
// "lower"/"uppercase") would let the enum check below see the corrected
|
|
92
|
+
// value and pass a theme the engine hard-rejects.
|
|
128
93
|
const entry = HUD_FONTS[raw.key];
|
|
129
|
-
if ((raw.case
|
|
130
|
-
&& !entry.suitsCase.includes(raw.case)) {
|
|
94
|
+
if (includes(LABEL_CASES, raw.case) && !includes(entry.suitsCase, raw.case)) {
|
|
131
95
|
const originalCase = raw.case;
|
|
132
96
|
const correctedCase = entry.suitsCase[0];
|
|
133
97
|
raw.case = correctedCase;
|
|
@@ -138,21 +102,21 @@ function checkFontBlock(ctx, path, raw, required) {
|
|
|
138
102
|
});
|
|
139
103
|
}
|
|
140
104
|
}
|
|
141
|
-
if (raw.case !== undefined &&
|
|
105
|
+
if (raw.case !== undefined && !includes(LABEL_CASES, raw.case)) {
|
|
142
106
|
ctx.errors.push({ path: `${path}.case`, message: `${JSON.stringify(raw.case)} is not "upper" or "normal"` });
|
|
143
107
|
}
|
|
144
|
-
if (raw.weightBody !== undefined && !
|
|
145
|
-
ctx.errors.push({ path: `${path}.weightBody`, message: `${JSON.stringify(raw.weightBody)} is not one of
|
|
108
|
+
if (raw.weightBody !== undefined && !includes(FONT_BODY_WEIGHTS, raw.weightBody)) {
|
|
109
|
+
ctx.errors.push({ path: `${path}.weightBody`, message: `${JSON.stringify(raw.weightBody)} is not one of ${FONT_BODY_WEIGHTS.join(', ')}` });
|
|
146
110
|
}
|
|
147
|
-
if (raw.weightHeading !== undefined && !
|
|
148
|
-
ctx.errors.push({ path: `${path}.weightHeading`, message: `${JSON.stringify(raw.weightHeading)} is not one of
|
|
111
|
+
if (raw.weightHeading !== undefined && !includes(FONT_HEADING_WEIGHTS, raw.weightHeading)) {
|
|
112
|
+
ctx.errors.push({ path: `${path}.weightHeading`, message: `${JSON.stringify(raw.weightHeading)} is not one of ${FONT_HEADING_WEIGHTS.join(', ')}` });
|
|
149
113
|
}
|
|
150
114
|
if (raw.trackingLabel !== undefined)
|
|
151
|
-
checkNumber(ctx, `${path}.trackingLabel`, raw.trackingLabel,
|
|
115
|
+
checkNumber(ctx, `${path}.trackingLabel`, raw.trackingLabel, 'trackingLabel');
|
|
152
116
|
if (raw.outlineWidth !== undefined)
|
|
153
|
-
checkNumber(ctx, `${path}.outlineWidth`, raw.outlineWidth,
|
|
117
|
+
checkNumber(ctx, `${path}.outlineWidth`, raw.outlineWidth, 'outlineWidth');
|
|
154
118
|
if (raw.sizeScale !== undefined)
|
|
155
|
-
checkNumber(ctx, `${path}.sizeScale`, raw.sizeScale,
|
|
119
|
+
checkNumber(ctx, `${path}.sizeScale`, raw.sizeScale, 'sizeScale');
|
|
156
120
|
}
|
|
157
121
|
function checkColorsBlock(ctx, path, raw, required) {
|
|
158
122
|
if (raw === undefined) {
|
|
@@ -165,20 +129,15 @@ function checkColorsBlock(ctx, path, raw, required) {
|
|
|
165
129
|
return;
|
|
166
130
|
}
|
|
167
131
|
if (required) {
|
|
168
|
-
for (const key of
|
|
132
|
+
for (const key of REQUIRED_THEME_COLOR_KEYS) {
|
|
169
133
|
if (raw[key] === undefined) {
|
|
170
134
|
ctx.errors.push({ path: `${path}.${key}`, message: `colors.${key} is required (hex string)` });
|
|
171
135
|
}
|
|
172
136
|
}
|
|
173
137
|
}
|
|
174
138
|
for (const key of Object.keys(raw)) {
|
|
175
|
-
if (!
|
|
176
|
-
|
|
177
|
-
ctx.errors.push({
|
|
178
|
-
path: `${path}.${key}`,
|
|
179
|
-
message: `unknown color token "${key}"`,
|
|
180
|
-
suggestion: close ? `Closest match: "${close}". Known: ${COLOR_KEYS.join(', ')}` : `Known: ${COLOR_KEYS.join(', ')}`,
|
|
181
|
-
});
|
|
139
|
+
if (!includes(THEME_COLOR_KEYS, key)) {
|
|
140
|
+
unknownKey(ctx, `${path}.${key}`, `unknown color token "${key}"`, key, THEME_COLOR_KEYS);
|
|
182
141
|
continue;
|
|
183
142
|
}
|
|
184
143
|
checkHexAt(ctx, `${path}.${key}`, raw[key]);
|
|
@@ -191,21 +150,15 @@ function checkShapeBlock(ctx, path, raw) {
|
|
|
191
150
|
ctx.errors.push({ path, message: 'shape must be an object' });
|
|
192
151
|
return;
|
|
193
152
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (raw.borderWidth !== undefined)
|
|
199
|
-
checkNumber(ctx, `${path}.borderWidth`, raw.borderWidth, 0, 6);
|
|
200
|
-
if (raw.bevel !== undefined)
|
|
201
|
-
checkNumber(ctx, `${path}.bevel`, raw.bevel, 0, 1);
|
|
202
|
-
if (raw.gloss !== undefined)
|
|
203
|
-
checkNumber(ctx, `${path}.gloss`, raw.gloss, 0, 1);
|
|
153
|
+
for (const token of ['radiusPill', 'radiusCard', 'borderWidth', 'bevel', 'gloss']) {
|
|
154
|
+
if (raw[token] !== undefined)
|
|
155
|
+
checkNumber(ctx, `${path}.${token}`, raw[token], token);
|
|
156
|
+
}
|
|
204
157
|
if (raw.glow !== undefined)
|
|
205
|
-
checkKeyed(ctx, `${path}.glow`, raw.glow,
|
|
158
|
+
checkKeyed(ctx, `${path}.glow`, raw.glow, GLOW_KEYS);
|
|
206
159
|
if (raw.glowColor !== undefined)
|
|
207
160
|
checkHexAt(ctx, `${path}.glowColor`, raw.glowColor);
|
|
208
|
-
if (raw.imageRendering !== undefined &&
|
|
161
|
+
if (raw.imageRendering !== undefined && !includes(IMAGE_RENDERINGS, raw.imageRendering)) {
|
|
209
162
|
ctx.errors.push({ path: `${path}.imageRendering`, message: `${JSON.stringify(raw.imageRendering)} is not "auto" or "pixelated"` });
|
|
210
163
|
}
|
|
211
164
|
}
|
|
@@ -226,7 +179,7 @@ function checkDecorationValue(ctx, path, slotName, value) {
|
|
|
226
179
|
// Suitability is a WARNING, matching the engine: an unsuited pairing
|
|
227
180
|
// renders, it just usually looks wrong.
|
|
228
181
|
const entry = HUD_DECORATIONS[value];
|
|
229
|
-
if (!entry.suitsSlots
|
|
182
|
+
if (!includes(entry.suitsSlots, slotName)) {
|
|
230
183
|
ctx.warnings.push({
|
|
231
184
|
path,
|
|
232
185
|
message: `decoration "${value}" isn't designed for slot "${slotName}" (suits: ${entry.suitsSlots.join(', ')})`,
|
|
@@ -239,18 +192,18 @@ function checkDecorationValue(ctx, path, slotName, value) {
|
|
|
239
192
|
ctx.errors.push({ path: `${path}.svg`, message: 'inline decoration must have an svg string' });
|
|
240
193
|
return;
|
|
241
194
|
}
|
|
242
|
-
if (value.svg.length >
|
|
195
|
+
if (value.svg.length > MAX_INLINE_SVG_LENGTH) {
|
|
243
196
|
ctx.errors.push({ path: `${path}.svg`, message: 'inline SVG exceeds 16kb limit' });
|
|
244
197
|
}
|
|
245
198
|
if (slotName === 'borderImage') {
|
|
246
199
|
if (value.borderSlice !== undefined)
|
|
247
|
-
checkNumber(ctx, `${path}.borderSlice`, value.borderSlice,
|
|
200
|
+
checkNumber(ctx, `${path}.borderSlice`, value.borderSlice, 'borderSlice');
|
|
248
201
|
if (value.repeat !== undefined)
|
|
249
|
-
checkKeyed(ctx, `${path}.repeat`, value.repeat,
|
|
202
|
+
checkKeyed(ctx, `${path}.repeat`, value.repeat, BORDER_IMAGE_REPEATS);
|
|
250
203
|
}
|
|
251
204
|
else {
|
|
252
|
-
if (value.size !== undefined &&
|
|
253
|
-
&& !(typeof value.size === 'string' &&
|
|
205
|
+
if (value.size !== undefined && !includes(BACKGROUND_SIZE_KEYWORDS, value.size)
|
|
206
|
+
&& !(typeof value.size === 'string' && BACKGROUND_TILE_SIZE.test(value.size))) {
|
|
254
207
|
ctx.errors.push({
|
|
255
208
|
path: `${path}.size`,
|
|
256
209
|
message: `${JSON.stringify(value.size)} is not "cover", "contain", "auto", or an explicit tile size`,
|
|
@@ -258,14 +211,20 @@ function checkDecorationValue(ctx, path, slotName, value) {
|
|
|
258
211
|
});
|
|
259
212
|
}
|
|
260
213
|
if (value.position !== undefined)
|
|
261
|
-
checkKeyed(ctx, `${path}.position`, value.position,
|
|
214
|
+
checkKeyed(ctx, `${path}.position`, value.position, BACKGROUND_POSITIONS);
|
|
262
215
|
if (value.repeat !== undefined)
|
|
263
|
-
checkKeyed(ctx, `${path}.repeat`, value.repeat,
|
|
216
|
+
checkKeyed(ctx, `${path}.repeat`, value.repeat, BACKGROUND_REPEATS);
|
|
264
217
|
}
|
|
265
218
|
return;
|
|
266
219
|
}
|
|
267
220
|
ctx.errors.push({ path, message: 'expected curated decoration key string or inline SVG object' });
|
|
268
221
|
}
|
|
222
|
+
function checkElementClassKey(ctx, path, elementClass) {
|
|
223
|
+
if (includes(HUD_ELEMENT_CLASSES, elementClass))
|
|
224
|
+
return true;
|
|
225
|
+
unknownKey(ctx, `${path}.${elementClass}`, `unknown HUD element class "${elementClass}"`, elementClass, HUD_ELEMENT_CLASSES);
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
269
228
|
function checkDecorationsBlock(ctx, path, raw) {
|
|
270
229
|
if (raw === undefined || raw === null)
|
|
271
230
|
return;
|
|
@@ -274,30 +233,14 @@ function checkDecorationsBlock(ctx, path, raw) {
|
|
|
274
233
|
return;
|
|
275
234
|
}
|
|
276
235
|
for (const elementClass of Object.keys(raw)) {
|
|
277
|
-
if (!
|
|
278
|
-
const close = suggestClosest(elementClass, HUD_ELEMENT_CLASSES);
|
|
279
|
-
ctx.errors.push({
|
|
280
|
-
path: `${path}.${elementClass}`,
|
|
281
|
-
message: `unknown HUD element class "${elementClass}"`,
|
|
282
|
-
suggestion: close
|
|
283
|
-
? `Closest match: "${close}". Known: ${HUD_ELEMENT_CLASSES.join(', ')}`
|
|
284
|
-
: `Known: ${HUD_ELEMENT_CLASSES.join(', ')}`,
|
|
285
|
-
});
|
|
236
|
+
if (!checkElementClassKey(ctx, path, elementClass))
|
|
286
237
|
continue;
|
|
287
|
-
}
|
|
288
238
|
const slots = raw[elementClass];
|
|
289
239
|
if (!isObject(slots))
|
|
290
240
|
continue;
|
|
291
241
|
for (const slotName of Object.keys(slots)) {
|
|
292
|
-
if (!
|
|
293
|
-
|
|
294
|
-
ctx.errors.push({
|
|
295
|
-
path: `${path}.${elementClass}.${slotName}`,
|
|
296
|
-
message: `unknown slot "${slotName}"`,
|
|
297
|
-
suggestion: close
|
|
298
|
-
? `Closest match: "${close}". Known: ${HUD_DECORATION_SLOTS.join(', ')}`
|
|
299
|
-
: `Known: ${HUD_DECORATION_SLOTS.join(', ')}`,
|
|
300
|
-
});
|
|
242
|
+
if (!includes(DECORATION_SLOT_KINDS, slotName)) {
|
|
243
|
+
unknownKey(ctx, `${path}.${elementClass}.${slotName}`, `unknown slot "${slotName}"`, slotName, DECORATION_SLOT_KINDS);
|
|
301
244
|
continue;
|
|
302
245
|
}
|
|
303
246
|
checkDecorationValue(ctx, `${path}.${elementClass}.${slotName}`, slotName, slots[slotName]);
|
|
@@ -312,17 +255,8 @@ function checkElementsBlock(ctx, path, raw) {
|
|
|
312
255
|
return;
|
|
313
256
|
}
|
|
314
257
|
for (const elementClass of Object.keys(raw)) {
|
|
315
|
-
if (!
|
|
316
|
-
const close = suggestClosest(elementClass, HUD_ELEMENT_CLASSES);
|
|
317
|
-
ctx.errors.push({
|
|
318
|
-
path: `${path}.${elementClass}`,
|
|
319
|
-
message: `unknown HUD element class "${elementClass}"`,
|
|
320
|
-
suggestion: close
|
|
321
|
-
? `Closest match: "${close}". Known: ${HUD_ELEMENT_CLASSES.join(', ')}`
|
|
322
|
-
: `Known: ${HUD_ELEMENT_CLASSES.join(', ')}`,
|
|
323
|
-
});
|
|
258
|
+
if (!checkElementClassKey(ctx, path, elementClass))
|
|
324
259
|
continue;
|
|
325
|
-
}
|
|
326
260
|
const override = raw[elementClass];
|
|
327
261
|
if (!isObject(override)) {
|
|
328
262
|
ctx.errors.push({ path: `${path}.${elementClass}`, message: 'expected a partial theme object' });
|
|
@@ -339,6 +273,34 @@ function checkElementsBlock(ctx, path, raw) {
|
|
|
339
273
|
checkDecorationsBlock(ctx, `${path}.${elementClass}.decorations`, override.decorations);
|
|
340
274
|
}
|
|
341
275
|
}
|
|
276
|
+
function checkIconsBlock(ctx, raw) {
|
|
277
|
+
if (raw === undefined || raw === null)
|
|
278
|
+
return;
|
|
279
|
+
if (!isObject(raw)) {
|
|
280
|
+
ctx.errors.push({ path: 'icons', message: `icons must be an object keyed by slot name (${THEME_ICON_KEYS.join(', ')})` });
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
for (const key of Object.keys(raw)) {
|
|
284
|
+
if (!includes(THEME_ICON_KEYS, key)) {
|
|
285
|
+
ctx.errors.push({
|
|
286
|
+
path: `icons.${key}`,
|
|
287
|
+
message: `unknown icon slot "${key}"`,
|
|
288
|
+
suggestion: `Known: ${THEME_ICON_KEYS.join(', ')}`,
|
|
289
|
+
});
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const value = raw[key];
|
|
293
|
+
if (value === undefined || value === null)
|
|
294
|
+
continue;
|
|
295
|
+
if (typeof value !== 'string') {
|
|
296
|
+
ctx.errors.push({ path: `icons.${key}`, message: 'icon SVG must be a string' });
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (value.length > MAX_INLINE_SVG_LENGTH) {
|
|
300
|
+
ctx.errors.push({ path: `icons.${key}`, message: 'icon SVG exceeds 16kb limit' });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
342
304
|
/**
|
|
343
305
|
* Whether an issue would make the ENGINE reject the whole theme, or only drop a
|
|
344
306
|
* leaf while the theme keeps rendering.
|
|
@@ -358,6 +320,11 @@ export function isEngineFatalIssue(issue) {
|
|
|
358
320
|
return false;
|
|
359
321
|
return true;
|
|
360
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Validate a whole inline theme at write time. Mutates `theme` in one place: a
|
|
325
|
+
* font case the font is not designed for is auto-corrected (with a warning), and
|
|
326
|
+
* the corrected object is what the caller writes.
|
|
327
|
+
*/
|
|
361
328
|
export function validateThemeStrict(theme) {
|
|
362
329
|
const ctx = { errors: [], warnings: [] };
|
|
363
330
|
if (!isObject(theme)) {
|
|
@@ -372,35 +339,6 @@ export function validateThemeStrict(theme) {
|
|
|
372
339
|
checkShapeBlock(ctx, 'shape', theme.shape);
|
|
373
340
|
checkDecorationsBlock(ctx, 'decorations', theme.decorations);
|
|
374
341
|
checkElementsBlock(ctx, 'elements', theme.elements);
|
|
375
|
-
|
|
376
|
-
const icons = theme.icons;
|
|
377
|
-
if (icons !== undefined && icons !== null) {
|
|
378
|
-
if (!isObject(icons)) {
|
|
379
|
-
ctx.errors.push({ path: 'icons', message: 'icons must be an object keyed by slot name (heart, mouse, arrow)' });
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
for (const key of Object.keys(icons)) {
|
|
383
|
-
if (!HUD_ICON_KEYS.includes(key)) {
|
|
384
|
-
ctx.errors.push({
|
|
385
|
-
path: `icons.${key}`,
|
|
386
|
-
message: `unknown icon slot "${key}"`,
|
|
387
|
-
suggestion: `Known: ${HUD_ICON_KEYS.join(', ')}`,
|
|
388
|
-
});
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
const value = icons[key];
|
|
392
|
-
if (value === undefined || value === null)
|
|
393
|
-
continue;
|
|
394
|
-
if (typeof value !== 'string') {
|
|
395
|
-
ctx.errors.push({ path: `icons.${key}`, message: 'icon SVG must be a string' });
|
|
396
|
-
continue;
|
|
397
|
-
}
|
|
398
|
-
if (value.length > 16384) {
|
|
399
|
-
ctx.errors.push({ path: `icons.${key}`, message: 'icon SVG exceeds 16kb limit' });
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
342
|
+
checkIconsBlock(ctx, theme.icons);
|
|
404
343
|
return { ok: ctx.errors.length === 0, errors: ctx.errors, warnings: ctx.warnings };
|
|
405
344
|
}
|
|
406
|
-
//# sourceMappingURL=theme-validate.js.map
|
package/dist/verify/browser.d.ts
CHANGED
|
@@ -102,3 +102,14 @@ export declare class VerifyBrowserSession {
|
|
|
102
102
|
close(): Promise<void>;
|
|
103
103
|
check(options: HeadlessCheckOptions): Promise<VerifyObservations>;
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* One check on a page that already exists: arm the probes, `navigate` (a `goto` for verify, a
|
|
107
|
+
* `reload` for a packaged desktop app that loaded its own page before anything could attach),
|
|
108
|
+
* start the game the way a player does, settle, and observe. Split from
|
|
109
|
+
* {@link VerifyBrowserSession.check} so `bitmagic steam build` can hold the real Electron app to
|
|
110
|
+
* the same verdict a browser tab gets.
|
|
111
|
+
*/
|
|
112
|
+
export declare function observeGamePage(page: Page, options: HeadlessCheckOptions, viewport: {
|
|
113
|
+
width: number;
|
|
114
|
+
height: number;
|
|
115
|
+
}, navigate: (page: Page) => Promise<unknown>): Promise<VerifyObservations>;
|