@datamoc/mw_games 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +6 -2
- package/api-surface.json +234 -0
- package/dist/assets/paths.d.ts.map +1 -1
- package/dist/assets/paths.js +3 -0
- package/dist/assets/paths.js.map +1 -1
- package/dist/audio/Playable.d.ts.map +1 -1
- package/dist/audio/Playable.js +14 -1
- package/dist/audio/Playable.js.map +1 -1
- package/dist/audio/Synth.d.ts +14 -0
- package/dist/audio/Synth.d.ts.map +1 -1
- package/dist/audio/Synth.js +16 -0
- package/dist/audio/Synth.js.map +1 -1
- package/dist/audio/index.d.ts +1 -1
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +1 -1
- package/dist/audio/index.js.map +1 -1
- package/dist/core/FixedStepper.d.ts +38 -0
- package/dist/core/FixedStepper.d.ts.map +1 -0
- package/dist/core/FixedStepper.js +56 -0
- package/dist/core/FixedStepper.js.map +1 -0
- package/dist/core/Input.d.ts +61 -0
- package/dist/core/Input.d.ts.map +1 -1
- package/dist/core/Input.js +101 -0
- package/dist/core/Input.js.map +1 -1
- package/dist/core/StoredValue.d.ts +11 -2
- package/dist/core/StoredValue.d.ts.map +1 -1
- package/dist/core/StoredValue.js +11 -2
- package/dist/core/StoredValue.js.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/index.js.map +1 -1
- package/dist/mw_games.global.js +121 -121
- package/dist/physics/PhysicsWorld.d.ts +123 -0
- package/dist/physics/PhysicsWorld.d.ts.map +1 -0
- package/dist/physics/PhysicsWorld.js +137 -0
- package/dist/physics/PhysicsWorld.js.map +1 -0
- package/dist/physics/XpbdWorld.d.ts +139 -0
- package/dist/physics/XpbdWorld.d.ts.map +1 -0
- package/dist/physics/XpbdWorld.js +207 -0
- package/dist/physics/XpbdWorld.js.map +1 -0
- package/dist/physics/index.d.ts +3 -0
- package/dist/physics/index.d.ts.map +1 -0
- package/dist/physics/index.js +2 -0
- package/dist/physics/index.js.map +1 -0
- package/dist/physics/xpbd.d.ts +3 -0
- package/dist/physics/xpbd.d.ts.map +1 -0
- package/dist/physics/xpbd.js +2 -0
- package/dist/physics/xpbd.js.map +1 -0
- package/dist/two-d/render/Camera.d.ts +6 -0
- package/dist/two-d/render/Camera.d.ts.map +1 -1
- package/dist/two-d/render/Camera.js +9 -0
- package/dist/two-d/render/Camera.js.map +1 -1
- package/dist/two-d/render/Particles.d.ts +10 -1
- package/dist/two-d/render/Particles.d.ts.map +1 -1
- package/dist/two-d/render/Particles.js +14 -7
- package/dist/two-d/render/Particles.js.map +1 -1
- package/dist/two-d/render/Shape2D.d.ts +7 -0
- package/dist/two-d/render/Shape2D.d.ts.map +1 -1
- package/dist/two-d/render/Shape2D.js +18 -0
- package/dist/two-d/render/Shape2D.js.map +1 -1
- package/dist/two-d/render/SpriteSheet.d.ts +5 -0
- package/dist/two-d/render/SpriteSheet.d.ts.map +1 -1
- package/dist/two-d/render/SpriteSheet.js +15 -0
- package/dist/two-d/render/SpriteSheet.js.map +1 -1
- package/dist/two-d/ui/Button.d.ts +7 -0
- package/dist/two-d/ui/Button.d.ts.map +1 -1
- package/dist/two-d/ui/Button.js +16 -2
- package/dist/two-d/ui/Button.js.map +1 -1
- package/dist/two-d/ui/Dropdown.d.ts +3 -0
- package/dist/two-d/ui/Dropdown.d.ts.map +1 -1
- package/dist/two-d/ui/Dropdown.js +3 -0
- package/dist/two-d/ui/Dropdown.js.map +1 -1
- package/dist/two-d/ui/DropdownView.d.ts +40 -0
- package/dist/two-d/ui/DropdownView.d.ts.map +1 -0
- package/dist/two-d/ui/DropdownView.js +75 -0
- package/dist/two-d/ui/DropdownView.js.map +1 -0
- package/dist/two-d/ui/Label.d.ts +2 -0
- package/dist/two-d/ui/Label.d.ts.map +1 -1
- package/dist/two-d/ui/Label.js +7 -0
- package/dist/two-d/ui/Label.js.map +1 -1
- package/dist/two-d/ui/Layout.d.ts +34 -0
- package/dist/two-d/ui/Layout.d.ts.map +1 -1
- package/dist/two-d/ui/Layout.js +47 -0
- package/dist/two-d/ui/Layout.js.map +1 -1
- package/dist/two-d/ui/Window.d.ts +7 -0
- package/dist/two-d/ui/Window.d.ts.map +1 -1
- package/dist/two-d/ui/Window.js +13 -0
- package/dist/two-d/ui/Window.js.map +1 -1
- package/dist/two-d/ui/index.d.ts +4 -2
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +2 -1
- package/dist/two-d/ui/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +22 -3
- package/sbom.cdx.json +64 -12
- package/tools/browser-driver.d.mts +14 -0
- package/tools/browser-driver.mjs +86 -10
- package/tools/build-tablebases.mjs +6 -2
- package/tools/classic-html.mjs +9 -1
- package/tools/compile-openings.mjs +6 -2
- package/tools/compile-resources.d.mts +5 -0
- package/tools/compile-resources.mjs +45 -4
- package/tools/compress-dist.mjs +15 -3
- package/tools/emit-page.mjs +5 -1
- package/tools/init.d.mts +5 -0
- package/tools/init.mjs +145 -0
- package/tools/play-uci.mjs +6 -3
- package/tools/sbom.mjs +5 -1
- package/tools/single-file.mjs +9 -1
- package/tools/vite.mjs +5 -0
- package/tools/webp-convert.mjs +9 -1
package/sbom.cdx.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.6",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:fc255c4f-4827-5c1a-9b7d-2851ddb9d40e",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
7
|
"tools": {
|
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
"type": "application",
|
|
11
11
|
"group": "datamoc",
|
|
12
12
|
"name": "tools/sbom.mjs",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.37.0"
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
},
|
|
17
17
|
"component": {
|
|
18
18
|
"type": "library",
|
|
19
|
-
"bom-ref": "pkg:npm/%40datamoc/mw_games@0.
|
|
19
|
+
"bom-ref": "pkg:npm/%40datamoc/mw_games@0.37.0",
|
|
20
20
|
"name": "@datamoc/mw_games",
|
|
21
|
-
"version": "0.
|
|
22
|
-
"purl": "pkg:npm/%40datamoc/mw_games@0.
|
|
21
|
+
"version": "0.37.0",
|
|
22
|
+
"purl": "pkg:npm/%40datamoc/mw_games@0.37.0",
|
|
23
23
|
"description": "A local-file game framework: renderer-free game logic, drawn through PixiJS in 2D or Babylon.js in 3D, with mobile and desktop packaging.",
|
|
24
24
|
"licenses": [
|
|
25
25
|
{
|
|
@@ -159,15 +159,15 @@
|
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
161
|
"type": "library",
|
|
162
|
-
"bom-ref": "pkg:npm/%40datamoc/pyroeis@0.
|
|
162
|
+
"bom-ref": "pkg:npm/%40datamoc/pyroeis@0.9.0",
|
|
163
163
|
"name": "@datamoc/pyroeis",
|
|
164
|
-
"version": "0.
|
|
165
|
-
"purl": "pkg:npm/%40datamoc/pyroeis@0.
|
|
164
|
+
"version": "0.9.0",
|
|
165
|
+
"purl": "pkg:npm/%40datamoc/pyroeis@0.9.0",
|
|
166
166
|
"scope": "optional",
|
|
167
167
|
"hashes": [
|
|
168
168
|
{
|
|
169
169
|
"alg": "SHA-512",
|
|
170
|
-
"content": "
|
|
170
|
+
"content": "22c2104fc4df7b87209157a3168afb143eefd46abdb5b4f92c45125244c26265badca578a81b78be2bc6c5f59c29973b5b20dc0b940143aa00016abebed6da22"
|
|
171
171
|
}
|
|
172
172
|
],
|
|
173
173
|
"licenses": [
|
|
@@ -1375,6 +1375,27 @@
|
|
|
1375
1375
|
}
|
|
1376
1376
|
]
|
|
1377
1377
|
},
|
|
1378
|
+
{
|
|
1379
|
+
"type": "library",
|
|
1380
|
+
"bom-ref": "pkg:npm/%40types/matter-js@0.20.2",
|
|
1381
|
+
"name": "@types/matter-js",
|
|
1382
|
+
"version": "0.20.2",
|
|
1383
|
+
"purl": "pkg:npm/%40types/matter-js@0.20.2",
|
|
1384
|
+
"scope": "excluded",
|
|
1385
|
+
"hashes": [
|
|
1386
|
+
{
|
|
1387
|
+
"alg": "SHA-512",
|
|
1388
|
+
"content": "dcf3cacb7431bd9f3d87dfb0741576e3cf08d492d5b3b0c4a4890fbe03bc242d6651d89548edfb648bd572d3930fb848abc3802f6809dee8064ae522a7a0e10b"
|
|
1389
|
+
}
|
|
1390
|
+
],
|
|
1391
|
+
"licenses": [
|
|
1392
|
+
{
|
|
1393
|
+
"license": {
|
|
1394
|
+
"id": "MIT"
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
]
|
|
1398
|
+
},
|
|
1378
1399
|
{
|
|
1379
1400
|
"type": "library",
|
|
1380
1401
|
"bom-ref": "pkg:npm/%40types/node@26.6.5",
|
|
@@ -3160,6 +3181,27 @@
|
|
|
3160
3181
|
}
|
|
3161
3182
|
]
|
|
3162
3183
|
},
|
|
3184
|
+
{
|
|
3185
|
+
"type": "library",
|
|
3186
|
+
"bom-ref": "pkg:npm/matter-js@0.20.0",
|
|
3187
|
+
"name": "matter-js",
|
|
3188
|
+
"version": "0.20.0",
|
|
3189
|
+
"purl": "pkg:npm/matter-js@0.20.0",
|
|
3190
|
+
"scope": "excluded",
|
|
3191
|
+
"hashes": [
|
|
3192
|
+
{
|
|
3193
|
+
"alg": "SHA-512",
|
|
3194
|
+
"content": "882f5f611ef3553dc7a69367b05b29f573a841d40ddad5327da2a0e021cb1fc6cdfa3e864f81b0ec81f6acfd2d7e501e6eb1c5c3bdf4451dc3915499457f21c0"
|
|
3195
|
+
}
|
|
3196
|
+
],
|
|
3197
|
+
"licenses": [
|
|
3198
|
+
{
|
|
3199
|
+
"license": {
|
|
3200
|
+
"id": "MIT"
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
]
|
|
3204
|
+
},
|
|
3163
3205
|
{
|
|
3164
3206
|
"type": "library",
|
|
3165
3207
|
"bom-ref": "pkg:npm/minimatch@10.2.6",
|
|
@@ -4601,7 +4643,7 @@
|
|
|
4601
4643
|
]
|
|
4602
4644
|
},
|
|
4603
4645
|
{
|
|
4604
|
-
"ref": "pkg:npm/%40datamoc/mw_games@0.
|
|
4646
|
+
"ref": "pkg:npm/%40datamoc/mw_games@0.37.0",
|
|
4605
4647
|
"dependsOn": [
|
|
4606
4648
|
"pkg:npm/%40babylonjs/core@9.30.0",
|
|
4607
4649
|
"pkg:npm/%40babylonjs/loaders@9.30.0",
|
|
@@ -4609,10 +4651,12 @@
|
|
|
4609
4651
|
"pkg:npm/%40capacitor/cli@8.5.3",
|
|
4610
4652
|
"pkg:npm/%40capacitor/core@8.5.3",
|
|
4611
4653
|
"pkg:npm/%40capacitor/ios@8.5.3",
|
|
4612
|
-
"pkg:npm/%40datamoc/pyroeis@0.
|
|
4654
|
+
"pkg:npm/%40datamoc/pyroeis@0.9.0",
|
|
4655
|
+
"pkg:npm/%40types/matter-js@0.20.2",
|
|
4613
4656
|
"pkg:npm/%40types/node@26.6.5",
|
|
4614
4657
|
"pkg:npm/%40types/ws@8.18.2",
|
|
4615
4658
|
"pkg:npm/json5@2.2.3",
|
|
4659
|
+
"pkg:npm/matter-js@0.20.0",
|
|
4616
4660
|
"pkg:npm/multithreading@0.3.52",
|
|
4617
4661
|
"pkg:npm/pixi.js@8.22.0",
|
|
4618
4662
|
"pkg:npm/playwright-core@1.64.0",
|
|
@@ -4625,7 +4669,7 @@
|
|
|
4625
4669
|
]
|
|
4626
4670
|
},
|
|
4627
4671
|
{
|
|
4628
|
-
"ref": "pkg:npm/%40datamoc/pyroeis@0.
|
|
4672
|
+
"ref": "pkg:npm/%40datamoc/pyroeis@0.9.0",
|
|
4629
4673
|
"dependsOn": [
|
|
4630
4674
|
"pkg:npm/readline-sync@1.4.10",
|
|
4631
4675
|
"pkg:npm/sprintf-js@1.1.3",
|
|
@@ -4936,6 +4980,10 @@
|
|
|
4936
4980
|
"pkg:npm/%40types/node@26.6.5"
|
|
4937
4981
|
]
|
|
4938
4982
|
},
|
|
4983
|
+
{
|
|
4984
|
+
"ref": "pkg:npm/%40types/matter-js@0.20.2",
|
|
4985
|
+
"dependsOn": []
|
|
4986
|
+
},
|
|
4939
4987
|
{
|
|
4940
4988
|
"ref": "pkg:npm/%40types/node@26.6.5",
|
|
4941
4989
|
"dependsOn": [
|
|
@@ -5337,6 +5385,10 @@
|
|
|
5337
5385
|
"ref": "pkg:npm/lru-cache@11.5.3",
|
|
5338
5386
|
"dependsOn": []
|
|
5339
5387
|
},
|
|
5388
|
+
{
|
|
5389
|
+
"ref": "pkg:npm/matter-js@0.20.0",
|
|
5390
|
+
"dependsOn": []
|
|
5391
|
+
},
|
|
5340
5392
|
{
|
|
5341
5393
|
"ref": "pkg:npm/minimatch@10.2.6",
|
|
5342
5394
|
"dependsOn": [
|
|
@@ -38,6 +38,8 @@ export interface BrowserDriver {
|
|
|
38
38
|
evaluate<T>(expression: string): Promise<T>;
|
|
39
39
|
script<Args extends unknown[], T>(fn: (...args: Args) => T | Promise<T>, ...args: Args): Promise<T>;
|
|
40
40
|
tap(xFraction: number, yFraction: number): Promise<void>;
|
|
41
|
+
/** presses at one canvas fraction, moves to another with the button held in `steps` moves (default 8), releases */
|
|
42
|
+
drag(fromX: number, fromY: number, toX: number, toY: number, steps?: number): Promise<void>;
|
|
41
43
|
screenshot(): Promise<Buffer>;
|
|
42
44
|
consoleErrors(): string[];
|
|
43
45
|
close(): Promise<void>;
|
|
@@ -77,6 +79,18 @@ export function parseBidiEndpoint(stderr: string): string | null;
|
|
|
77
79
|
/** the CDP Input.dispatchMouseEvent sequence a tap is, at CSS pixels */
|
|
78
80
|
export function buildTapSequence(x: number, y: number): CdpMouseEvent[];
|
|
79
81
|
|
|
82
|
+
/** the CDP Input.dispatchMouseEvent sequence a drag is: press, held moves, release, at CSS pixels */
|
|
83
|
+
export function buildDragSequence(
|
|
84
|
+
x1: number,
|
|
85
|
+
y1: number,
|
|
86
|
+
x2: number,
|
|
87
|
+
y2: number,
|
|
88
|
+
steps?: number,
|
|
89
|
+
): Record<string, unknown>[];
|
|
90
|
+
|
|
91
|
+
/** the in-page synthetic pointer drag, the Firefox path (see pageTapSource) */
|
|
92
|
+
export function pageDragSource(x1: number, y1: number, x2: number, y2: number, steps?: number): string;
|
|
93
|
+
|
|
80
94
|
/** the BiDi input.performActions pointer half of a tap, at CSS pixels */
|
|
81
95
|
export function buildPointerActions(x: number, y: number): BidiPointerAction[];
|
|
82
96
|
|
package/tools/browser-driver.mjs
CHANGED
|
@@ -146,6 +146,47 @@ export function buildTapSequence(x, y) {
|
|
|
146
146
|
];
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/** `steps` evenly spaced points from (x1, y1) to (x2, y2), both ends included */
|
|
150
|
+
function dragPath(x1, y1, x2, y2, steps) {
|
|
151
|
+
const count = Math.max(1, Math.floor(steps));
|
|
152
|
+
return Array.from({ length: count + 1 }, (_, i) => ({
|
|
153
|
+
x: Math.round(x1 + ((x2 - x1) * i) / count),
|
|
154
|
+
y: Math.round(y1 + ((y2 - y1) * i) / count),
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The CDP `Input.dispatchMouseEvent` sequence a drag is: press at the start, move through
|
|
160
|
+
* `steps` intermediate points with the button held, release at the end - the down, move, up a
|
|
161
|
+
* pointer-drag handler (a bridge builder, a slider, a pan) needs and `tap` cannot give.
|
|
162
|
+
*/
|
|
163
|
+
export function buildDragSequence(x1, y1, x2, y2, steps = 8) {
|
|
164
|
+
const path = dragPath(x1, y1, x2, y2, steps);
|
|
165
|
+
const last = path[path.length - 1];
|
|
166
|
+
return [
|
|
167
|
+
{ type: 'mouseMoved', x: x1, y: y1, button: 'none', buttons: 0 },
|
|
168
|
+
{ type: 'mousePressed', x: x1, y: y1, button: 'left', buttonValue: 0, clickCount: 1, buttons: 1 },
|
|
169
|
+
...path.slice(1).map((p) => ({ type: 'mouseMoved', x: p.x, y: p.y, button: 'left', buttons: 1 })),
|
|
170
|
+
{ type: 'mouseReleased', x: last.x, y: last.y, button: 'left', buttonValue: 0, clickCount: 1, buttons: 0 },
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** the in-page counterpart of `buildDragSequence` for Firefox, for the reason `pageTapSource` exists */
|
|
175
|
+
export function pageDragSource(x1, y1, x2, y2, steps = 8) {
|
|
176
|
+
const path = dragPath(x1, y1, x2, y2, steps);
|
|
177
|
+
return `(() => {
|
|
178
|
+
const canvas = document.querySelector("canvas");
|
|
179
|
+
const base = { bubbles: true, cancelable: true, composed: true, pointerId: 1, pointerType: "mouse", isPrimary: true, button: 0 };
|
|
180
|
+
const fire = (type, x, y, buttons) => canvas.dispatchEvent(new PointerEvent(type, { ...base, clientX: x, clientY: y, buttons }));
|
|
181
|
+
const path = ${JSON.stringify(path)};
|
|
182
|
+
fire("pointermove", path[0].x, path[0].y, 0);
|
|
183
|
+
fire("pointerdown", path[0].x, path[0].y, 1);
|
|
184
|
+
for (const p of path.slice(1)) fire("pointermove", p.x, p.y, 1);
|
|
185
|
+
const end = path[path.length - 1];
|
|
186
|
+
fire("pointerup", end.x, end.y, 0);
|
|
187
|
+
})()`;
|
|
188
|
+
}
|
|
189
|
+
|
|
149
190
|
/**
|
|
150
191
|
* The named keys a game binds, with everything each input protocol needs: the
|
|
151
192
|
* DOM `code`, the Windows virtual-key code CDP wants, the character the key types
|
|
@@ -355,17 +396,34 @@ export function findBrowser(kind) {
|
|
|
355
396
|
return null;
|
|
356
397
|
}
|
|
357
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Ends a browser and its helper processes. `child.kill()` reaches only the launcher: the
|
|
401
|
+
* zygote, renderers and crashpad handlers survive it holding the output pipes, which keeps
|
|
402
|
+
* the whole Node process (a test run, a CI job) alive until the runner's own timeout. On
|
|
403
|
+
* POSIX the browser is spawned as a process-group leader, so the group goes in one signal.
|
|
404
|
+
*/
|
|
405
|
+
function killBrowser(child) {
|
|
406
|
+
try {
|
|
407
|
+
if (process.platform !== 'win32' && child.pid) process.kill(-child.pid, 'SIGKILL');
|
|
408
|
+
else child.kill();
|
|
409
|
+
} catch {
|
|
410
|
+
child.kill();
|
|
411
|
+
}
|
|
412
|
+
child.stdout?.destroy();
|
|
413
|
+
child.stderr?.destroy();
|
|
414
|
+
}
|
|
415
|
+
|
|
358
416
|
/**
|
|
359
417
|
* Waits for `match` in the child's output, watching both stdout and stderr: which of the
|
|
360
418
|
* two a given browser build prints its debugging endpoint on is not something to bet on.
|
|
361
419
|
* The timeout message carries what was seen, because "no endpoint" alone cannot tell a
|
|
362
420
|
* slow cold start from a browser that started and refused to open one.
|
|
363
421
|
*/
|
|
364
|
-
function waitForEndpoint(child, match, timeoutMs =
|
|
422
|
+
function waitForEndpoint(child, match, timeoutMs = 90000) {
|
|
365
423
|
return new Promise((resolveEndpoint, reject) => {
|
|
366
424
|
let seen = '';
|
|
367
425
|
const timer = setTimeout(() => {
|
|
368
|
-
child
|
|
426
|
+
killBrowser(child);
|
|
369
427
|
reject(
|
|
370
428
|
new Error(
|
|
371
429
|
`browser printed no debugging endpoint within ${timeoutMs} ms; output so far:\n${seen || '(none)'}`,
|
|
@@ -388,6 +446,7 @@ function waitForEndpoint(child, match, timeoutMs = 30000) {
|
|
|
388
446
|
});
|
|
389
447
|
child.once('exit', () => {
|
|
390
448
|
clearTimeout(timer);
|
|
449
|
+
killBrowser(child);
|
|
391
450
|
reject(new Error(`browser exited before printing a debugging endpoint:\n${seen}`));
|
|
392
451
|
});
|
|
393
452
|
});
|
|
@@ -502,6 +561,13 @@ async function waitForReadyPage(evaluate, consoleErrors, url, timeoutMs = READY_
|
|
|
502
561
|
}
|
|
503
562
|
}
|
|
504
563
|
|
|
564
|
+
/** the canvas rectangle in CSS pixels, which canvas fractions are measured against */
|
|
565
|
+
function canvasBox(evaluate) {
|
|
566
|
+
return evaluate(
|
|
567
|
+
'(() => { const box = document.querySelector("canvas").getBoundingClientRect(); return { x: box.x, y: box.y, w: box.width, h: box.height }; })()',
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
505
571
|
/** runs a consumer function against the live page, awaiting it when it is async */
|
|
506
572
|
function runPageScript(evaluate, fn, args) {
|
|
507
573
|
return evaluate(`(${fn.toString()})(...${JSON.stringify(args)})`, true);
|
|
@@ -534,7 +600,7 @@ export async function driveChrome(options = {}) {
|
|
|
534
600
|
...(options.chromeArgs ?? extraArgs),
|
|
535
601
|
'about:blank',
|
|
536
602
|
],
|
|
537
|
-
{ stdio: ['ignore', 'pipe', 'pipe'] },
|
|
603
|
+
{ stdio: ['ignore', 'pipe', 'pipe'], detached: process.platform !== 'win32' },
|
|
538
604
|
);
|
|
539
605
|
await waitForEndpoint(child, parseDevToolsEndpoint);
|
|
540
606
|
|
|
@@ -598,6 +664,14 @@ export async function driveChrome(options = {}) {
|
|
|
598
664
|
await cdp.send('Input.dispatchMouseEvent', event);
|
|
599
665
|
}
|
|
600
666
|
},
|
|
667
|
+
/** press at one canvas fraction, move to another with the button held, release */
|
|
668
|
+
drag: async (fromX, fromY, toX, toY, steps) => {
|
|
669
|
+
const box = await canvasBox(evaluate);
|
|
670
|
+
const at = (fx, fy) => [Math.round(box.x + box.w * fx), Math.round(box.y + box.h * fy)];
|
|
671
|
+
for (const event of buildDragSequence(...at(fromX, fromY), ...at(toX, toY), steps)) {
|
|
672
|
+
await cdp.send('Input.dispatchMouseEvent', event);
|
|
673
|
+
}
|
|
674
|
+
},
|
|
601
675
|
/** presses and releases one key through Chrome's trusted input channel (P41) */
|
|
602
676
|
press: async (key, holdMs = 60) => {
|
|
603
677
|
const sequence = cdpKeySequence(key);
|
|
@@ -623,11 +697,7 @@ export async function driveChrome(options = {}) {
|
|
|
623
697
|
} catch {
|
|
624
698
|
// already closed
|
|
625
699
|
}
|
|
626
|
-
child
|
|
627
|
-
//the pipes must go: a browser's surviving helper processes can hold them (and so
|
|
628
|
-
//the whole suite) open past the main process's death
|
|
629
|
-
child.stdout?.destroy();
|
|
630
|
-
child.stderr?.destroy();
|
|
700
|
+
killBrowser(child);
|
|
631
701
|
if (ownsProfile) {
|
|
632
702
|
//a browser releases its profile files briefly after the kill, so wait for
|
|
633
703
|
//the exit and retry: a profile left behind is clutter, not a failure
|
|
@@ -676,7 +746,7 @@ export async function driveFirefox(options = {}) {
|
|
|
676
746
|
...(options.firefoxArgs ?? []),
|
|
677
747
|
'about:blank',
|
|
678
748
|
],
|
|
679
|
-
{ stdio: ['ignore', 'ignore', 'pipe'] },
|
|
749
|
+
{ stdio: ['ignore', 'ignore', 'pipe'], detached: process.platform !== 'win32' },
|
|
680
750
|
);
|
|
681
751
|
const { endpoint } = await waitForEndpoint(child, parseBidiEndpoint);
|
|
682
752
|
const socket = await openSocket(endpoint);
|
|
@@ -755,6 +825,12 @@ export async function driveFirefox(options = {}) {
|
|
|
755
825
|
//actions never reach Pixi 8's EventSystem, and this is the measured-working path.
|
|
756
826
|
await evaluate(pageTapSource(x, y), true);
|
|
757
827
|
},
|
|
828
|
+
/** press at one canvas fraction, move to another with the button held, release */
|
|
829
|
+
drag: async (fromX, fromY, toX, toY, steps) => {
|
|
830
|
+
const box = await canvasBox(evaluate);
|
|
831
|
+
const at = (fx, fy) => [Math.round(box.x + box.w * fx), Math.round(box.y + box.h * fy)];
|
|
832
|
+
await evaluate(pageDragSource(...at(fromX, fromY), ...at(toX, toY), steps), true);
|
|
833
|
+
},
|
|
758
834
|
/** presses and releases one key through WebDriver's key actions (P41) */
|
|
759
835
|
press: async (key, holdMs = 60) => {
|
|
760
836
|
const actions = bidiKeyActions(key, holdMs);
|
|
@@ -779,7 +855,7 @@ export async function driveFirefox(options = {}) {
|
|
|
779
855
|
} catch {
|
|
780
856
|
// already closed
|
|
781
857
|
}
|
|
782
|
-
child
|
|
858
|
+
killBrowser(child);
|
|
783
859
|
},
|
|
784
860
|
};
|
|
785
861
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
2
4
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join, relative
|
|
5
|
+
import { join, relative } from 'node:path';
|
|
4
6
|
import { huffEncode } from '../src/board/tablebase.ts';
|
|
5
7
|
import { spawn } from '../src/threads/spawn.ts';
|
|
6
8
|
|
|
@@ -451,7 +453,9 @@ function defaultJobs() {
|
|
|
451
453
|
return 2;
|
|
452
454
|
}
|
|
453
455
|
|
|
454
|
-
|
|
456
|
+
//the real path, since an npm `bin` shim reaches this file through a symlink; a suffix test on the
|
|
457
|
+
//name is not enough (`compress-dist.mjs` ends with `t.mjs`)
|
|
458
|
+
const isMain = process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
455
459
|
|
|
456
460
|
if (isMain) {
|
|
457
461
|
const args = process.argv.slice(2);
|
package/tools/classic-html.mjs
CHANGED
|
@@ -37,7 +37,7 @@ export function toClassicScript(html) {
|
|
|
37
37
|
return { html: html.replace(scriptTag, `<script defer src="${match[1]}"></script>`), src: match[1] };
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
async function runCli() {
|
|
41
41
|
const input = process.argv[2];
|
|
42
42
|
if (!input) {
|
|
43
43
|
console.error('Usage: node tools/classic-html.mjs dist/index.html');
|
|
@@ -59,3 +59,11 @@ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.ur
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
64
|
+
//no top-level await: a vite config loaded as CommonJS has to be able to require() this module
|
|
65
|
+
runCli().catch((error) => {
|
|
66
|
+
console.error(error);
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
2
4
|
import { readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
3
|
-
import {
|
|
5
|
+
import { join, relative } from 'node:path';
|
|
4
6
|
import { applyMove, legalMoves, positionKey, sq, squareName, startingChess } from '../src/board/chess.ts';
|
|
5
7
|
import { parseUciMove } from '../src/board/openings.ts';
|
|
6
8
|
|
|
@@ -115,7 +117,9 @@ export function compileBook(dir) {
|
|
|
115
117
|
return { version: 1, positions };
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
|
|
120
|
+
//the real path, since an npm `bin` shim reaches this file through a symlink; a suffix test on the
|
|
121
|
+
//name is not enough (`compress-dist.mjs` ends with `t.mjs`)
|
|
122
|
+
const isMain = process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
119
123
|
|
|
120
124
|
if (isMain) {
|
|
121
125
|
const [dir, out] = process.argv.slice(2);
|
|
@@ -24,6 +24,11 @@ export interface CompileResourcesResult {
|
|
|
24
24
|
embeddedBytes: number;
|
|
25
25
|
webpConverted: number;
|
|
26
26
|
skipped: number;
|
|
27
|
+
/** the `CREDITS.txt` text when the asset folder holds a `credits.json`, else null */
|
|
28
|
+
credits: string | null;
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
/** the plain-text credits for a manifest (an array, or `{ credits: [...] }`, of `{ name, license, author?, url? }`) */
|
|
32
|
+
export function creditsText(manifest: unknown): string;
|
|
33
|
+
|
|
29
34
|
export function compileResources(options: CompileResourcesOptions): Promise<CompileResourcesResult>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
1
3
|
import { readdir, readFile, mkdir, writeFile, rm } from 'node:fs/promises';
|
|
2
|
-
import { join, extname, relative, sep, dirname
|
|
4
|
+
import { join, extname, relative, sep, dirname } from 'node:path';
|
|
3
5
|
import { WEBP_CONVERTIBLE_EXTENSIONS } from './webp-convert.mjs';
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -68,6 +70,11 @@ export async function* walk(dir) {
|
|
|
68
70
|
* lossy re-encoding for photographic source art)
|
|
69
71
|
* @param options.webpQuality 0-100, used only when `webpLossless` is false (default 90)
|
|
70
72
|
* @returns the group names, in the order the page should load them
|
|
73
|
+
*
|
|
74
|
+
* An `assets/credits.json` (an array, or `{ "credits": [...] }`, of `{ name, license, author?, url? }`)
|
|
75
|
+
* is not embedded as an asset: it is turned into `CREDITS.txt` beside the compiled scripts, for
|
|
76
|
+
* the attribution that free art packs require. An entry without a `name` and a `license` is an
|
|
77
|
+
* error, since a credits file that silently omits a pack defeats its purpose.
|
|
71
78
|
*/
|
|
72
79
|
export async function compileResources({
|
|
73
80
|
from,
|
|
@@ -96,7 +103,14 @@ export async function compileResources({
|
|
|
96
103
|
// (or requires installing) sharp
|
|
97
104
|
const toWebp = convertToWebp ? (await import('./webp-convert.mjs')).toWebp : null;
|
|
98
105
|
|
|
106
|
+
const creditsFile = join(from, 'credits.json');
|
|
107
|
+
let credits = null;
|
|
108
|
+
|
|
99
109
|
for await (const file of walk(from)) {
|
|
110
|
+
if (file === creditsFile) {
|
|
111
|
+
credits = creditsText(JSON.parse(await readFile(file, 'utf8')));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
100
114
|
const ext = extname(file).toLowerCase();
|
|
101
115
|
const type = types[ext];
|
|
102
116
|
if (!type) {
|
|
@@ -136,14 +150,33 @@ export async function compileResources({
|
|
|
136
150
|
written.push({ name, count: Object.keys(assets).length, size: js.length });
|
|
137
151
|
}
|
|
138
152
|
|
|
139
|
-
|
|
153
|
+
if (credits) await writeFile(join(to, 'CREDITS.txt'), credits, 'utf8');
|
|
154
|
+
|
|
155
|
+
return { groups: written, rawBytes, embeddedBytes, webpConverted, skipped, credits };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** the plain-text credits for a manifest, one block per entry; throws on an entry that cannot be attributed */
|
|
159
|
+
export function creditsText(manifest) {
|
|
160
|
+
const entries = Array.isArray(manifest) ? manifest : manifest?.credits;
|
|
161
|
+
if (!Array.isArray(entries)) throw new Error('credits.json must be an array or { "credits": [...] }');
|
|
162
|
+
const blocks = entries.map((entry, index) => {
|
|
163
|
+
if (typeof entry?.name !== 'string' || typeof entry?.license !== 'string') {
|
|
164
|
+
throw new Error(`credits.json entry ${index} needs a "name" and a "license"`);
|
|
165
|
+
}
|
|
166
|
+
const lines = [entry.name + (entry.author ? ` by ${entry.author}` : ''), `License: ${entry.license}`];
|
|
167
|
+
if (entry.url) lines.push(entry.url);
|
|
168
|
+
return lines.join('\n');
|
|
169
|
+
});
|
|
170
|
+
return blocks.join('\n\n') + '\n';
|
|
140
171
|
}
|
|
141
172
|
|
|
142
173
|
// ---------------------------------------------------------------- CLI
|
|
143
174
|
|
|
144
|
-
|
|
175
|
+
//the real path, since an npm `bin` shim reaches this file through a symlink; a suffix test on the
|
|
176
|
+
//name is not enough (`compress-dist.mjs` ends with `t.mjs`)
|
|
177
|
+
const isMain = process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
145
178
|
|
|
146
|
-
|
|
179
|
+
async function runCli() {
|
|
147
180
|
const args = process.argv.slice(2);
|
|
148
181
|
const [from, to] = args.filter((a) => !a.startsWith('--'));
|
|
149
182
|
if (!from || !to) {
|
|
@@ -180,4 +213,12 @@ if (isMain) {
|
|
|
180
213
|
}
|
|
181
214
|
}
|
|
182
215
|
|
|
216
|
+
//no top-level await: a vite config loaded as CommonJS has to be able to require() this module
|
|
217
|
+
if (isMain) {
|
|
218
|
+
runCli().catch((error) => {
|
|
219
|
+
console.error(error);
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
183
224
|
export { dirname };
|
package/tools/compress-dist.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
1
3
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { relative, extname
|
|
4
|
+
import { relative, extname } from 'node:path';
|
|
3
5
|
import { gzip, brotliCompress, constants } from 'node:zlib';
|
|
4
6
|
import { promisify } from 'node:util';
|
|
5
7
|
import { availableParallelism } from 'node:os';
|
|
@@ -153,9 +155,11 @@ async function compressOne(dir, file, { gzip, brotli, wantXz }) {
|
|
|
153
155
|
return row;
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
//the real path, since an npm `bin` shim reaches this file through a symlink; a suffix test on the
|
|
159
|
+
//name is not enough (`compress-dist.mjs` ends with `t.mjs`)
|
|
160
|
+
const isMain = process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
157
161
|
|
|
158
|
-
|
|
162
|
+
async function runCli() {
|
|
159
163
|
const [dir, ...flags] = process.argv.slice(2);
|
|
160
164
|
if (!dir) {
|
|
161
165
|
console.error('usage: node tools/compress-dist.mjs <dist folder> [--xz] [--no-gzip] [--no-brotli]');
|
|
@@ -179,3 +183,11 @@ if (isMain) {
|
|
|
179
183
|
}
|
|
180
184
|
console.log(` ${rows.length} file(s) considered in ${relative(process.cwd(), dir)}`);
|
|
181
185
|
}
|
|
186
|
+
|
|
187
|
+
//no top-level await: a vite config loaded as CommonJS has to be able to require() this module
|
|
188
|
+
if (isMain) {
|
|
189
|
+
runCli().catch((error) => {
|
|
190
|
+
console.error(error);
|
|
191
|
+
process.exitCode = 1;
|
|
192
|
+
});
|
|
193
|
+
}
|
package/tools/emit-page.mjs
CHANGED
|
@@ -184,5 +184,9 @@ async function main(argv) {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
//realpath, since an npm `bin` shim reaches this file through a symlink
|
|
187
|
+
//no top-level await: a vite config loaded as CommonJS has to be able to require() this module
|
|
187
188
|
if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url))
|
|
188
|
-
|
|
189
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
190
|
+
console.error(error);
|
|
191
|
+
process.exitCode = 1;
|
|
192
|
+
});
|
package/tools/init.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** the starter project's files as `{ path: content }`, for a package called `name` */
|
|
2
|
+
export function initFiles(name: string): Record<string, string>;
|
|
3
|
+
|
|
4
|
+
/** writes the starter project into an empty or missing `folder`; throws if it already holds files */
|
|
5
|
+
export function initProject(folder: string): { folder: string; files: string[] };
|