Skip to content

Commit 77daf8a

Browse files
author
DavidQ
committed
centralize string normalization helpers
1 parent d657b54 commit 77daf8a

8 files changed

Lines changed: 75 additions & 41 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MODEL: GPT-5.3-codex
22
REASONING: high
33
COMMAND:
4-
Execute docs/pr/BUILD_PR_SHARED_EXTRACTION_36_ARRAY_UTILS_ENSURE_ARRAY_BATCH.md exactly.
4+
Execute docs/pr/BUILD_PR_SHARED_EXTRACTION_37_STRING_UTILS_TRIM_NORMALIZE_BATCH.md exactly.
55
Edit only listed files.
6-
Package to <project folder>/tmp/BUILD_PR_SHARED_EXTRACTION_36_ARRAY_UTILS_ENSURE_ARRAY_BATCH_delta.zip
6+
Package to <project folder>/tmp/BUILD_PR_SHARED_EXTRACTION_37_STRING_UTILS_TRIM_NORMALIZE_BATCH_delta.zip

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
centralize ensureArray helper
1+
centralize string normalization helpers

docs/dev/NEXT_COMMAND.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Next: string utils batch
1+
Next: id utils batch
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ensureArray centralized
1+
string utils centralized
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# BUILD_PR_SHARED_EXTRACTION_37_STRING_UTILS_TRIM_NORMALIZE_BATCH
2+
3+
## Purpose
4+
Centralize duplicated string normalization helpers across tools/debug domains.
5+
6+
## Single PR Purpose
7+
Normalize ONLY:
8+
9+
- normalizeString(value)
10+
- trimSafe(value)
11+
12+
## Exact Files Allowed
13+
14+
### Shared
15+
1. src/shared/utils/stringUtils.js
16+
17+
### Consumers (from dupes report)
18+
2. tools/dev/devConsoleIntegration.js
19+
3. tools/dev/commandPacks/packUtils.js
20+
4. tools/dev/presets/debugPresetRegistry.js
21+
5. tools/shared/runtimeAssetLoader.js
22+
6. tools/shared/vectorTemplateSampleGame.js
23+
24+
## Rules
25+
- remove local implementations
26+
- import from shared
27+
- no behavior change
28+
- no scope expansion
29+
30+
## Validation
31+
- helper exists once
32+
- imports correct
33+
- no local duplicates remain

src/shared/utils/stringUtils.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function trimSafe(value) {
2+
return typeof value === "string" ? value.trim() : "";
3+
}
4+
5+
export function normalizeString(value) {
6+
return trimSafe(value);
7+
}

tools/shared/runtimeAssetLoader.js

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,13 @@ import ImageAssetLoader from "../../engine/assets/ImageAssetLoader.js";
44
import { prepareVectorGeometryRuntimeAsset } from "./vectorGeometryRuntime.js";
55
import { ensureArray } from "../../src/shared/utils/arrayUtils.js";
66
import { cloneJson } from "../../src/shared/utils/jsonUtils.js";
7-
8-
function sanitizeText(value) {
9-
return typeof value === "string" ? value.trim() : "";
10-
}
7+
import { trimSafe } from "../../src/shared/utils/stringUtils.js";
118

129
function createReport(level, code, message) {
1310
return {
14-
level: sanitizeText(level) || "info",
15-
code: sanitizeText(code),
16-
message: sanitizeText(message)
11+
level: trimSafe(level) || "info",
12+
code: trimSafe(code),
13+
message: trimSafe(message)
1714
};
1815
}
1916

@@ -36,7 +33,7 @@ function validatePackageManifest(manifest) {
3633
if (!Number.isFinite(pkg.version)) {
3734
throw new Error("Invalid packaged input: package.version must be numeric.");
3835
}
39-
if (!sanitizeText(pkg.projectId)) {
36+
if (!trimSafe(pkg.projectId)) {
4037
throw new Error("Invalid packaged input: package.projectId is required.");
4138
}
4239
if (!Array.isArray(pkg.assets) || pkg.assets.length === 0) {
@@ -51,8 +48,8 @@ function validatePackageManifest(manifest) {
5148

5249
const seenIds = new Set();
5350
pkg.assets.forEach((asset, index) => {
54-
const id = sanitizeText(asset?.id);
55-
const type = sanitizeText(asset?.type);
51+
const id = trimSafe(asset?.id);
52+
const type = trimSafe(asset?.type);
5653
if (!id) {
5754
throw new Error(`Invalid packaged input: asset at index ${index} is missing id.`);
5855
}
@@ -66,16 +63,16 @@ function validatePackageManifest(manifest) {
6663
});
6764

6865
pkg.roots.forEach((root, index) => {
69-
const id = sanitizeText(root?.id);
66+
const id = trimSafe(root?.id);
7067
if (!id || !seenIds.has(id)) {
7168
throw new Error(`Invalid packaged input: startup root at index ${index} does not resolve to a packaged asset.`);
7269
}
7370
});
7471

7572
pkg.dependencies.forEach((dependency, index) => {
76-
const from = sanitizeText(dependency?.from);
77-
const to = sanitizeText(dependency?.to);
78-
const type = sanitizeText(dependency?.type);
73+
const from = trimSafe(dependency?.from);
74+
const to = trimSafe(dependency?.to);
75+
const type = trimSafe(dependency?.type);
7976
if (!from || !to || !type) {
8077
throw new Error(`Invalid packaged input: dependency at index ${index} is incomplete.`);
8178
}
@@ -88,12 +85,12 @@ function validatePackageManifest(manifest) {
8885
}
8986

9087
function createRegistryDefinition(asset, source) {
91-
const type = sanitizeText(asset?.type);
88+
const type = trimSafe(asset?.type);
9289
const sourceType = type === "image" ? "image" : "data";
9390
return {
94-
id: sanitizeText(asset?.id),
91+
id: trimSafe(asset?.id),
9592
type: sourceType,
96-
path: sanitizeText(asset?.path),
93+
path: trimSafe(asset?.path),
9794
source
9895
};
9996
}
@@ -102,7 +99,7 @@ function toBootstrapData(packageManifest, loadedAssets) {
10299
const pkg = packageManifest.package;
103100
return {
104101
projectId: pkg.projectId,
105-
startupAssetIds: pkg.roots.map((root) => sanitizeText(root.id)),
102+
startupAssetIds: pkg.roots.map((root) => trimSafe(root.id)),
106103
assetTable: loadedAssets.reduce((accumulator, entry) => {
107104
accumulator[entry.id] = entry.asset;
108105
return accumulator;
@@ -112,12 +109,12 @@ function toBootstrapData(packageManifest, loadedAssets) {
112109
}
113110

114111
function finalizeLoadedAsset(asset, loadedAsset) {
115-
if (sanitizeText(asset?.type) !== "vector") {
112+
if (trimSafe(asset?.type) !== "vector") {
116113
return loadedAsset;
117114
}
118115
return prepareVectorGeometryRuntimeAsset(loadedAsset, {
119-
sourcePath: sanitizeText(asset?.path),
120-
assetId: sanitizeText(asset?.id)
116+
sourcePath: trimSafe(asset?.path),
117+
assetId: trimSafe(asset?.id)
121118
});
122119
}
123120

@@ -169,7 +166,7 @@ export async function loadPackagedProjectRuntime(options = {}) {
169166

170167
for (let index = 0; index < pkg.assets.length; index += 1) {
171168
const asset = pkg.assets[index];
172-
const assetId = sanitizeText(asset?.id);
169+
const assetId = trimSafe(asset?.id);
173170
const resolvedSource = assetProvider(asset, manifest);
174171
if (resolvedSource === undefined || resolvedSource === null || resolvedSource === "") {
175172
reports.push(createReport("error", "MISSING_PACKAGED_ASSET", `Required packaged asset ${assetId} could not be resolved from package input.`));
@@ -189,7 +186,7 @@ export async function loadPackagedProjectRuntime(options = {}) {
189186
};
190187
}
191188
const finalizedAsset = finalizeLoadedAsset(asset, result.asset);
192-
if (sanitizeText(asset?.type) === "vector") {
189+
if (trimSafe(asset?.type) === "vector") {
193190
reports.push(createReport("info", "VECTOR_RUNTIME_READY", `Prepared vector geometry runtime data for ${assetId}.`));
194191
}
195192
loadedAssets.push({

tools/shared/vectorTemplateSampleGame.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,18 @@ import { buildDebugVisualizationLayer, summarizeDebugVisualizationLayer } from "
99
import { buildPerformanceProfiler, summarizePerformanceProfiler } from "./performanceProfiler.js";
1010
import { createVectorNativeTemplateDefinition } from "./vectorNativeTemplate.js";
1111
import { cloneJson } from "../../src/shared/utils/jsonUtils.js";
12-
13-
function sanitizeText(value) {
14-
return typeof value === "string" ? value.trim() : "";
15-
}
12+
import { normalizeString } from "../../src/shared/utils/stringUtils.js";
1613

1714
function createReport(level, code, message) {
1815
return {
19-
level: sanitizeText(level) || "info",
20-
code: sanitizeText(code),
21-
message: sanitizeText(message)
16+
level: normalizeString(level) || "info",
17+
code: normalizeString(code),
18+
message: normalizeString(message)
2219
};
2320
}
2421

2522
function remapPath(pathValue) {
26-
return sanitizeText(pathValue).replace("templates/vector-native-arcade/", "games/vector-arcade-sample/");
23+
return normalizeString(pathValue).replace("templates/vector-native-arcade/", "games/vector-arcade-sample/");
2724
}
2825

2926
function remapRegistry(registry) {
@@ -84,24 +81,24 @@ function buildImageSource(asset) {
8481
image: {
8582
width: 960,
8683
height: 720,
87-
src: sanitizeText(asset?.path)
84+
src: normalizeString(asset?.path)
8885
},
8986
status: "provided-loaded"
9087
};
9188
}
9289

9390
function createResolvePackagedAsset(assetSources) {
9491
return (asset) => {
95-
const assetId = sanitizeText(asset?.id);
96-
if (sanitizeText(asset?.type) === "image") {
92+
const assetId = normalizeString(asset?.id);
93+
if (normalizeString(asset?.type) === "image") {
9794
return buildImageSource(asset);
9895
}
9996
return assetSources[assetId] ? cloneJson(assetSources[assetId]) : null;
10097
};
10198
}
10299

103100
export function summarizeVectorTemplateSampleGame(result) {
104-
const status = sanitizeText(result?.sampleGame?.status);
101+
const status = normalizeString(result?.sampleGame?.status);
105102
if (status !== "ready") {
106103
return "Vector template sample game unavailable.";
107104
}
@@ -172,7 +169,7 @@ export async function buildVectorTemplateSampleGame(options = {}) {
172169
});
173170

174171
const packageAssetIds = Array.isArray(packageResult.manifest?.package?.assets)
175-
? packageResult.manifest.package.assets.map((asset) => sanitizeText(asset?.id))
172+
? packageResult.manifest.package.assets.map((asset) => normalizeString(asset?.id))
176173
: [];
177174
const hasSpriteRuntimeDependency = packageAssetIds.some((id) => id.startsWith("sprite."));
178175

0 commit comments

Comments
 (0)