Skip to content

Commit 5328c36

Browse files
author
DavidQ
committed
Continue Phase 2 extraction of duplicated repo-owned helpers into shared utility modules - PR_26140_033-shared-utils-phase-2
1 parent 5f821f9 commit 5328c36

30 files changed

Lines changed: 108 additions & 134 deletions
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# PR_26140_033 Shared Utils Phase 2 Report
2+
3+
## Summary
4+
- Added shared string helpers for `normalizeText`, `normalizeToken`, and case-preserving `normalizeGameId`.
5+
- Added shared `toObject` in `src/shared/utils/objectUtils.js` for callers that intentionally allow arrays, while preserving existing `asObject` array-rejecting semantics.
6+
- Replaced exact-match local helpers across active game, engine, and tool code for `sanitizeText`, `normalizeToken`, `normalizeGameId`, `toObject`, and `positiveInteger`.
7+
- Left lowercasing/slug `normalizeGameId` variants and other differing semantics in place.
8+
9+
## Duplicate Scan
10+
- Re-ran `tools/shared/powerShell/find_dupes_called.ps1` into `tmp/dupes_called.txt`.
11+
- Verified `node_modules` paths are not present in the regenerated duplicate report.
12+
- Selected duplicate block counts from `tmp/dupes_called.txt`:
13+
- `function sanitizeText(value)`: 41 -> 38
14+
- `export function sanitizeText(value)`: 3 -> 0
15+
- `function toObject(value)`: 12 -> 2
16+
- `function normalizeToken(value)`: 5 -> 2
17+
- `function normalizeGameId(value)`: 5 -> 2
18+
- `function positiveInteger(value)`: 3 -> 0
19+
20+
## Scope Notes
21+
- No vendor, generated bundle, `node_modules`, `tests/results`, or archived tool files were modified.
22+
- `normalizeGameId` variants that lowercase or slugify game IDs remain local because their behavior differs from case-preserving game ID trimming.
23+
- `normalizeString` helpers that coerce non-strings through `String(value || "")` remain local because they differ from the shared string-only trim semantics.
24+
- `distance`/`near` helpers were not migrated in this pass because the remaining candidates either use different distance metrics or are outside the requested validation surface.
25+
26+
## Validation
27+
- PASS: `npm run build:manifest`
28+
- PASS: `node tests\games\AsteroidsValidation.test.mjs`
29+
- PASS: `node tests\games\AsteroidsManifestScreenDimensions.test.mjs`
30+
- PASS: `node tests\games\AsteroidsPresentation.test.mjs`
31+
- PASS: `node tests\tools\ObjectVectorFinalRuntimeCleanup.test.mjs`
32+
- PASS: `node tests\tools\ObjectVectorStudioV2DeleteCleanup.test.mjs`
33+
- PASS: `npm run test:workspace-v2` (58 passed)
34+
- PASS: `npx playwright test tests/playwright/tools/ObjectVectorStudioV2FirstClassToolStarter.spec.mjs --project=playwright --workers=1 --reporter=list` (4 passed)
35+
- PASS: `npx playwright test tests/playwright/tools/AsteroidsGameSceneCleanup.spec.mjs --project=playwright --workers=1 --reporter=list` (1 passed)
36+
- PASS: `git diff --check` (no whitespace errors; Git reported existing CRLF/LF normalization warnings for two modified files)
37+
38+
## Corrected During Validation
39+
- The first Asteroids Node validation exposed a new browser-absolute import in `games/shared/workspaceGameAssetCatalog.js`; switched new `games/shared` and `games/index.render.js` imports to relative paths and reran validation successfully.
40+
- A first Playwright invocation used Windows backslashes and matched no tests; reran with forward slashes successfully.

games/Asteroids/debug/asteroidsShowcaseDebug.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ asteroidsShowcaseDebug.js
66
*/
77

88
import { asArray, asObject } from "../../../src/engine/debug/inspectors/shared/inspectorUtils.js";
9-
10-
function sanitizeText(value) {
11-
return typeof value === "string" ? value.trim() : "";
12-
}
9+
import { sanitizeText } from "../../../src/shared/string/index.js";
1310

1411
function formatNumber(value, fallback = 0) {
1512
return Number.isFinite(value) ? Number(value) : fallback;

games/Asteroids/game/AsteroidsGameScene.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ AsteroidsGameScene.js
66
*/
77
import { AttractModeController, Scene } from '../../../src/engine/scene/index.js';
88
import { ParticleSystem } from '../../../src/engine/fx/index.js';
9+
import { asPositiveInteger } from '../../../src/shared/number/index.js';
910
import AsteroidsSession from './AsteroidsSession.js';
1011
import AsteroidsWorld from './AsteroidsWorld.js';
1112
import AsteroidsAudio from '../systems/AsteroidsAudio.js';
@@ -69,14 +70,9 @@ function logSceneBootStage(stage, details = null) {
6970
}
7071
}
7172

72-
function positiveInteger(value) {
73-
const parsed = Math.floor(Number(value));
74-
return Number.isNaN(parsed) || Math.abs(parsed) === Infinity || parsed <= 0 ? 0 : parsed;
75-
}
76-
7773
function screenDimensionsFromOptions(options) {
78-
const width = positiveInteger(options?.screenDimensions?.width);
79-
const height = positiveInteger(options?.screenDimensions?.height);
74+
const width = asPositiveInteger(options?.screenDimensions?.width, 0);
75+
const height = asPositiveInteger(options?.screenDimensions?.height, 0);
8076
if (!width || !height) {
8177
throw new Error('AsteroidsGameScene requires screenDimensions.width and screenDimensions.height from game.manifest.json.');
8278
}

games/Asteroids/index.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ObjectVectorRuntimeAssetService } from "../../src/engine/rendering/inde
88
import { Theme, ThemeTokens } from "../../src/engine/theme/index.js";
99
import { resolveDebugConfig } from "../../src/shared/utils/debugConfigUtils.js";
1010
import { createNoopDevConsoleIntegration } from "../../src/shared/utils/createNoopDevConsoleIntegration.js";
11+
import { asPositiveInteger } from "../../src/shared/number/index.js";
1112
import AsteroidsGameScene from "./game/AsteroidsGameScene.js";
1213
import { loadAsteroidsObjectGeometryFromManifest } from "./game/asteroidsObjectGeometryManifest.js";
1314
import { preloadWorkspaceGameAssetCatalog } from "../shared/workspaceGameAssetCatalog.js";
@@ -73,14 +74,9 @@ async function loadAsteroidsManifestPayload(manifestPath, manifestPayload = null
7374
return response.json();
7475
}
7576

76-
function positiveInteger(value) {
77-
const parsed = Math.floor(Number(value));
78-
return Number.isNaN(parsed) || Math.abs(parsed) === Infinity || parsed <= 0 ? 0 : parsed;
79-
}
80-
8177
export function resolveAsteroidsScreenDimensions(manifest) {
82-
const width = positiveInteger(manifest?.screen?.width);
83-
const height = positiveInteger(manifest?.screen?.height);
78+
const width = asPositiveInteger(manifest?.screen?.width, 0);
79+
const height = asPositiveInteger(manifest?.screen?.height, 0);
8480
if (!width || !height) {
8581
throw new Error("Asteroids game.manifest.json requires screen.width and screen.height.");
8682
}

games/SolarSystem/game/SolarSystemScene.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ SolarSystemScene.js
66
*/
77
import { Scene } from '/src/engine/scene/index.js';
88
import SolarSystemWorld from './SolarSystemWorld.js';
9+
import { toObject } from '/src/shared/utils/objectUtils.js';
910

1011
const VIEW = { width: 960, height: 720 };
1112

@@ -18,10 +19,6 @@ const DEFAULT_COLORS = {
1819
panel: '#07101d',
1920
};
2021

21-
function toObject(value) {
22-
return value && typeof value === 'object' ? value : {};
23-
}
24-
2522
function sanitizeSolarSceneColors(skin) {
2623
const colors = toObject(skin?.colors);
2724
return {

games/SolarSystem/game/SolarSystemWorld.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ David Quesenberry
44
03/24/2026
55
SolarSystemWorld.js
66
*/
7+
import { toObject } from '/src/shared/utils/objectUtils.js';
8+
79
const MAX_STEP_SECONDS = 1 / 60;
810

911
const TIME_SCALES = [
@@ -84,10 +86,6 @@ function clampIndex(value, max) {
8486
return Math.max(0, Math.min(max, value));
8587
}
8688

87-
function toObject(value) {
88-
return value && typeof value === 'object' ? value : {};
89-
}
90-
9189
function toFiniteNumber(value, fallback) {
9290
const numeric = Number(value);
9391
return Number.isFinite(numeric) ? numeric : fallback;

games/bouncing-ball/game/BouncingBallScene.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import BouncingBallAudio from './BouncingBallAudio.js';
99
import BouncingBallInputController from './BouncingBallInputController.js';
1010
import BouncingBallWorld from './BouncingBallWorld.js';
1111
import { wrapTextByCharacterCount } from '/src/shared/utils/index.js';
12+
import { toObject } from '/src/shared/utils/objectUtils.js';
1213

1314
const VIEW = {
1415
width: 960,
@@ -24,10 +25,6 @@ const DEFAULT_COLORS = {
2425
ball: '#f4f4ef',
2526
};
2627

27-
function toObject(value) {
28-
return value && typeof value === 'object' ? value : {};
29-
}
30-
3128
function sanitizeBouncingBallSceneColors(skin) {
3229
const colors = toObject(skin?.colors);
3330
return {

games/bouncing-ball/game/BouncingBallWorld.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ David Quesenberry
55
BouncingBallWorld.js
66
*/
77
import { clamp } from '/src/shared/utils/mathUtils.js';
8+
import { toObject } from '/src/shared/utils/objectUtils.js';
89

910
const MAX_STEP_SECONDS = 1 / 120;
1011
const DEFAULT_BOUNCING_BALL_WORLD_SKIN = Object.freeze({
@@ -17,10 +18,6 @@ const DEFAULT_BOUNCING_BALL_WORLD_SKIN = Object.freeze({
1718
}
1819
});
1920

20-
function toObject(value) {
21-
return value && typeof value === 'object' ? value : {};
22-
}
23-
2421
function toFiniteNumber(value, fallback) {
2522
const numeric = Number(value);
2623
return Number.isFinite(numeric) ? numeric : fallback;

games/breakout/game/BreakoutScene.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import BreakoutAudio from './BreakoutAudio.js';
99
import BreakoutInputController from './BreakoutInputController.js';
1010
import BreakoutWorld from './BreakoutWorld.js';
1111
import { wrapTextByCharacterCount } from '/src/shared/utils/index.js';
12+
import { toObject } from '/src/shared/utils/objectUtils.js';
1213

1314
const VIEW = {
1415
width: 960,
@@ -25,10 +26,6 @@ const DEFAULT_COLORS = {
2526
panel: '#000000',
2627
};
2728

28-
function toObject(value) {
29-
return value && typeof value === 'object' ? value : {};
30-
}
31-
3229
function sanitizeBreakoutSceneColors(skin) {
3330
const colors = toObject(skin?.colors);
3431
return {

games/breakout/game/BreakoutWorld.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ David Quesenberry
55
BreakoutWorld.js
66
*/
77
import { clamp } from '/src/shared/utils/mathUtils.js';
8+
import { toObject } from '/src/shared/utils/objectUtils.js';
89

910
const MAX_STEP_SECONDS = 1 / 120;
1011

@@ -33,10 +34,6 @@ const DEFAULT_BREAKOUT_SKIN = Object.freeze({
3334
}
3435
});
3536

36-
function toObject(value) {
37-
return value && typeof value === 'object' ? value : {};
38-
}
39-
4037
function toFiniteNumber(value, fallback) {
4138
const numeric = Number(value);
4239
return Number.isFinite(numeric) ? numeric : fallback;

0 commit comments

Comments
 (0)