Parent: #2129
Blocked by: none. Wave 1; this is a proven multi-owner pure URL split, not a new common utility.
Scope — exact tracked files
Production:
Tests:
src/utils/__tests__/url.test.ts
Destination and importer edits
Split by owning behavior:
normalizeBaseUrl -> src/client/base-url.ts
buildBundleUrl -> src/metro/bundle-url.ts
Move the existing normalization tests to src/client/base-url.test.ts and add the bundle URL cases beside src/metro/bundle-url.test.ts; delete the old utility test rather than retaining a shared test path.
Update exact production importers:
src/client/client-companion-tunnel-worker.ts -> src/client/base-url.ts
src/client/client-companion-tunnel.ts -> src/client/base-url.ts
src/metro/client-metro.ts -> both owning modules
src/sdk/metro.ts -> re-export the two functions from their owning modules
Do not create src/common, a generic URL package, an internal barrel, or a compatibility re-export at src/utils/url.ts.
Behavior invariants
Preserve removal of all trailing slash characters, empty-root handling, preservation of all other URL characters, URL parsing/serialization, platform/dev/minify query values, and existing SDK exports. Preserve companion tunnel base URL normalization and Metro bundle URL behavior exactly.
Focused validation
Run:
pnpm vitest run src/client/base-url.test.ts src/metro/bundle-url.test.ts
- the existing companion-tunnel and Metro tests
- the SDK type/build checks
pnpm check:affected --run
Planted-red proof
No new structural rule is added. Temporarily retain one trailing slash or change a bundle query value and observe the moved tests fail independently; restore the exact behavior before the green run. The SDK export check must prove the public names resolve from their owners, not from a hidden shim.
Dependency order
Wave 1. This child is independent of the other owner slices. The terminal child is blocked by it.
Shrink target
Delete src/utils/url.ts and its old test. Split the two behaviors without duplicating normalization logic; after discounting the physical move, production LOC must not grow and no new top-level shared zone may be introduced.
Parent: #2129
Blocked by: none. Wave 1; this is a proven multi-owner pure URL split, not a new common utility.
Scope — exact tracked files
Production:
src/utils/url.tsTests:
src/utils/__tests__/url.test.tsDestination and importer edits
Split by owning behavior:
normalizeBaseUrl->src/client/base-url.tsbuildBundleUrl->src/metro/bundle-url.tsMove the existing normalization tests to
src/client/base-url.test.tsand add the bundle URL cases besidesrc/metro/bundle-url.test.ts; delete the old utility test rather than retaining a shared test path.Update exact production importers:
src/client/client-companion-tunnel-worker.ts->src/client/base-url.tssrc/client/client-companion-tunnel.ts->src/client/base-url.tssrc/metro/client-metro.ts-> both owning modulessrc/sdk/metro.ts-> re-export the two functions from their owning modulesDo not create
src/common, a generic URL package, an internal barrel, or a compatibility re-export atsrc/utils/url.ts.Behavior invariants
Preserve removal of all trailing slash characters, empty-root handling, preservation of all other URL characters, URL parsing/serialization,
platform/dev/minifyquery values, and existing SDK exports. Preserve companion tunnel base URL normalization and Metro bundle URL behavior exactly.Focused validation
Run:
pnpm vitest run src/client/base-url.test.ts src/metro/bundle-url.test.tspnpm check:affected --runPlanted-red proof
No new structural rule is added. Temporarily retain one trailing slash or change a bundle query value and observe the moved tests fail independently; restore the exact behavior before the green run. The SDK export check must prove the public names resolve from their owners, not from a hidden shim.
Dependency order
Wave 1. This child is independent of the other owner slices. The terminal child is blocked by it.
Shrink target
Delete
src/utils/url.tsand its old test. Split the two behaviors without duplicating normalization logic; after discounting the physical move, production LOC must not grow and no new top-level shared zone may be introduced.