@bitmagic/cli 0.1.52-dev.10 → 0.1.52-dev.12
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 +44 -8
- package/dist/assets/add.js +2 -1
- package/dist/assets/add.js.map +1 -1
- package/dist/assets/voxelize-glb.d.ts +17 -1
- package/dist/assets/voxelize-glb.js +43 -19
- package/dist/assets/voxelize-glb.js.map +1 -1
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +6 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/generate.js +36 -67
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/judge.d.ts +5 -0
- package/dist/commands/judge.js +28 -7
- package/dist/commands/judge.js.map +1 -1
- package/dist/commands/levels.d.ts +10 -17
- package/dist/commands/levels.js +48 -48
- package/dist/commands/levels.js.map +1 -1
- package/dist/commands/publish.d.ts +6 -1
- package/dist/commands/publish.js +29 -15
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/verify.d.ts +4 -0
- package/dist/commands/verify.js +94 -51
- package/dist/commands/verify.js.map +1 -1
- package/dist/editor/journal.js +13 -7
- package/dist/editor/journal.js.map +1 -1
- package/dist/generate/model.d.ts +8 -2
- package/dist/generate/model.js +69 -21
- package/dist/generate/model.js.map +1 -1
- package/dist/generate/prop.d.ts +2 -0
- package/dist/generate/prop.js +1 -1
- package/dist/generate/prop.js.map +1 -1
- package/dist/judge/record.d.ts +3 -0
- package/dist/judge/record.js +3 -0
- package/dist/judge/record.js.map +1 -1
- package/dist/levels/registry.d.ts +5 -2
- package/dist/levels/registry.js +32 -27
- package/dist/levels/registry.js.map +1 -1
- package/dist/project/jobs.js +16 -20
- package/dist/project/jobs.js.map +1 -1
- package/dist/project/platform-declaration.d.ts +51 -0
- package/dist/project/platform-declaration.js +81 -0
- package/dist/project/platform-declaration.js.map +1 -0
- package/dist/publish/platform.d.ts +4 -8
- package/dist/publish/platform.js +19 -53
- package/dist/publish/platform.js.map +1 -1
- package/dist/scaffold/project-files.js +19 -5
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/verify/artifacts.d.ts +19 -0
- package/dist/verify/artifacts.js +34 -0
- package/dist/verify/artifacts.js.map +1 -0
- package/dist/verify/browser.d.ts +10 -0
- package/dist/verify/browser.js +79 -9
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/classify.d.ts +37 -1
- package/dist/verify/classify.js +66 -2
- package/dist/verify/classify.js.map +1 -1
- package/dist/verify/device.d.ts +43 -0
- package/dist/verify/device.js +59 -0
- package/dist/verify/device.js.map +1 -0
- package/dist/verify/iterate.d.ts +21 -2
- package/dist/verify/iterate.js +43 -36
- package/dist/verify/iterate.js.map +1 -1
- package/dist/verify/mobile-parity.d.ts +42 -0
- package/dist/verify/mobile-parity.js +84 -0
- package/dist/verify/mobile-parity.js.map +1 -0
- package/dist/verify/platform.d.ts +47 -0
- package/dist/verify/platform.js +89 -0
- package/dist/verify/platform.js.map +1 -0
- package/dist/verify/result.d.ts +90 -6
- package/dist/verify/result.js +89 -10
- package/dist/verify/result.js.map +1 -1
- package/dist/verify/url.d.ts +9 -1
- package/dist/verify/url.js +10 -2
- package/dist/verify/url.js.map +1 -1
- package/package.json +4 -4
package/dist/verify/iterate.js
CHANGED
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
* agent's five-file edit burst becomes one re-verify, and a red build reports its type errors
|
|
10
10
|
* instead of wasting a browser run on the previous emit.
|
|
11
11
|
*/
|
|
12
|
-
import * as path from 'path';
|
|
13
12
|
import { browserLaunchPlan, shouldRetrySoftwareGl, softwareGlLaunchPlan, } from '../browser-gl.js';
|
|
14
13
|
import { ReloadCoordinator, pipeTscWatch, watchProject } from '../editor/watch.js';
|
|
15
14
|
import { computeFingerprint } from '../publish/fingerprint.js';
|
|
16
15
|
import { VerifyBrowserSession, DEFAULT_START_INTERACTION } from './browser.js';
|
|
17
16
|
import { classifyVerifyRun } from './classify.js';
|
|
18
|
-
import { decideVerifyOutcome, writeVerifyRecord } from './result.js';
|
|
17
|
+
import { decideVerifyOutcome, mergeVerifyPlatformRuns, toVerifyPlatformRun, writeVerifyRecord, } from './result.js';
|
|
19
18
|
import { buildVerifyUrl } from './url.js';
|
|
19
|
+
import { verifyArtifactPaths } from './artifacts.js';
|
|
20
|
+
import { mobileContextOptions } from './device.js';
|
|
20
21
|
/**
|
|
21
22
|
* Runs verify iterations one at a time. A change that lands mid-iteration queues exactly one
|
|
22
23
|
* follow-up — not one per change, because the follow-up will see all of them anyway.
|
|
@@ -81,48 +82,54 @@ export async function runVerifyWatch(options) {
|
|
|
81
82
|
// that changes mid-run makes the record stale, and the coordinator immediately queues the
|
|
82
83
|
// iteration that will re-verify it.
|
|
83
84
|
const fingerprint = computeFingerprint(options.root);
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
85
|
+
const classifyContext = { mobilePrimary: options.mobilePrimary };
|
|
86
|
+
const runs = [];
|
|
87
|
+
const states = [];
|
|
88
|
+
let mobileParity;
|
|
89
|
+
for (const platform of options.platforms) {
|
|
90
|
+
const paths = verifyArtifactPaths(options.artifactDir, platform);
|
|
91
|
+
const checkOptions = {
|
|
92
|
+
url: buildVerifyUrl(options.port, options.renderer, platform),
|
|
93
|
+
screenshotPath: paths.screenshotPath,
|
|
94
|
+
consolePath: paths.consolePath,
|
|
95
|
+
settleMs: options.settleMs,
|
|
96
|
+
startInteraction: DEFAULT_START_INTERACTION,
|
|
97
|
+
requestedRenderer: options.renderer,
|
|
98
|
+
captureScreenshot: !options.fast,
|
|
99
|
+
...(platform === 'mobile' ? { device: mobileContextOptions(options.orientation) } : {}),
|
|
100
|
+
};
|
|
101
|
+
let observations = await session.check(checkOptions);
|
|
102
|
+
let result = classifyVerifyRun(observations, { ...classifyContext, platform });
|
|
103
|
+
let retriedWithSoftwareGl = false;
|
|
104
|
+
if (!result.ok && !onSoftwareGl && shouldRetrySoftwareGl(observations, process.env)) {
|
|
105
|
+
output.info('The WebGL context was lost — switching this watch to software rendering…');
|
|
106
|
+
await session.close();
|
|
107
|
+
session = await VerifyBrowserSession.launch(softwareGlLaunchPlan(process.env, options.renderer));
|
|
108
|
+
onSoftwareGl = true;
|
|
109
|
+
retriedWithSoftwareGl = true;
|
|
110
|
+
observations = await session.check({ ...checkOptions, consolePath: paths.retryConsolePath });
|
|
111
|
+
result = classifyVerifyRun(observations, { ...classifyContext, platform });
|
|
112
|
+
}
|
|
113
|
+
if (mobileParity === undefined)
|
|
114
|
+
mobileParity = observations.mobileParity;
|
|
115
|
+
runs.push(toVerifyPlatformRun(platform, result, observations, retriedWithSoftwareGl, paths, options.fast));
|
|
116
|
+
const state = options.describeRunState(observations);
|
|
117
|
+
states.push(options.platforms.length > 1 ? `${platform}: ${state}` : state);
|
|
107
118
|
}
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
settle: observations.settle,
|
|
111
|
-
snapshot: observations.snapshot,
|
|
112
|
-
events: observations.events,
|
|
113
|
-
...(retriedWithSoftwareGl ? { retriedWithSoftwareGl } : {}),
|
|
114
|
-
});
|
|
119
|
+
const merged = mergeVerifyPlatformRuns(runs, mobileParity);
|
|
120
|
+
const outcome = decideVerifyOutcome(merged.result, fingerprint, new Date().toISOString(), merged.extras);
|
|
115
121
|
// Recorded but never thrown: a failing iteration is information for the next edit, not the
|
|
116
122
|
// end of the loop. Publish still gates on this record exactly as it does after one-shot.
|
|
117
123
|
writeVerifyRecord(options.artifactDir, outcome.record);
|
|
124
|
+
const { failures } = merged.result;
|
|
118
125
|
const seconds = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
119
|
-
if (result.ok) {
|
|
120
|
-
output.info(`✓ verified in ${seconds}s (${reason}) — ${
|
|
126
|
+
if (merged.result.ok) {
|
|
127
|
+
output.info(`✓ verified in ${seconds}s (${reason}) — ${states.join(' | ')}`);
|
|
121
128
|
}
|
|
122
129
|
else {
|
|
123
|
-
for (const failure of
|
|
130
|
+
for (const failure of failures)
|
|
124
131
|
output.info(`✗ ${failure}`);
|
|
125
|
-
output.info(`✗ ${
|
|
132
|
+
output.info(`✗ ${failures.length} failure${failures.length === 1 ? '' : 's'} in `
|
|
126
133
|
+ `${seconds}s (${reason})`);
|
|
127
134
|
}
|
|
128
135
|
}, reportIterationError);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iterate.js","sourceRoot":"","sources":["../../src/verify/iterate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"iterate.js","sourceRoot":"","sources":["../../src/verify/iterate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the engine's mobile-parity verdict out of the console verify already captures.
|
|
3
|
+
*
|
|
4
|
+
* `GameTemplate` calls `playerController.verifyMobileParity()` on every game start — desktop runs
|
|
5
|
+
* included — and when it finds gaps the engine logs one `console.error` listing every one of
|
|
6
|
+
* them (`game/src/engine/PlayerController.ts`, documented in
|
|
7
|
+
* `game/agent-docs/control-system.md`). Nothing consumed it, so a game with actions no phone can
|
|
8
|
+
* reach passed verify silently.
|
|
9
|
+
*
|
|
10
|
+
* Parsing that line rather than listening for the `aitopia:mobile-parity-gap` postMessage the
|
|
11
|
+
* same check also fires is deliberate on two counts: the console is already captured, so this
|
|
12
|
+
* needs no new browser plumbing; and only the console line carries the `(source)` annotation
|
|
13
|
+
* naming which system registered an orphan key, which is the difference between "KeyF is
|
|
14
|
+
* unreachable" and "the attack system left KeyF unreachable".
|
|
15
|
+
*
|
|
16
|
+
* The line's format is a documented contract, but this parser still degrades rather than
|
|
17
|
+
* insisting on it: anything it cannot read comes back null, which classifies as "not observed"
|
|
18
|
+
* and never as a failure. Two legitimate ways to get null: a clean game (the engine logs nothing
|
|
19
|
+
* when parity holds) and a genre whose controller has no parity check at all — the 2D genres'
|
|
20
|
+
* `Physics2DPlayerController` deliberately has no `verifyMobileParity`, so `GameTemplate`'s
|
|
21
|
+
* duck-typed guard skips it.
|
|
22
|
+
*/
|
|
23
|
+
/** The engine's marker. Also what a reader greps the console log for. */
|
|
24
|
+
export declare const MOBILE_PARITY_MARKER = "[mobile-parity]";
|
|
25
|
+
export interface MobileParityGap {
|
|
26
|
+
/**
|
|
27
|
+
* Unpaired desktop keys exactly as the engine names them, source annotation included —
|
|
28
|
+
* `KeyF(CombatSystem)`, or `KeyF((anonymous))` when the registration passed no `source`. The
|
|
29
|
+
* annotation is the actionable half: it points at the call site to fix.
|
|
30
|
+
*/
|
|
31
|
+
unpairedDesktopKeys: string[];
|
|
32
|
+
/** Mobile buttons with no desktop key, as bare action names. */
|
|
33
|
+
unpairedMobileActions: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The gap the engine reported during this run, or null when it reported none.
|
|
37
|
+
*
|
|
38
|
+
* The last matching line wins: a run that starts gameplay more than once (a restart during the
|
|
39
|
+
* settle) logs the check once per start, and the newest verdict is the one about the game as it
|
|
40
|
+
* ended up.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseMobileParityGap(consoleLines: string[]): MobileParityGap | null;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the engine's mobile-parity verdict out of the console verify already captures.
|
|
3
|
+
*
|
|
4
|
+
* `GameTemplate` calls `playerController.verifyMobileParity()` on every game start — desktop runs
|
|
5
|
+
* included — and when it finds gaps the engine logs one `console.error` listing every one of
|
|
6
|
+
* them (`game/src/engine/PlayerController.ts`, documented in
|
|
7
|
+
* `game/agent-docs/control-system.md`). Nothing consumed it, so a game with actions no phone can
|
|
8
|
+
* reach passed verify silently.
|
|
9
|
+
*
|
|
10
|
+
* Parsing that line rather than listening for the `aitopia:mobile-parity-gap` postMessage the
|
|
11
|
+
* same check also fires is deliberate on two counts: the console is already captured, so this
|
|
12
|
+
* needs no new browser plumbing; and only the console line carries the `(source)` annotation
|
|
13
|
+
* naming which system registered an orphan key, which is the difference between "KeyF is
|
|
14
|
+
* unreachable" and "the attack system left KeyF unreachable".
|
|
15
|
+
*
|
|
16
|
+
* The line's format is a documented contract, but this parser still degrades rather than
|
|
17
|
+
* insisting on it: anything it cannot read comes back null, which classifies as "not observed"
|
|
18
|
+
* and never as a failure. Two legitimate ways to get null: a clean game (the engine logs nothing
|
|
19
|
+
* when parity holds) and a genre whose controller has no parity check at all — the 2D genres'
|
|
20
|
+
* `Physics2DPlayerController` deliberately has no `verifyMobileParity`, so `GameTemplate`'s
|
|
21
|
+
* duck-typed guard skips it.
|
|
22
|
+
*/
|
|
23
|
+
/** The engine's marker. Also what a reader greps the console log for. */
|
|
24
|
+
export const MOBILE_PARITY_MARKER = '[mobile-parity]';
|
|
25
|
+
/** The engine's literal for "this side has none", on either list. */
|
|
26
|
+
const NONE = '(none)';
|
|
27
|
+
const DESKTOP_LABEL = 'Desktop keys with no mobile button:';
|
|
28
|
+
const MOBILE_LABEL = 'Mobile actions with no desktop key:';
|
|
29
|
+
/**
|
|
30
|
+
* The gap the engine reported during this run, or null when it reported none.
|
|
31
|
+
*
|
|
32
|
+
* The last matching line wins: a run that starts gameplay more than once (a restart during the
|
|
33
|
+
* settle) logs the check once per start, and the newest verdict is the one about the game as it
|
|
34
|
+
* ended up.
|
|
35
|
+
*/
|
|
36
|
+
export function parseMobileParityGap(consoleLines) {
|
|
37
|
+
for (let i = consoleLines.length - 1; i >= 0; i--) {
|
|
38
|
+
const gap = parseLine(consoleLines[i]);
|
|
39
|
+
if (gap !== null)
|
|
40
|
+
return gap;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function parseLine(line) {
|
|
45
|
+
if (!line.includes(MOBILE_PARITY_MARKER))
|
|
46
|
+
return null;
|
|
47
|
+
const desktop = between(line, DESKTOP_LABEL, MOBILE_LABEL);
|
|
48
|
+
const mobile = between(line, MOBILE_LABEL, 'Use playerController');
|
|
49
|
+
if (desktop === null && mobile === null)
|
|
50
|
+
return null;
|
|
51
|
+
const gap = {
|
|
52
|
+
unpairedDesktopKeys: splitList(desktop),
|
|
53
|
+
unpairedMobileActions: splitList(mobile),
|
|
54
|
+
};
|
|
55
|
+
// Both sides empty means the line was recognised but says nothing actionable — the engine
|
|
56
|
+
// never logs that, so treat it as unobserved rather than inventing a gap with no contents.
|
|
57
|
+
return gap.unpairedDesktopKeys.length === 0 && gap.unpairedMobileActions.length === 0
|
|
58
|
+
? null
|
|
59
|
+
: gap;
|
|
60
|
+
}
|
|
61
|
+
/** The text between `label` and whatever comes next, or null when `label` is absent. */
|
|
62
|
+
function between(line, label, next) {
|
|
63
|
+
const start = line.indexOf(label);
|
|
64
|
+
if (start === -1)
|
|
65
|
+
return null;
|
|
66
|
+
const from = start + label.length;
|
|
67
|
+
const end = line.indexOf(next, from);
|
|
68
|
+
return line.slice(from, end === -1 ? undefined : end);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* One comma-separated engine list. Safe to split on a bare comma even though every desktop entry
|
|
72
|
+
* carries a parenthesised source: `source` strings are identifiers naming the registering system,
|
|
73
|
+
* never prose with commas in it.
|
|
74
|
+
*/
|
|
75
|
+
function splitList(raw) {
|
|
76
|
+
if (raw === null)
|
|
77
|
+
return [];
|
|
78
|
+
return raw
|
|
79
|
+
.replace(/\.\s*$/, '')
|
|
80
|
+
.split(',')
|
|
81
|
+
.map((entry) => entry.trim())
|
|
82
|
+
.filter((entry) => entry.length > 0 && entry !== NONE);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=mobile-parity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile-parity.js","sourceRoot":"","sources":["../../src/verify/mobile-parity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAEtD,qEAAqE;AACrE,MAAM,IAAI,GAAG,QAAQ,CAAC;AAEtB,MAAM,aAAa,GAAG,qCAAqC,CAAC;AAC5D,MAAM,YAAY,GAAG,qCAAqC,CAAC;AAa3D;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,YAAsB;IACzD,KAAK,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAClD,MAAM,GAAG,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,GAAG,CAAC;IAC/B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,sBAAsB,CAAC,CAAC;IACnE,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,GAAG,GAAG;QACV,mBAAmB,EAAE,SAAS,CAAC,OAAO,CAAC;QACvC,qBAAqB,EAAE,SAAS,CAAC,MAAM,CAAC;KACzC,CAAC;IACF,0FAA0F;IAC1F,2FAA2F;IAC3F,OAAO,GAAG,CAAC,mBAAmB,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,qBAAqB,CAAC,MAAM,KAAK,CAAC;QACnF,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,GAAG,CAAC;AACV,CAAC;AAED,wFAAwF;AACxF,SAAS,OAAO,CAAC,IAAY,EAAE,KAAa,EAAE,IAAY;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,GAAkB;IACnC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,GAAG;SACP,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { MobileOrientation, PlatformDeclaration } from '../project/platform-declaration.js';
|
|
2
|
+
export type VerifyPlatform = 'desktop' | 'mobile';
|
|
3
|
+
/** What `--platform` accepts. `both` is a request for two runs, not a platform. */
|
|
4
|
+
export type PlatformRequest = 'desktop' | 'mobile' | 'both';
|
|
5
|
+
export interface ResolvedPlatforms {
|
|
6
|
+
/**
|
|
7
|
+
* The platforms to run, in order. `platforms[0]` is the LEAD: the run whose observations land
|
|
8
|
+
* at the top level of `result.json`, which is what `bitmagic judge` summarises and what the
|
|
9
|
+
* one-line run state reports. A `both` run leads with the platform the game is FOR, so the
|
|
10
|
+
* headline numbers describe the game as it is meant to be played; the other run is in
|
|
11
|
+
* `platforms[]`. Artifact names are per platform, never per lead, so this ordering never moves
|
|
12
|
+
* a file.
|
|
13
|
+
*/
|
|
14
|
+
platforms: VerifyPlatform[];
|
|
15
|
+
/** How to orient a mobile run. Portrait unless world.json locks the game to landscape. */
|
|
16
|
+
orientation: MobileOrientation;
|
|
17
|
+
/**
|
|
18
|
+
* The project declares itself mobile-primary. This is what turns a mobile-parity gap from a
|
|
19
|
+
* warning into a failure: an action a phone cannot reach is a broken game when the phone is
|
|
20
|
+
* the platform.
|
|
21
|
+
*/
|
|
22
|
+
mobilePrimary: boolean;
|
|
23
|
+
/** One line for the creator naming what will run and why it was chosen. */
|
|
24
|
+
note: string;
|
|
25
|
+
/** Values the declaration carried that could not be used, and what was done instead. */
|
|
26
|
+
warnings: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate `--platform`, or undefined when it was not passed. Mirrors `parseRendererArg` — an
|
|
30
|
+
* unrecognised value is a mistake worth stopping for, not something to silently default.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parsePlatformArg(raw: string | undefined): PlatformRequest | undefined;
|
|
33
|
+
export declare function resolveVerifyPlatforms(requested: PlatformRequest | undefined, declaration: PlatformDeclaration): ResolvedPlatforms;
|
|
34
|
+
/**
|
|
35
|
+
* Validate `bitmagic judge --platform`. No `both`: the judge sends ONE frame and returns one
|
|
36
|
+
* scorecard, and a verdict averaged over two screens would be a verdict about neither.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseJudgePlatformArg(raw: string | undefined): VerifyPlatform | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Which frame `bitmagic judge` grades.
|
|
41
|
+
*
|
|
42
|
+
* Explicit wins. Otherwise mobile, but only when BOTH halves hold: the game says it is a phone
|
|
43
|
+
* game, and a phone frame actually exists. Requiring the file is what keeps the auto-default from
|
|
44
|
+
* becoming a dead end — a mobile-primary project whose last verify was `--platform desktop` gets
|
|
45
|
+
* its desktop frame graded rather than an error about a screenshot nobody asked for.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveJudgePlatform(explicit: VerifyPlatform | undefined, declaration: PlatformDeclaration, mobileScreenshotExists: boolean): VerifyPlatform;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which platform(s) a verify run boots, and why.
|
|
3
|
+
*
|
|
4
|
+
* Pure, so the decision is testable without a browser: `commands/verify.ts` reads the project's
|
|
5
|
+
* declaration off disk, this decides, and `browser.ts` obeys.
|
|
6
|
+
*
|
|
7
|
+
* The default is the project's own answer rather than a fixed 'desktop'. A game whose
|
|
8
|
+
* `src/work/game.json` says `primaryPlatform: "mobile"` is a phone game, and verifying it on a
|
|
9
|
+
* 1280x720 desktop viewport answers a question nobody asked — it exercises neither the touch HUD
|
|
10
|
+
* nor the mobile pixel-ratio clamp, level-detail tier or warmup path the player will actually
|
|
11
|
+
* run. An explicit `--platform` always wins, and the chosen platform is always reported, so an
|
|
12
|
+
* auto-selected mobile run is never a surprise.
|
|
13
|
+
*/
|
|
14
|
+
import { CliError } from '../errors.js';
|
|
15
|
+
/**
|
|
16
|
+
* Validate `--platform`, or undefined when it was not passed. Mirrors `parseRendererArg` — an
|
|
17
|
+
* unrecognised value is a mistake worth stopping for, not something to silently default.
|
|
18
|
+
*/
|
|
19
|
+
export function parsePlatformArg(raw) {
|
|
20
|
+
if (raw === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
if (raw === 'desktop' || raw === 'mobile' || raw === 'both')
|
|
23
|
+
return raw;
|
|
24
|
+
throw new CliError(`--platform must be "desktop", "mobile" or "both", got ${JSON.stringify(raw)}.`);
|
|
25
|
+
}
|
|
26
|
+
export function resolveVerifyPlatforms(requested, declaration) {
|
|
27
|
+
const mobilePrimary = declaration.primaryPlatform === 'mobile';
|
|
28
|
+
const warnings = [];
|
|
29
|
+
if (declaration.rejected.primaryPlatform !== undefined) {
|
|
30
|
+
warnings.push(`game.json primaryPlatform ${JSON.stringify(declaration.rejected.primaryPlatform)} is not `
|
|
31
|
+
+ '"desktop" or "mobile" — verifying as a desktop game.');
|
|
32
|
+
}
|
|
33
|
+
if (declaration.rejected.mobileOrientation !== undefined) {
|
|
34
|
+
warnings.push(`worldProfileData.mobileOrientation ${JSON.stringify(declaration.rejected.mobileOrientation)} `
|
|
35
|
+
+ 'is not "portrait" or "landscape" — verifying the mobile run in portrait.');
|
|
36
|
+
}
|
|
37
|
+
const effective = requested ?? (mobilePrimary ? 'mobile' : 'desktop');
|
|
38
|
+
const platforms = effective !== 'both'
|
|
39
|
+
? [effective]
|
|
40
|
+
: mobilePrimary ? ['mobile', 'desktop'] : ['desktop', 'mobile'];
|
|
41
|
+
const orientation = declaration.mobileOrientation ?? 'portrait';
|
|
42
|
+
return {
|
|
43
|
+
platforms,
|
|
44
|
+
orientation,
|
|
45
|
+
mobilePrimary,
|
|
46
|
+
note: describe(effective, orientation, requested !== undefined, mobilePrimary),
|
|
47
|
+
warnings,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate `bitmagic judge --platform`. No `both`: the judge sends ONE frame and returns one
|
|
52
|
+
* scorecard, and a verdict averaged over two screens would be a verdict about neither.
|
|
53
|
+
*/
|
|
54
|
+
export function parseJudgePlatformArg(raw) {
|
|
55
|
+
if (raw === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (raw === 'desktop' || raw === 'mobile')
|
|
58
|
+
return raw;
|
|
59
|
+
throw new CliError(`--platform must be "desktop" or "mobile", got ${JSON.stringify(raw)}.`
|
|
60
|
+
+ (raw === 'both' ? ' The judge grades one frame; run it once per platform.' : ''));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Which frame `bitmagic judge` grades.
|
|
64
|
+
*
|
|
65
|
+
* Explicit wins. Otherwise mobile, but only when BOTH halves hold: the game says it is a phone
|
|
66
|
+
* game, and a phone frame actually exists. Requiring the file is what keeps the auto-default from
|
|
67
|
+
* becoming a dead end — a mobile-primary project whose last verify was `--platform desktop` gets
|
|
68
|
+
* its desktop frame graded rather than an error about a screenshot nobody asked for.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveJudgePlatform(explicit, declaration, mobileScreenshotExists) {
|
|
71
|
+
if (explicit !== undefined)
|
|
72
|
+
return explicit;
|
|
73
|
+
return declaration.primaryPlatform === 'mobile' && mobileScreenshotExists ? 'mobile' : 'desktop';
|
|
74
|
+
}
|
|
75
|
+
function describe(effective, orientation, explicit, mobilePrimary) {
|
|
76
|
+
const includesMobile = effective !== 'desktop';
|
|
77
|
+
const what = effective === 'both'
|
|
78
|
+
? `desktop and mobile (${orientation})`
|
|
79
|
+
: effective === 'mobile'
|
|
80
|
+
? `mobile (${orientation})`
|
|
81
|
+
: 'desktop';
|
|
82
|
+
if (explicit)
|
|
83
|
+
return `Platform: ${what}.`;
|
|
84
|
+
return mobilePrimary && includesMobile
|
|
85
|
+
? `Platform: ${what} — game.json declares primaryPlatform "mobile". `
|
|
86
|
+
+ 'Pass --platform desktop to verify the desktop path instead.'
|
|
87
|
+
: `Platform: ${what}.`;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/verify/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/verify/result.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { RendererObservation, SettleObservation, StateSnapshot, VerifyResult } from './classify.js';
|
|
1
|
+
import type { RendererObservation, SettleObservation, StateSnapshot, TouchControlsObservation, VerifyObservations, VerifyResult } from './classify.js';
|
|
2
2
|
import type { EventsObservation } from './events.js';
|
|
3
|
+
import type { MobileParityGap } from './mobile-parity.js';
|
|
4
|
+
import type { VerifyPlatform } from './platform.js';
|
|
3
5
|
export declare const VERIFY_RESULT_FILENAME = "result.json";
|
|
4
6
|
/**
|
|
5
7
|
* What `bitmagic verify` observed, written so `bitmagic publish` can gate on it.
|
|
@@ -30,6 +32,53 @@ export interface VerifyRunRecord {
|
|
|
30
32
|
/** Semantic gameplay events (deaths, pickups, match-end) from the engine's `?eventlog=1`
|
|
31
33
|
* session; null when the engine predates it. Optional for the same cross-version reason. */
|
|
32
34
|
events?: EventsObservation | null;
|
|
35
|
+
/**
|
|
36
|
+
* Which platform the fields above describe — the LEAD platform, `platforms[0]` of the run.
|
|
37
|
+
* Absent on records written before `--platform` existed, which were all desktop.
|
|
38
|
+
*/
|
|
39
|
+
platform?: VerifyPlatform;
|
|
40
|
+
/**
|
|
41
|
+
* Touch affordances the engine rendered — the direct evidence that the mobile branch ran.
|
|
42
|
+
* Absent on a desktop lead run, where the engine never builds MobileControls at all, so
|
|
43
|
+
* absent is not the same as zero.
|
|
44
|
+
*/
|
|
45
|
+
touchControls?: TouchControlsObservation | null;
|
|
46
|
+
/**
|
|
47
|
+
* The engine's mobile-parity gaps, as it reported them at game start. Null means it reported
|
|
48
|
+
* none: a clean game, an engine too old to have the check, or a genre whose controller does
|
|
49
|
+
* not implement it. A property of the game's CODE rather than of the emulated device, so it is
|
|
50
|
+
* reported once whatever the run covered.
|
|
51
|
+
*/
|
|
52
|
+
mobileParity?: MobileParityGap | null;
|
|
53
|
+
/**
|
|
54
|
+
* One entry per platform, in run order, present ONLY on a run that covered more than one.
|
|
55
|
+
* A single-platform record keeps exactly the shape it has always had — nothing that reads
|
|
56
|
+
* these records today has a new key to learn.
|
|
57
|
+
*/
|
|
58
|
+
platforms?: VerifyPlatformRun[];
|
|
59
|
+
}
|
|
60
|
+
/** One platform's half of a multi-platform run. */
|
|
61
|
+
export interface VerifyPlatformRun {
|
|
62
|
+
platform: VerifyPlatform;
|
|
63
|
+
ok: boolean;
|
|
64
|
+
/** Unprefixed — the platform is the sibling key, so the prefixing the merged top-level
|
|
65
|
+
* `failures`/`warnings` need would only be noise here. */
|
|
66
|
+
failures: string[];
|
|
67
|
+
warnings: string[];
|
|
68
|
+
renderer?: RendererObservation;
|
|
69
|
+
retriedWithSoftwareGl?: boolean;
|
|
70
|
+
settle?: SettleObservation;
|
|
71
|
+
snapshot?: StateSnapshot | null;
|
|
72
|
+
events?: EventsObservation | null;
|
|
73
|
+
/** Touch affordances the engine rendered. Only on a mobile run. */
|
|
74
|
+
touchControls?: TouchControlsObservation | null;
|
|
75
|
+
/**
|
|
76
|
+
* File names, not paths: every artifact is a sibling of the `result.json` these appear in, and
|
|
77
|
+
* an absolute path baked into a persisted record describes the machine that wrote it rather
|
|
78
|
+
* than the run.
|
|
79
|
+
*/
|
|
80
|
+
screenshotFile?: string;
|
|
81
|
+
consoleFile: string;
|
|
33
82
|
}
|
|
34
83
|
export declare function writeVerifyRecord(artifactDir: string, record: VerifyRunRecord): void;
|
|
35
84
|
/**
|
|
@@ -50,7 +99,38 @@ export declare function readVerifyRecord(artifactDir: string): VerifyRunRecord |
|
|
|
50
99
|
* different messages and different remedies. Inside `run()` that ordering is only observable by
|
|
51
100
|
* spawning vite and a browser, which is why it went unguarded.
|
|
52
101
|
*/
|
|
53
|
-
export
|
|
102
|
+
export type VerifyRunExtras = Pick<VerifyRunRecord, 'renderer' | 'retriedWithSoftwareGl' | 'settle' | 'snapshot' | 'events' | 'platform' | 'touchControls' | 'mobileParity' | 'platforms'>;
|
|
103
|
+
/**
|
|
104
|
+
* One platform's finished run, ready to merge.
|
|
105
|
+
*
|
|
106
|
+
* Shared by the one-shot command and the watch loop because both assemble exactly this from
|
|
107
|
+
* exactly these parts, and the only interesting thing about the assembly — which observations
|
|
108
|
+
* belong to the platform and which to the run as a whole — is the thing that would drift if it
|
|
109
|
+
* were written twice.
|
|
110
|
+
*/
|
|
111
|
+
export declare function toVerifyPlatformRun(platform: VerifyPlatform, result: VerifyResult, observations: Pick<VerifyObservations, 'renderer' | 'settle' | 'snapshot' | 'events' | 'touchControls' | 'mobileParity'>, retriedWithSoftwareGl: boolean, paths: {
|
|
112
|
+
screenshotPath: string;
|
|
113
|
+
consolePath: string;
|
|
114
|
+
}, fast: boolean): VerifyPlatformRun;
|
|
115
|
+
/**
|
|
116
|
+
* One verdict, and one set of top-level fields, from however many platforms the run covered.
|
|
117
|
+
*
|
|
118
|
+
* The prefixing rule is what keeps a `both` run readable. A finding EVERY platform produced is
|
|
119
|
+
* one problem, not two — a missing `Loaded world.json` is the same missing file whichever
|
|
120
|
+
* viewport asked for it — so it is reported once, unprefixed. A finding only some platforms
|
|
121
|
+
* produced is prefixed with the platform, because there the platform IS the information. A
|
|
122
|
+
* single-platform run therefore comes out byte-identical to what it produced before this
|
|
123
|
+
* existed: every finding is in every run, so nothing is prefixed.
|
|
124
|
+
*
|
|
125
|
+
* The top-level observations are the LEAD run's (`runs[0]`), which `resolveVerifyPlatforms`
|
|
126
|
+
* chooses as the platform the game is actually for. `platforms` is only attached for a genuine
|
|
127
|
+
* multi-platform run, so a single-platform record grows no keys.
|
|
128
|
+
*/
|
|
129
|
+
export declare function mergeVerifyPlatformRuns(runs: VerifyPlatformRun[], mobileParity: MobileParityGap | null | undefined): {
|
|
130
|
+
result: VerifyResult;
|
|
131
|
+
extras: VerifyRunExtras;
|
|
132
|
+
};
|
|
133
|
+
export declare function decideVerifyOutcome(result: VerifyResult, fingerprint: string, at: string, extras?: VerifyRunExtras): {
|
|
54
134
|
record: VerifyRunRecord;
|
|
55
135
|
shouldThrow: boolean;
|
|
56
136
|
};
|
|
@@ -71,7 +151,11 @@ export interface VerifyReporter {
|
|
|
71
151
|
export declare function recordAndGateVerify(artifactDir: string, outcome: {
|
|
72
152
|
record: VerifyRunRecord;
|
|
73
153
|
shouldThrow: boolean;
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
154
|
+
},
|
|
155
|
+
/**
|
|
156
|
+
* Every file this run actually wrote, in the order to list them. A list rather than a
|
|
157
|
+
* console/screenshot pair because a run can now cover two platforms and write four files, and
|
|
158
|
+
* because a `--fast` run captures no screenshot at all — naming a file that does not exist
|
|
159
|
+
* (or worse, a stale one from an earlier run) is exactly what this avoids.
|
|
160
|
+
*/
|
|
161
|
+
artifacts: string[], reporter: VerifyReporter): void;
|