@datamoc/mw_games 0.37.0 → 0.39.1
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 +79 -0
- package/README.md +26 -0
- package/api-surface.json +650 -2
- package/dist/assets/SvgParts.d.ts +31 -0
- package/dist/assets/SvgParts.d.ts.map +1 -0
- package/dist/assets/SvgParts.js +54 -0
- package/dist/assets/SvgParts.js.map +1 -0
- package/dist/assets/SvgSource.d.ts +3 -0
- package/dist/assets/SvgSource.d.ts.map +1 -0
- package/dist/assets/SvgSource.js +22 -0
- package/dist/assets/SvgSource.js.map +1 -0
- package/dist/assets/index.d.ts +2 -0
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +1 -0
- package/dist/assets/index.js.map +1 -1
- package/dist/assets/svg.d.ts.map +1 -1
- package/dist/assets/svg.js +9 -5
- package/dist/assets/svg.js.map +1 -1
- package/dist/mw_games.global.js +110 -110
- package/dist/mwl/presentation.d.ts +70 -0
- package/dist/mwl/presentation.d.ts.map +1 -0
- package/dist/mwl/presentation.js +419 -0
- package/dist/mwl/presentation.js.map +1 -0
- package/dist/physics/PhysicsWorld.d.ts +49 -17
- package/dist/physics/PhysicsWorld.d.ts.map +1 -1
- package/dist/physics/PhysicsWorld.js +297 -59
- package/dist/physics/PhysicsWorld.js.map +1 -1
- package/dist/physics/XpbdWorld.d.ts +1 -1
- package/dist/physics/XpbdWorld.js +1 -1
- package/dist/physics/cloth/index.d.ts +98 -0
- package/dist/physics/cloth/index.d.ts.map +1 -0
- package/dist/physics/cloth/index.js +207 -0
- package/dist/physics/cloth/index.js.map +1 -0
- package/dist/physics/index.d.ts +4 -0
- package/dist/physics/index.d.ts.map +1 -1
- package/dist/physics/index.js +2 -0
- package/dist/physics/index.js.map +1 -1
- package/dist/physics/spring/index.d.ts +90 -0
- package/dist/physics/spring/index.d.ts.map +1 -0
- package/dist/physics/spring/index.js +97 -0
- package/dist/physics/spring/index.js.map +1 -0
- package/dist/skeleton/index.d.ts +94 -13
- package/dist/skeleton/index.d.ts.map +1 -1
- package/dist/skeleton/index.js +192 -73
- package/dist/skeleton/index.js.map +1 -1
- package/dist/two-d/presentation/MwlPresentation.d.ts +110 -0
- package/dist/two-d/presentation/MwlPresentation.d.ts.map +1 -0
- package/dist/two-d/presentation/MwlPresentation.js +755 -0
- package/dist/two-d/presentation/MwlPresentation.js.map +1 -0
- package/dist/two-d/presentation/index.d.ts +3 -0
- package/dist/two-d/presentation/index.d.ts.map +1 -0
- package/dist/two-d/presentation/index.js +2 -0
- package/dist/two-d/presentation/index.js.map +1 -0
- package/dist/two-d/render/ClothSprite.d.ts +83 -0
- package/dist/two-d/render/ClothSprite.d.ts.map +1 -0
- package/dist/two-d/render/ClothSprite.js +105 -0
- package/dist/two-d/render/ClothSprite.js.map +1 -0
- package/dist/two-d/render/HazeLayer.d.ts +112 -0
- package/dist/two-d/render/HazeLayer.d.ts.map +1 -0
- package/dist/two-d/render/HazeLayer.js +185 -0
- package/dist/two-d/render/HazeLayer.js.map +1 -0
- package/dist/two-d/render/NeonGlow.d.ts +100 -0
- package/dist/two-d/render/NeonGlow.d.ts.map +1 -0
- package/dist/two-d/render/NeonGlow.js +183 -0
- package/dist/two-d/render/NeonGlow.js.map +1 -0
- package/dist/two-d/render/SkeletalSprite.d.ts +5 -2
- package/dist/two-d/render/SkeletalSprite.d.ts.map +1 -1
- package/dist/two-d/render/SkeletalSprite.js +27 -3
- package/dist/two-d/render/SkeletalSprite.js.map +1 -1
- package/dist/two-d/render/SpringSprite.d.ts +40 -0
- package/dist/two-d/render/SpringSprite.d.ts.map +1 -0
- package/dist/two-d/render/SpringSprite.js +39 -0
- package/dist/two-d/render/SpringSprite.js.map +1 -0
- package/dist/two-d/render/SpriteDeform.d.ts +156 -0
- package/dist/two-d/render/SpriteDeform.d.ts.map +1 -0
- package/dist/two-d/render/SpriteDeform.js +278 -0
- package/dist/two-d/render/SpriteDeform.js.map +1 -0
- package/dist/two-d/render/SvgSkeleton.d.ts +54 -0
- package/dist/two-d/render/SvgSkeleton.d.ts.map +1 -0
- package/dist/two-d/render/SvgSkeleton.js +112 -0
- package/dist/two-d/render/SvgSkeleton.js.map +1 -0
- package/dist/two-d/render/index.d.ts +12 -0
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +6 -0
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/two-d/ui/Bar.d.ts +3 -1
- package/dist/two-d/ui/Bar.d.ts.map +1 -1
- package/dist/two-d/ui/Bar.js +5 -0
- package/dist/two-d/ui/Bar.js.map +1 -1
- package/dist/two-d/ui/Button.d.ts +12 -0
- package/dist/two-d/ui/Button.d.ts.map +1 -1
- package/dist/two-d/ui/Button.js +16 -4
- package/dist/two-d/ui/Button.js.map +1 -1
- package/dist/two-d/ui/Window.d.ts +10 -0
- package/dist/two-d/ui/Window.d.ts.map +1 -1
- package/dist/two-d/ui/Window.js +17 -6
- package/dist/two-d/ui/Window.js.map +1 -1
- package/dist/two-d/ui/index.d.ts +2 -0
- package/dist/two-d/ui/index.d.ts.map +1 -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 +24 -7
- package/sbom.cdx.json +6 -58
- package/tools/asset-report.d.mts +37 -0
- package/tools/asset-report.mjs +157 -0
- package/tools/compile-resources.d.mts +2 -0
- package/tools/compile-resources.mjs +23 -2
- package/tools/emit-page.d.mts +2 -0
- package/tools/emit-page.mjs +11 -1
- package/tools/verify-scenarios.d.mts +15 -0
- package/tools/verify-scenarios.mjs +129 -0
- package/tools/vite.mjs +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datamoc/mw_games",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"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.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"bin": {
|
|
37
37
|
"mwg-bench": "tools/benchmark-browser.mjs",
|
|
38
|
+
"mwg-assets": "tools/asset-report.mjs",
|
|
38
39
|
"mwg-audit-adoptions": "tools/audit-adoptions.mjs",
|
|
39
40
|
"mwg-emit": "tools/emit-page.mjs",
|
|
40
41
|
"mwg-extract-rgssad": "tools/extract-rgssad.mjs",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"mwg-openings": "tools/compile-openings.mjs",
|
|
46
47
|
"mwg-placeholder-assets": "tools/make-example-assets.mjs",
|
|
47
48
|
"mwg-sbom": "tools/sbom.mjs",
|
|
49
|
+
"mwg-scenarios": "tools/verify-scenarios.mjs",
|
|
48
50
|
"mwg-size": "tools/bundle-size.mjs",
|
|
49
51
|
"mwg-smoke": "tools/browser-smoke.mjs",
|
|
50
52
|
"mwg-tablebases": "tools/build-tablebases.mjs",
|
|
@@ -57,6 +59,8 @@
|
|
|
57
59
|
"dist",
|
|
58
60
|
"tools/compile-resources.mjs",
|
|
59
61
|
"tools/compile-resources.d.mts",
|
|
62
|
+
"tools/asset-report.mjs",
|
|
63
|
+
"tools/asset-report.d.mts",
|
|
60
64
|
"tools/webp-convert.mjs",
|
|
61
65
|
"tools/webp-convert.d.mts",
|
|
62
66
|
"tools/extract-html.mjs",
|
|
@@ -71,6 +75,8 @@
|
|
|
71
75
|
"tools/play-uci.d.mts",
|
|
72
76
|
"tools/init.mjs",
|
|
73
77
|
"tools/init.d.mts",
|
|
78
|
+
"tools/verify-scenarios.mjs",
|
|
79
|
+
"tools/verify-scenarios.d.mts",
|
|
74
80
|
"tools/classic-html.mjs",
|
|
75
81
|
"tools/classic-html.d.mts",
|
|
76
82
|
"tools/sbom.mjs",
|
|
@@ -116,6 +122,14 @@
|
|
|
116
122
|
"MWG - Best Practices.md"
|
|
117
123
|
],
|
|
118
124
|
"exports": {
|
|
125
|
+
"./mwl/presentation": {
|
|
126
|
+
"types": "./dist/mwl/presentation.d.ts",
|
|
127
|
+
"import": "./dist/mwl/presentation.js"
|
|
128
|
+
},
|
|
129
|
+
"./two-d/presentation": {
|
|
130
|
+
"types": "./dist/two-d/presentation/index.d.ts",
|
|
131
|
+
"import": "./dist/two-d/presentation/index.js"
|
|
132
|
+
},
|
|
119
133
|
".": {
|
|
120
134
|
"types": "./dist/index.d.ts",
|
|
121
135
|
"import": "./dist/index.js"
|
|
@@ -214,6 +228,7 @@
|
|
|
214
228
|
},
|
|
215
229
|
"./tools/benchmark-browser": "./tools/benchmark-browser.mjs",
|
|
216
230
|
"./tools/audit-adoptions": "./tools/audit-adoptions.mjs",
|
|
231
|
+
"./tools/asset-report": "./tools/asset-report.mjs",
|
|
217
232
|
"./tools/browser-driver": "./tools/browser-driver.mjs",
|
|
218
233
|
"./tools/benchmark-history": "./tools/benchmark-history.mjs",
|
|
219
234
|
"./tools/browser-smoke": "./tools/browser-smoke.mjs",
|
|
@@ -223,6 +238,7 @@
|
|
|
223
238
|
"./tools/compile-openings": "./tools/compile-openings.mjs",
|
|
224
239
|
"./tools/build-tablebases": "./tools/build-tablebases.mjs",
|
|
225
240
|
"./tools/init": "./tools/init.mjs",
|
|
241
|
+
"./tools/verify-scenarios": "./tools/verify-scenarios.mjs",
|
|
226
242
|
"./tools/play-uci": "./tools/play-uci.mjs",
|
|
227
243
|
"./tools/compress-dist": "./tools/compress-dist.mjs",
|
|
228
244
|
"./tools/csp": "./tools/csp.mjs",
|
|
@@ -298,6 +314,9 @@
|
|
|
298
314
|
"i18n:edit": "node tools/i18n-edit.mjs",
|
|
299
315
|
"i18n:check": "node tools/i18n-edit.mjs --check",
|
|
300
316
|
"example:ui": "vite --config examples/interface/vite.config.ts",
|
|
317
|
+
"example:mwl-presentation": "npm run build:pkg && node examples/mwl-presentation/generate.mjs && vite --config examples/mwl-presentation/vite.config.ts",
|
|
318
|
+
"example:mwl-presentation:build": "npm run build:pkg && node examples/mwl-presentation/generate.mjs && vite build --config examples/mwl-presentation/vite.config.ts && node tools/emit-page.mjs examples/mwl-presentation --assets=examples/mwl-presentation/assets",
|
|
319
|
+
"visual:smoke:presentation": "npm run example:mwl-presentation:build && node tools/presentation-smoke.mjs",
|
|
301
320
|
"example:ui:build": "vite build --config examples/interface/vite.config.ts && node tools/emit-page.mjs examples/interface --assets=examples/assets",
|
|
302
321
|
"example:dialogue": "vite --config examples/dialogue/vite.config.ts",
|
|
303
322
|
"example:dialogue:build": "vite build --config examples/dialogue/vite.config.ts && node tools/emit-page.mjs examples/dialogue --assets=examples/assets",
|
|
@@ -313,6 +332,10 @@
|
|
|
313
332
|
"example:multi-turn-beam:build": "vite build --config examples/multi-turn-beam/vite.config.ts && node tools/emit-page.mjs examples/multi-turn-beam --assets=examples/assets",
|
|
314
333
|
"example:chess": "vite --config examples/chess/vite.config.ts",
|
|
315
334
|
"example:chess:build": "vite build --config examples/chess/vite.config.ts && node tools/emit-page.mjs examples/chess --assets=examples/assets",
|
|
335
|
+
"example:cloth": "vite --config examples/cloth/vite.config.ts",
|
|
336
|
+
"example:cloth:build": "vite build --config examples/cloth/vite.config.ts && node tools/emit-page.mjs examples/cloth --assets=examples/assets",
|
|
337
|
+
"example:spring": "vite --config examples/spring/vite.config.ts",
|
|
338
|
+
"example:spring:build": "vite build --config examples/spring/vite.config.ts && node tools/emit-page.mjs examples/spring --assets=examples/assets",
|
|
316
339
|
"example:tower-defense": "vite --config examples/tower-defense/vite.config.ts",
|
|
317
340
|
"example:tower-defense:build": "vite build --config examples/tower-defense/vite.config.ts && node tools/emit-page.mjs examples/tower-defense --assets=examples/assets",
|
|
318
341
|
"example:3d": "vite --config examples/three-d/vite.config.ts",
|
|
@@ -384,7 +407,6 @@
|
|
|
384
407
|
"@babylonjs/core": "^9.25.0",
|
|
385
408
|
"@babylonjs/loaders": "^9.25.0",
|
|
386
409
|
"@capacitor/core": "^8.5.2",
|
|
387
|
-
"matter-js": "^0.20.0",
|
|
388
410
|
"pixi.js": "^8.20.1",
|
|
389
411
|
"playwright-core": "^1.62.1",
|
|
390
412
|
"ws": "^8.21.3"
|
|
@@ -407,9 +429,6 @@
|
|
|
407
429
|
},
|
|
408
430
|
"ws": {
|
|
409
431
|
"optional": true
|
|
410
|
-
},
|
|
411
|
-
"matter-js": {
|
|
412
|
-
"optional": true
|
|
413
432
|
}
|
|
414
433
|
},
|
|
415
434
|
"devDependencies": {
|
|
@@ -419,10 +438,8 @@
|
|
|
419
438
|
"@capacitor/cli": "^8.5.2",
|
|
420
439
|
"@capacitor/core": "^8.5.2",
|
|
421
440
|
"@capacitor/ios": "^8.5.2",
|
|
422
|
-
"@types/matter-js": "^0.20.2",
|
|
423
441
|
"@types/node": "^26.4.1",
|
|
424
442
|
"@types/ws": "^8.18.1",
|
|
425
|
-
"matter-js": "^0.20.0",
|
|
426
443
|
"multithreading": "^0.3.52",
|
|
427
444
|
"pixi.js": "^8.20.1",
|
|
428
445
|
"playwright-core": "^1.62.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:87d5da55-61f5-526f-9055-cdcdd6680517",
|
|
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.39.1"
|
|
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.39.1",
|
|
20
20
|
"name": "@datamoc/mw_games",
|
|
21
|
-
"version": "0.
|
|
22
|
-
"purl": "pkg:npm/%40datamoc/mw_games@0.
|
|
21
|
+
"version": "0.39.1",
|
|
22
|
+
"purl": "pkg:npm/%40datamoc/mw_games@0.39.1",
|
|
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
|
{
|
|
@@ -1375,27 +1375,6 @@
|
|
|
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
|
-
},
|
|
1399
1378
|
{
|
|
1400
1379
|
"type": "library",
|
|
1401
1380
|
"bom-ref": "pkg:npm/%40types/node@26.6.5",
|
|
@@ -3181,27 +3160,6 @@
|
|
|
3181
3160
|
}
|
|
3182
3161
|
]
|
|
3183
3162
|
},
|
|
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
|
-
},
|
|
3205
3163
|
{
|
|
3206
3164
|
"type": "library",
|
|
3207
3165
|
"bom-ref": "pkg:npm/minimatch@10.2.6",
|
|
@@ -4643,7 +4601,7 @@
|
|
|
4643
4601
|
]
|
|
4644
4602
|
},
|
|
4645
4603
|
{
|
|
4646
|
-
"ref": "pkg:npm/%40datamoc/mw_games@0.
|
|
4604
|
+
"ref": "pkg:npm/%40datamoc/mw_games@0.39.1",
|
|
4647
4605
|
"dependsOn": [
|
|
4648
4606
|
"pkg:npm/%40babylonjs/core@9.30.0",
|
|
4649
4607
|
"pkg:npm/%40babylonjs/loaders@9.30.0",
|
|
@@ -4652,11 +4610,9 @@
|
|
|
4652
4610
|
"pkg:npm/%40capacitor/core@8.5.3",
|
|
4653
4611
|
"pkg:npm/%40capacitor/ios@8.5.3",
|
|
4654
4612
|
"pkg:npm/%40datamoc/pyroeis@0.9.0",
|
|
4655
|
-
"pkg:npm/%40types/matter-js@0.20.2",
|
|
4656
4613
|
"pkg:npm/%40types/node@26.6.5",
|
|
4657
4614
|
"pkg:npm/%40types/ws@8.18.2",
|
|
4658
4615
|
"pkg:npm/json5@2.2.3",
|
|
4659
|
-
"pkg:npm/matter-js@0.20.0",
|
|
4660
4616
|
"pkg:npm/multithreading@0.3.52",
|
|
4661
4617
|
"pkg:npm/pixi.js@8.22.0",
|
|
4662
4618
|
"pkg:npm/playwright-core@1.64.0",
|
|
@@ -4980,10 +4936,6 @@
|
|
|
4980
4936
|
"pkg:npm/%40types/node@26.6.5"
|
|
4981
4937
|
]
|
|
4982
4938
|
},
|
|
4983
|
-
{
|
|
4984
|
-
"ref": "pkg:npm/%40types/matter-js@0.20.2",
|
|
4985
|
-
"dependsOn": []
|
|
4986
|
-
},
|
|
4987
4939
|
{
|
|
4988
4940
|
"ref": "pkg:npm/%40types/node@26.6.5",
|
|
4989
4941
|
"dependsOn": [
|
|
@@ -5385,10 +5337,6 @@
|
|
|
5385
5337
|
"ref": "pkg:npm/lru-cache@11.5.3",
|
|
5386
5338
|
"dependsOn": []
|
|
5387
5339
|
},
|
|
5388
|
-
{
|
|
5389
|
-
"ref": "pkg:npm/matter-js@0.20.0",
|
|
5390
|
-
"dependsOn": []
|
|
5391
|
-
},
|
|
5392
5340
|
{
|
|
5393
5341
|
"ref": "pkg:npm/minimatch@10.2.6",
|
|
5394
5342
|
"dependsOn": [
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface AssetReportFile {
|
|
2
|
+
path: string;
|
|
3
|
+
raw: number;
|
|
4
|
+
gzip: number;
|
|
5
|
+
brotli: number;
|
|
6
|
+
sha256: string;
|
|
7
|
+
/** This file's gzip bytes over the page's total gzip bytes, 0 when the page is empty. */
|
|
8
|
+
share: number;
|
|
9
|
+
}
|
|
10
|
+
export interface AssetReportSource {
|
|
11
|
+
path: string;
|
|
12
|
+
raw: number;
|
|
13
|
+
sha256: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AssetReportTotals {
|
|
16
|
+
files: number;
|
|
17
|
+
raw: number;
|
|
18
|
+
gzip: number;
|
|
19
|
+
brotli: number;
|
|
20
|
+
assetFiles: number;
|
|
21
|
+
assetRaw: number;
|
|
22
|
+
}
|
|
23
|
+
export interface AssetReport {
|
|
24
|
+
dist: string;
|
|
25
|
+
files: AssetReportFile[];
|
|
26
|
+
assets: AssetReportSource[];
|
|
27
|
+
/** Groups of source paths with byte-identical content, sorted; empty when none exist. */
|
|
28
|
+
duplicates: string[][];
|
|
29
|
+
totals: AssetReportTotals;
|
|
30
|
+
}
|
|
31
|
+
export function fingerprint(data: Uint8Array): string;
|
|
32
|
+
export function compressedSizes(data: Uint8Array): { gzip: number; brotli: number };
|
|
33
|
+
export function assetReport(options: {
|
|
34
|
+
dist: string;
|
|
35
|
+
assets?: string;
|
|
36
|
+
mime?: Record<string, string>;
|
|
37
|
+
}): Promise<AssetReport>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { gzipSync, brotliCompressSync, constants } from 'node:zlib';
|
|
4
|
+
import { realpathSync, statSync } from 'node:fs';
|
|
5
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { join, relative, sep, extname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { walk, DEFAULT_MIME } from './compile-resources.mjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Reports what a built page actually ships, so a game can see where its bytes go.
|
|
12
|
+
*
|
|
13
|
+
* Two halves, both deterministic (sorted paths, fixed compression settings, hashes):
|
|
14
|
+
*
|
|
15
|
+
* - `files`: every file under `dist` (precompressed `.gz`/`.br`/`.xz` siblings
|
|
16
|
+
* excluded - they are derived artifacts, and the gzip/brotli columns already count
|
|
17
|
+
* them) with raw, gzip (level 9) and brotli (quality 11) weights plus its share of
|
|
18
|
+
* the compressed page, the same settings `tools/compress-dist.mjs` writes with;
|
|
19
|
+
* - `assets`: every source file the asset folder embeds (the root `credits.json`
|
|
20
|
+
* and unknown extensions excluded, exactly the files `compileResources` skips),
|
|
21
|
+
* with duplicates grouped by SHA-256 so an asset embedded twice is visible.
|
|
22
|
+
*
|
|
23
|
+
* Shipped as `@datamoc/mw_games/tools/asset-report` (`assetReport(options)`, what the
|
|
24
|
+
* flags below map onto) and as the `mwg-assets` command.
|
|
25
|
+
*
|
|
26
|
+
* usage: mwg-assets <dist folder> [--assets=<dir>] [--json=<report file>]
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const DERIVED_SIBLINGS = new Set(['.gz', '.br', '.xz']);
|
|
30
|
+
|
|
31
|
+
export function fingerprint(data) {
|
|
32
|
+
return createHash('sha256').update(data).digest('hex');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function compressedSizes(data) {
|
|
36
|
+
return {
|
|
37
|
+
gzip: gzipSync(data, { level: 9 }).length,
|
|
38
|
+
brotli: brotliCompressSync(data, { params: { [constants.BROTLI_PARAM_QUALITY]: 11 } }).length,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isDirectory(path) {
|
|
43
|
+
try {
|
|
44
|
+
return statSync(path).isDirectory();
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param options.dist the built page folder to measure
|
|
52
|
+
* @param options.assets the asset source folder to inventory (skipped when absent)
|
|
53
|
+
* @param options.mime extension to MIME type, merged over the compile defaults, so a
|
|
54
|
+
* game embedding extra types reports the same set the build embeds
|
|
55
|
+
* @returns the sorted files, sources, duplicate groups and totals described above
|
|
56
|
+
*/
|
|
57
|
+
export async function assetReport({ dist, assets, mime = {} } = {}) {
|
|
58
|
+
if (!dist || !isDirectory(dist))
|
|
59
|
+
throw new Error(`assetReport needs the dist folder of a built page, got "${dist}"`);
|
|
60
|
+
if (assets !== undefined && !isDirectory(assets))
|
|
61
|
+
throw new Error(`assetReport needs a readable assets folder, got "${assets}"`);
|
|
62
|
+
|
|
63
|
+
const files = [];
|
|
64
|
+
for await (const file of walk(dist)) {
|
|
65
|
+
if (DERIVED_SIBLINGS.has(extname(file).toLowerCase())) continue;
|
|
66
|
+
const data = await readFile(file);
|
|
67
|
+
const { gzip, brotli } = compressedSizes(data);
|
|
68
|
+
files.push({
|
|
69
|
+
path: relative(dist, file).split(sep).join('/'),
|
|
70
|
+
raw: data.length,
|
|
71
|
+
gzip,
|
|
72
|
+
brotli,
|
|
73
|
+
sha256: fingerprint(data),
|
|
74
|
+
share: 0,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
files.sort((a, b) => (a.path < b.path ? -1 : 1));
|
|
78
|
+
const totalGzip = files.reduce((sum, file) => sum + file.gzip, 0);
|
|
79
|
+
for (const file of files) file.share = totalGzip ? file.gzip / totalGzip : 0;
|
|
80
|
+
|
|
81
|
+
const sources = [];
|
|
82
|
+
const duplicates = [];
|
|
83
|
+
if (assets !== undefined) {
|
|
84
|
+
const types = { ...DEFAULT_MIME, ...mime };
|
|
85
|
+
const creditsFile = join(assets, 'credits.json');
|
|
86
|
+
const byHash = new Map();
|
|
87
|
+
for await (const file of walk(assets)) {
|
|
88
|
+
if (file === creditsFile) continue;
|
|
89
|
+
if (!types[extname(file).toLowerCase()]) continue;
|
|
90
|
+
const data = await readFile(file);
|
|
91
|
+
const sha256 = fingerprint(data);
|
|
92
|
+
const key = relative(assets, file).split(sep).join('/');
|
|
93
|
+
sources.push({ path: key, raw: data.length, sha256 });
|
|
94
|
+
const group = byHash.get(sha256) ?? [];
|
|
95
|
+
group.push(key);
|
|
96
|
+
byHash.set(sha256, group);
|
|
97
|
+
}
|
|
98
|
+
sources.sort((a, b) => (a.path < b.path ? -1 : 1));
|
|
99
|
+
for (const group of byHash.values()) if (group.length > 1) duplicates.push([...group].sort());
|
|
100
|
+
duplicates.sort((a, b) => (a[0] < b[0] ? -1 : 1));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
dist,
|
|
105
|
+
files,
|
|
106
|
+
assets: sources,
|
|
107
|
+
duplicates,
|
|
108
|
+
totals: {
|
|
109
|
+
files: files.length,
|
|
110
|
+
raw: files.reduce((sum, file) => sum + file.raw, 0),
|
|
111
|
+
gzip: totalGzip,
|
|
112
|
+
brotli: files.reduce((sum, file) => sum + file.brotli, 0),
|
|
113
|
+
assetFiles: sources.length,
|
|
114
|
+
assetRaw: sources.reduce((sum, source) => sum + source.raw, 0),
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const kb = (n) => (n / 1024).toFixed(1) + ' KB';
|
|
120
|
+
const percent = (share) => (share * 100).toFixed(1) + '%';
|
|
121
|
+
|
|
122
|
+
async function main(argv) {
|
|
123
|
+
const folder = argv.find((arg) => !arg.startsWith('--'));
|
|
124
|
+
if (!folder) {
|
|
125
|
+
console.error('usage: mwg-assets <dist folder> [--assets=<dir>] [--json=<report file>]');
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
const value = (name) => argv.find((a) => a.startsWith(`--${name}=`))?.slice(name.length + 3);
|
|
129
|
+
const report = await assetReport({ dist: folder, assets: value('assets') });
|
|
130
|
+
|
|
131
|
+
console.log(`\n ${report.dist}`);
|
|
132
|
+
for (const file of report.files) {
|
|
133
|
+
console.log(
|
|
134
|
+
` ${file.path.padEnd(40)}${kb(file.raw).padStart(10)} gz ${kb(file.gzip).padStart(10)} br ${kb(file.brotli).padStart(10)} ${percent(file.share).padStart(6)}`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
console.log(
|
|
138
|
+
` ${report.totals.files} file(s), ${kb(report.totals.raw)} raw -> ${kb(report.totals.gzip)} gz / ${kb(report.totals.brotli)} br`,
|
|
139
|
+
);
|
|
140
|
+
if (report.assets.length) {
|
|
141
|
+
console.log(` ${report.totals.assetFiles} embedded asset(s), ${kb(report.totals.assetRaw)} raw`);
|
|
142
|
+
for (const group of report.duplicates) console.log(` duplicate: ${group.join(' == ')}`);
|
|
143
|
+
}
|
|
144
|
+
const json = value('json');
|
|
145
|
+
if (json) {
|
|
146
|
+
await writeFile(json, JSON.stringify(report, null, 2) + '\n', 'utf8');
|
|
147
|
+
console.log(` report written to ${json}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
//realpath, since an npm `bin` shim reaches this file through a symlink
|
|
152
|
+
//no top-level await: a vite config loaded as CommonJS has to be able to require() this module
|
|
153
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url))
|
|
154
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
155
|
+
console.error(error);
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
});
|
|
@@ -7,6 +7,8 @@ export interface CompileResourcesOptions {
|
|
|
7
7
|
to: string;
|
|
8
8
|
mime?: Record<string, string>;
|
|
9
9
|
groupBy?: (key: string) => string;
|
|
10
|
+
/** Exact paths relative to the asset folder to embed. Omit to include all; [] includes none. Unknown entries fail by name. */
|
|
11
|
+
assetFiles?: readonly string[];
|
|
10
12
|
toWebp?: boolean;
|
|
11
13
|
webpLossless?: boolean;
|
|
12
14
|
webpQuality?: number;
|
|
@@ -58,6 +58,9 @@ export async function* walk(dir) {
|
|
|
58
58
|
* @param options.from folder holding the assets
|
|
59
59
|
* @param options.to folder to write the generated scripts into
|
|
60
60
|
* @param options.mime extension to MIME type, merged over the defaults
|
|
61
|
+
* @param options.assetFiles exact paths relative to `from` to embed; omit for all, [] for none.
|
|
62
|
+
* Unknown paths and explicitly selected unsupported formats fail before replacing output.
|
|
63
|
+
* credits.json is converted to CREDITS.txt only when selected (or when no list is given).
|
|
61
64
|
* @param options.groupBy how to name the script an asset lands in; defaults to its top folder
|
|
62
65
|
* @param options.toWebp convert `.png`/`.jpg`/`.jpeg` assets to WebP before embedding
|
|
63
66
|
* (default false - see `tools/webp-convert.mjs`). The asset's *key* never changes (a game
|
|
@@ -81,6 +84,7 @@ export async function compileResources({
|
|
|
81
84
|
to,
|
|
82
85
|
mime = {},
|
|
83
86
|
groupBy,
|
|
87
|
+
assetFiles,
|
|
84
88
|
toWebp: convertToWebp = false,
|
|
85
89
|
webpLossless = true,
|
|
86
90
|
webpQuality = 90,
|
|
@@ -90,6 +94,22 @@ export async function compileResources({
|
|
|
90
94
|
const types = { ...DEFAULT_MIME, ...mime };
|
|
91
95
|
const group = groupBy ?? ((key) => (key.includes('/') ? key.slice(0, key.indexOf('/')) : 'assets'));
|
|
92
96
|
|
|
97
|
+
const files = [];
|
|
98
|
+
for await (const file of walk(from)) files.push(file);
|
|
99
|
+
const keyOf = (file) => relative(from, file).split(sep).join('/');
|
|
100
|
+
const selected =
|
|
101
|
+
assetFiles === undefined
|
|
102
|
+
? undefined
|
|
103
|
+
: new Set(assetFiles.map((key) => key.replaceAll('\\', '/').replace(/^\.\//, '')));
|
|
104
|
+
if (selected) {
|
|
105
|
+
const known = new Set(files.map(keyOf));
|
|
106
|
+
for (const key of selected) {
|
|
107
|
+
if (!known.has(key)) throw new Error(`unknown asset file ${key}`);
|
|
108
|
+
if (key !== 'credits.json' && !types[extname(key).toLowerCase()])
|
|
109
|
+
throw new Error(`unsupported asset file ${key}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
93
113
|
await rm(to, { recursive: true, force: true });
|
|
94
114
|
await mkdir(to, { recursive: true });
|
|
95
115
|
|
|
@@ -106,7 +126,8 @@ export async function compileResources({
|
|
|
106
126
|
const creditsFile = join(from, 'credits.json');
|
|
107
127
|
let credits = null;
|
|
108
128
|
|
|
109
|
-
for
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
if (selected && !selected.has(keyOf(file))) continue;
|
|
110
131
|
if (file === creditsFile) {
|
|
111
132
|
credits = creditsText(JSON.parse(await readFile(file, 'utf8')));
|
|
112
133
|
continue;
|
|
@@ -118,7 +139,7 @@ export async function compileResources({
|
|
|
118
139
|
continue;
|
|
119
140
|
}
|
|
120
141
|
|
|
121
|
-
const key =
|
|
142
|
+
const key = keyOf(file);
|
|
122
143
|
const data = await readFile(file);
|
|
123
144
|
|
|
124
145
|
let mimeType = type;
|
package/tools/emit-page.d.mts
CHANGED
|
@@ -9,6 +9,8 @@ export interface EmitPageOptions {
|
|
|
9
9
|
assets?: string;
|
|
10
10
|
/** maps an asset path to its script's name; defaults to one script per top-level folder */
|
|
11
11
|
groupBy?: (key: string) => string;
|
|
12
|
+
/** Exact paths relative to the asset folder to embed. Omit to include all; [] includes none. Unknown entries fail by name. */
|
|
13
|
+
assetFiles?: readonly string[];
|
|
12
14
|
/** write `.gz`/`.br` siblings for a server to negotiate; default true */
|
|
13
15
|
compress?: boolean;
|
|
14
16
|
/** also write `.xz`, where the system `xz` binary exists */
|
package/tools/emit-page.mjs
CHANGED
|
@@ -66,6 +66,7 @@ export async function emitPage({
|
|
|
66
66
|
dist,
|
|
67
67
|
assets,
|
|
68
68
|
groupBy,
|
|
69
|
+
assetFiles,
|
|
69
70
|
compress = true,
|
|
70
71
|
xz = false,
|
|
71
72
|
singleFile = false,
|
|
@@ -75,8 +76,17 @@ export async function emitPage({
|
|
|
75
76
|
csp = true,
|
|
76
77
|
}) {
|
|
77
78
|
if (!dist) throw new Error('emitPage needs the dist folder of a vite build');
|
|
79
|
+
if (!assets && assetFiles?.length) throw new Error(`unknown asset file ${assetFiles[0]}: no assets folder`);
|
|
78
80
|
const compiled = assets
|
|
79
|
-
? await compileResources({
|
|
81
|
+
? await compileResources({
|
|
82
|
+
from: assets,
|
|
83
|
+
to: join(dist, 'assets'),
|
|
84
|
+
groupBy,
|
|
85
|
+
assetFiles,
|
|
86
|
+
toWebp,
|
|
87
|
+
webpLossless,
|
|
88
|
+
webpQuality,
|
|
89
|
+
})
|
|
80
90
|
: { groups: [], rawBytes: 0, embeddedBytes: 0, webpConverted: 0 };
|
|
81
91
|
|
|
82
92
|
const html = await readFile(join(dist, 'index.html'), 'utf8');
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ScenarioPlanEntry {
|
|
2
|
+
id: string;
|
|
3
|
+
enter: string;
|
|
4
|
+
/** Steps that play all sides and advance dialogue for one game-owned turn. */
|
|
5
|
+
turn: string[];
|
|
6
|
+
/** A step that throws when game-owned map, units, outcome or campaign checks fail. */
|
|
7
|
+
verify: string;
|
|
8
|
+
leave?: string;
|
|
9
|
+
}
|
|
10
|
+
export function validateScenarioPlan(plan: unknown): ScenarioPlanEntry[];
|
|
11
|
+
export function verifyScenarios(options: { file: string; turns?: number; timeoutMs?: number; only?: string }): Promise<{
|
|
12
|
+
ok: boolean;
|
|
13
|
+
scenarios: { id: string; turns: number; ok: boolean }[];
|
|
14
|
+
failures: string[];
|
|
15
|
+
}>;
|