@bitmagic/cli 0.1.52-dev.10 → 0.1.52-dev.11
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/cli.d.ts +8 -0
- 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/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/judge/record.d.ts +3 -0
- package/dist/judge/record.js +3 -0
- package/dist/judge/record.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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/verify/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAA0B,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/verify/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAA0B,MAAM,aAAa,CAAC;AAyK3E;;;;;;;GAOG;AACH,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAEjC,uFAAuF;AACvF,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAElC,0DAA0D;AAC1D,MAAM,aAAa,GAAG;IACpB,sBAAsB;IACtB,sBAAsB;IACtB,wCAAwC;IACxC,iCAAiC;IACjC,4FAA4F;IAC5F,yFAAyF;IACzF,8FAA8F;IAC9F,0EAA0E;IAC1E,4FAA4F;IAC5F,oDAAoD;CACrD,CAAC;AAEF;mFACmF;AACnF,MAAM,gBAAgB,GAAG,CAAC,mBAAmB,EAAE,wBAAwB,CAAC,CAAC;AAEzE;iDACiD;AACjD,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAEpC,oDAAoD;AACpD,MAAM,uBAAuB,GAAG,IAAI,GAAG,GAAG,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,SAAS,kBAAkB,CAAC,QAAuD;IACjF,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC1E,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,MAAM,MAAM,GAAG,oBAAoB,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,uBAAuB,CAAC,CAAC;IACrG,+FAA+F;IAC/F,yEAAyE;IACzE,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAE9C;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,UAAU,GAAG,iCAAiC,CAAC;AAErD;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG,CAAC,EAAE,CAAC;AAE/B;;;GAGG;AACH,MAAM,2BAA2B,GAC/B,+EAA+E;MAC7E,+EAA+E,CAAC;AAEpF,MAAM,UAAU,iBAAiB,CAC/B,YAAgC,EAChC,UAA2B,EAAE;IAE7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAC5C,QAAQ,CAAC,IAAI,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC;IAC/B,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC7B,2FAA2F;YAC3F,uFAAuF;YACvF,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CACX,kFAAkF;sBAChF,eAAe,CAClB,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;YAClG,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,IAAI,CACX,kFAAkF,CACnF,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,CAAC,sBAAsB,EAAE,CAAC;YACxC,2FAA2F;YAC3F,2FAA2F;YAC3F,QAAQ,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC;IAClC,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QAChD,IAAI,QAAQ,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAClC,QAAQ,CAAC,IAAI,CACX,uFAAuF;kBACrF,oFAAoF;kBACpF,+DAA+D,CAClE,CAAC;QACJ,CAAC;aAAM,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACrC,QAAQ,CAAC,IAAI,CACX,qFAAqF;kBACnF,qDAAqD,CACxD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,4FAA4F;IAC5F,4FAA4F;IAC5F,gGAAgG;IAChG,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,CAAC;IACzC,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,OAAO,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;gBACnC,yFAAyF;gBACzF,2CAA2C;gBAC3C,QAAQ,CAAC,IAAI,CACX,uFAAuF;sBACrF,yCAAyC,IAAI,KAAK,2BAA2B,EAAE,CAClF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,IAAI,CACX,2EAA2E,IAAI,IAAI;sBACjF,+EAA+E;sBAC/E,2BAA2B,CAC9B,CAAC;YACJ,CAAC;QACH,CAAC;QACD,IAAI,MAAM,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,0FAA0F;YAC1F,uDAAuD;YACvD,QAAQ,CAAC,IAAI,CACX,4CAA4C,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;kBACrF,yFAAyF;kBACzF,sDAAsD,CACzD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,8FAA8F;IAC9F,4FAA4F;IAC5F,sFAAsF;IACtF,MAAM,KAAK,GAAG,YAAY,CAAC,aAAa,CAAC;IACzC,IAAI,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QAC1D,QAAQ,CAAC,IAAI,CACX,yFAAyF;cACvF,uFAAuF,CAC1F,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,QAAQ,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;IAC7F,CAAC;SAAM,CAAC;QACN,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,QAAQ,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,KAAK,sBAAsB,IAAI,MAAM,CAAC,YAAY,KAAK,uBAAuB,EAAE,CAAC;YACrG,QAAQ,CAAC,IAAI,CACX,yDAAyD,sBAAsB,GAAG;gBAChF,GAAG,uBAAuB,sCAAsC,CACnE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,kBAAkB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC3D,IAAI,YAAY,CAAC,eAAe,KAAK,IAAI,IAAI,YAAY,CAAC,eAAe,GAAG,QAAQ,EAAE,CAAC;QACrF,QAAQ,CAAC,IAAI,CACX,wCAAwC,YAAY,CAAC,eAAe,sBAAsB;YACxF,cAAc,CACjB,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,gBAAgB,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CACX,2FAA2F;cACzF,2FAA2F;cAC3F,yFAAyF,CAC5F,CAAC;IACJ,CAAC;IAED,yFAAyF;IACzF,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,CAAC;IACvC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,2EAA2E;QAC3E,MAAM,OAAO,GAAG,yEAAyE;cACrF,0DAA0D,CAAC;QAC/D,IAAI,KAAK,CAAC,WAAW,IAAI,4BAA4B,EAAE,CAAC;YACtD,QAAQ,CAAC,IAAI,CACX,uFAAuF;kBACrF,uBAAuB,OAAO,EAAE,CACnC,CAAC;QACJ,CAAC;aAAM,IAAI,KAAK,CAAC,cAAc,GAAG,mBAAmB,EAAE,CAAC;YACtD,QAAQ,CAAC,IAAI,CACX,yEAAyE;kBACvE,IAAI,KAAK,CAAC,cAAc,mBAAmB,KAAK,CAAC,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,UAAU;kBAC1F,+EAA+E,OAAO,EAAE,CAC3F,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,YAAY,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,IAAI,KAAK;YAAE,QAAQ,CAAC,IAAI,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACtF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,QAAQ,CAAC,IAAI,CACX,+EAA+E,OAAO,CAAC,CAAC,CAAC,EAAE,CAC5F,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC;IAChG,IAAI,QAAQ,GAAG,sBAAsB,EAAE,CAAC;QACtC,QAAQ,CAAC,IAAI,CACX,oCAAoC,QAAQ,kDAAkD;cAC5F,mFAAmF,CACtF,CAAC;IACJ,CAAC;SAAM,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CACX,oCAAoC,QAAQ,QAAQ,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,UAAU;cACrF,6EAA6E,CAChF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,KAAK,MAAM,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,aAAa,MAAM,sCAAsC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,8FAA8F;IAC9F,yFAAyF;IACzF,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC;IACvC,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;QAC5B,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC,GAAG,kBAAkB,EAAE,CAAC;YACzE,QAAQ,CAAC,IAAI,CACX,yDAAyD,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU;kBACrF,6EAA6E,CAChF,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;YACvB,qFAAqF;YACrF,yFAAyF;YACzF,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,EAAE,SAAS,KAAK,QAAQ;gBACxD,CAAC,CAAC,qFAAqF;sBACnF,8CAA8C;gBAClD,CAAC,CAAC,EAAE,CAAC;YACP,QAAQ,CAAC,IAAI,CACX,wFAAwF;kBACtF,8CAA8C,IAAI,EAAE,CACvD,CAAC;QACJ,CAAC;QACD,IACE,YAAY,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI;eAC3C,QAAQ,CAAC,SAAS,KAAK,IAAI;eAC3B,QAAQ,CAAC,SAAS,KAAK,SAAS;eAChC,QAAQ,CAAC,SAAS,KAAK,KAAK,EAC/B,CAAC;YACD,QAAQ,CAAC,IAAI,CACX,gDAAgD,QAAQ,CAAC,SAAS,uBAAuB;kBACvF,oCAAoC,CACvC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,2FAA2F;IAC3F,kFAAkF;IAClF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC;IACnC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,WAAW,IAAI,sBAAsB,EAAE,CAAC;YAC1C,QAAQ,CAAC,IAAI,CACX,mBAAmB,WAAW,uBAAuB,oBAAoB,cAAc;kBACrF,qDAAqD,CACxD,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,iBAAiB,KAAK,IAAI,IAAI,MAAM,CAAC,iBAAiB,GAAG,uBAAuB,EAAE,CAAC;YAC5F,QAAQ,CAAC,IAAI,CACX,mBAAmB,MAAM,CAAC,iBAAiB,2CAA2C;kBACpF,kDAAkD,CACrD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The phone `bitmagic verify --platform mobile` emulates.
|
|
3
|
+
*
|
|
4
|
+
* Zero new dependencies: `playwright-core` — already the CLI's only browser dependency — ships
|
|
5
|
+
* the device registry, and the whole descriptor goes into `newContext` as Playwright documents.
|
|
6
|
+
* What it buys, in order of how much it matters: `isMobile`, which makes Chromium honour the
|
|
7
|
+
* page's `meta viewport` tag so the engine lays out at phone width instead of a desktop
|
|
8
|
+
* fallback; `hasTouch`, without which the start interaction cannot tap; the viewport and
|
|
9
|
+
* `screen` sizes game code reads directly; and `deviceScaleFactor` 3, which is what actually
|
|
10
|
+
* exercises the engine's mobile pixel-ratio clamp (`RenderPixelRatio.ts` caps mobile at 2) — an
|
|
11
|
+
* emulation at DPR 1 would never reach it.
|
|
12
|
+
*
|
|
13
|
+
* The descriptor's iOS `userAgent` rides along deliberately rather than being stripped. The
|
|
14
|
+
* engine decides mobile-ness from `?platform=mobile` (`isMobileRuntime.ts`), so the UA is not
|
|
15
|
+
* what makes the run mobile — but plenty of game and engine code sniffs the UA directly rather
|
|
16
|
+
* than asking the engine, which is the same reason `installForcedMobileTouchShim` exists at all.
|
|
17
|
+
* A run whose UA says desktop while everything else says phone would make those sniffs disagree
|
|
18
|
+
* with the mode under test. Consistency is what is wanted here, not literal truth about which
|
|
19
|
+
* binary is running. (`defaultBrowserType: 'webkit'` is inert — only Playwright's test runner
|
|
20
|
+
* reads it; `newContext` does not.)
|
|
21
|
+
*/
|
|
22
|
+
import { devices } from 'playwright-core';
|
|
23
|
+
import type { MobileOrientation } from '../project/platform-declaration.js';
|
|
24
|
+
/** Context options for a phone run — a `playwright-core` device descriptor, verbatim. */
|
|
25
|
+
export type VerifyDeviceOptions = (typeof devices)[keyof typeof devices];
|
|
26
|
+
/**
|
|
27
|
+
* Which registry entries. A mid-size modern phone: narrow enough that a HUD laid out for a
|
|
28
|
+
* desktop viewport visibly breaks, and common enough that its dimensions are what most players
|
|
29
|
+
* actually hold.
|
|
30
|
+
*
|
|
31
|
+
* The registry's own `landscape` variant rather than swapping the portrait one's axes: those are
|
|
32
|
+
* Playwright's measured numbers for a rotated phone (750x340 viewport, screen still reported
|
|
33
|
+
* portrait), and a hand-rotated 664x390 would only be a guess at the same thing.
|
|
34
|
+
*/
|
|
35
|
+
export declare const MOBILE_DEVICE_NAMES: Record<MobileOrientation, string>;
|
|
36
|
+
/**
|
|
37
|
+
* The device a mobile check runs on, oriented the way the game asks to be played —
|
|
38
|
+
* `worldProfileData.mobileOrientation` in world.json, portrait when it says nothing. A
|
|
39
|
+
* landscape-locked game verified in a portrait window is not being verified.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mobileContextOptions(orientation: MobileOrientation): VerifyDeviceOptions;
|
|
42
|
+
/** `iPhone 14 (390x664, DPR 3)` — what the run tells the creator it emulated. */
|
|
43
|
+
export declare function describeMobileDevice(orientation: MobileOrientation): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The phone `bitmagic verify --platform mobile` emulates.
|
|
3
|
+
*
|
|
4
|
+
* Zero new dependencies: `playwright-core` — already the CLI's only browser dependency — ships
|
|
5
|
+
* the device registry, and the whole descriptor goes into `newContext` as Playwright documents.
|
|
6
|
+
* What it buys, in order of how much it matters: `isMobile`, which makes Chromium honour the
|
|
7
|
+
* page's `meta viewport` tag so the engine lays out at phone width instead of a desktop
|
|
8
|
+
* fallback; `hasTouch`, without which the start interaction cannot tap; the viewport and
|
|
9
|
+
* `screen` sizes game code reads directly; and `deviceScaleFactor` 3, which is what actually
|
|
10
|
+
* exercises the engine's mobile pixel-ratio clamp (`RenderPixelRatio.ts` caps mobile at 2) — an
|
|
11
|
+
* emulation at DPR 1 would never reach it.
|
|
12
|
+
*
|
|
13
|
+
* The descriptor's iOS `userAgent` rides along deliberately rather than being stripped. The
|
|
14
|
+
* engine decides mobile-ness from `?platform=mobile` (`isMobileRuntime.ts`), so the UA is not
|
|
15
|
+
* what makes the run mobile — but plenty of game and engine code sniffs the UA directly rather
|
|
16
|
+
* than asking the engine, which is the same reason `installForcedMobileTouchShim` exists at all.
|
|
17
|
+
* A run whose UA says desktop while everything else says phone would make those sniffs disagree
|
|
18
|
+
* with the mode under test. Consistency is what is wanted here, not literal truth about which
|
|
19
|
+
* binary is running. (`defaultBrowserType: 'webkit'` is inert — only Playwright's test runner
|
|
20
|
+
* reads it; `newContext` does not.)
|
|
21
|
+
*/
|
|
22
|
+
import { devices } from 'playwright-core';
|
|
23
|
+
import { CliError } from '../errors.js';
|
|
24
|
+
/**
|
|
25
|
+
* Which registry entries. A mid-size modern phone: narrow enough that a HUD laid out for a
|
|
26
|
+
* desktop viewport visibly breaks, and common enough that its dimensions are what most players
|
|
27
|
+
* actually hold.
|
|
28
|
+
*
|
|
29
|
+
* The registry's own `landscape` variant rather than swapping the portrait one's axes: those are
|
|
30
|
+
* Playwright's measured numbers for a rotated phone (750x340 viewport, screen still reported
|
|
31
|
+
* portrait), and a hand-rotated 664x390 would only be a guess at the same thing.
|
|
32
|
+
*/
|
|
33
|
+
export const MOBILE_DEVICE_NAMES = {
|
|
34
|
+
portrait: 'iPhone 14',
|
|
35
|
+
landscape: 'iPhone 14 landscape',
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The device a mobile check runs on, oriented the way the game asks to be played —
|
|
39
|
+
* `worldProfileData.mobileOrientation` in world.json, portrait when it says nothing. A
|
|
40
|
+
* landscape-locked game verified in a portrait window is not being verified.
|
|
41
|
+
*/
|
|
42
|
+
export function mobileContextOptions(orientation) {
|
|
43
|
+
const name = MOBILE_DEVICE_NAMES[orientation];
|
|
44
|
+
const device = devices[name];
|
|
45
|
+
if (device === undefined) {
|
|
46
|
+
// Only reachable if a playwright-core upgrade retires the entry. Say so plainly: an agent
|
|
47
|
+
// told "verification failed" would start editing game code over a missing device profile.
|
|
48
|
+
throw new CliError(`Playwright no longer ships a "${name}" device profile, so the mobile viewport cannot be `
|
|
49
|
+
+ 'emulated. This is a CLI problem, not a problem with your game — report it.');
|
|
50
|
+
}
|
|
51
|
+
return device;
|
|
52
|
+
}
|
|
53
|
+
/** `iPhone 14 (390x664, DPR 3)` — what the run tells the creator it emulated. */
|
|
54
|
+
export function describeMobileDevice(orientation) {
|
|
55
|
+
const device = mobileContextOptions(orientation);
|
|
56
|
+
const { width, height } = device.viewport;
|
|
57
|
+
return `${MOBILE_DEVICE_NAMES[orientation]} (${width}x${height}, DPR ${device.deviceScaleFactor})`;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/verify/device.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAMxC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAsC;IACpE,QAAQ,EAAE,WAAW;IACrB,SAAS,EAAE,qBAAqB;CACjC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAA8B;IACjE,MAAM,IAAI,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,0FAA0F;QAC1F,0FAA0F;QAC1F,MAAM,IAAI,QAAQ,CAChB,iCAAiC,IAAI,qDAAqD;cACtF,4EAA4E,CACjF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,oBAAoB,CAAC,WAA8B;IACjE,MAAM,MAAM,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACjD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC1C,OAAO,GAAG,mBAAmB,CAAC,WAAW,CAAC,KAAK,KAAK,IAAI,MAAM,SAAS,MAAM,CAAC,iBAAiB,GAAG,CAAC;AACrG,CAAC"}
|
package/dist/verify/iterate.d.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic verify --watch`: the warm inner loop.
|
|
3
|
+
*
|
|
4
|
+
* One-shot verify pays Chrome launch + full tsc build + vite spawn on every run — most of a
|
|
5
|
+
* minute before the game is even observed. The watch loop pays each of those once: `tsc --watch`
|
|
6
|
+
* keeps the build incremental, vite stays up (the command owns it, like one-shot), and one
|
|
7
|
+
* launched Chrome runs every iteration in a fresh browser context. What triggers an iteration is
|
|
8
|
+
* `editor/watch.ts`'s coordinator — the same debounce + compile gate `bitmagic dev` uses, so an
|
|
9
|
+
* agent's five-file edit burst becomes one re-verify, and a red build reports its type errors
|
|
10
|
+
* instead of wasting a browser run on the previous emit.
|
|
11
|
+
*/
|
|
1
12
|
import { type RendererChoice } from '../browser-gl.js';
|
|
2
13
|
import type { Output } from '../output.js';
|
|
3
14
|
import { type VerifyObservations } from './classify.js';
|
|
15
|
+
import type { VerifyPlatform } from './platform.js';
|
|
16
|
+
import type { MobileOrientation } from '../project/platform-declaration.js';
|
|
4
17
|
/**
|
|
5
18
|
* Runs verify iterations one at a time. A change that lands mid-iteration queues exactly one
|
|
6
19
|
* follow-up — not one per change, because the follow-up will see all of them anyway.
|
|
@@ -29,14 +42,20 @@ export interface VerifyWatchOptions {
|
|
|
29
42
|
/** Path to the project's `tsc` binary (for `tsc --watch`). */
|
|
30
43
|
tsc: string;
|
|
31
44
|
artifactDir: string;
|
|
32
|
-
consolePath: string;
|
|
33
|
-
screenshotPath: string;
|
|
34
45
|
/** The vite dev server's port — already up; the loop only builds URLs from it. */
|
|
35
46
|
port: number;
|
|
36
47
|
renderer: RendererChoice;
|
|
37
48
|
settleMs: number;
|
|
38
49
|
/** Skip the screenshot pipeline (default in watch mode). */
|
|
39
50
|
fast: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Which platforms each iteration covers, lead first. Two platforms roughly double an
|
|
53
|
+
* iteration: Chrome launch — the fixed cost the whole watch loop exists to amortise — is
|
|
54
|
+
* still paid once and shared, but the page load and the adaptive settle happen per platform.
|
|
55
|
+
*/
|
|
56
|
+
platforms: VerifyPlatform[];
|
|
57
|
+
orientation: MobileOrientation;
|
|
58
|
+
mobilePrimary: boolean;
|
|
40
59
|
output: Output;
|
|
41
60
|
describeRunState: (observations: VerifyObservations) => string;
|
|
42
61
|
}
|
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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"}
|