Read the source and its declared rule
The original pack’s docs/record refers to ../files/a. Its mapped destination is target/assets/a.bin, while the new record sits under target/pages. Reusing the same relative spelling incorrectly reaches target/files/a. Internal references require a supplied object map; external origins remain external under this fixture rule.
| Record | Input JSON |
|---|---|
| Base input | {"reference":"../files/a","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[{"source":"https://files.example.invalid/pack/files/a","destination":"https://files.example.invalid/target/assets/a.bin"}]} |
Compare the actual paths
Correct new reference ../assets/a.bin resolves to the mapped file. Retain the mapped destination’s query and fragment when constructing a relative reference. All example.invalid addresses are labels for local URL resolution and are never requested.
| Path | Output JSON |
|---|---|
| Misleading path | {"accepted":true,"reference":"../files/a","resolved":"https://files.example.invalid/target/files/a"} |
| Corrected path | {"accepted":true,"reference":"../assets/a.bin","resolved":"https://files.example.invalid/target/assets/a.bin"} |
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};
import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
if(!corrected)return {accepted:true,reference:i.reference,resolved:new URL(i.reference,i.newBase).href};
const old=new URL(i.reference,i.oldBase);const oldBase=new URL(i.oldBase);
if(old.origin!==oldBase.origin)return {accepted:true,reference:i.reference,resolved:old.href};
const target=i.mapping.find(m=>m.source===old.href);
if(!target)return {accepted:false,error:'unmapped-internal-reference',original:i.reference,resolvedSource:old.href};
const dest=new URL(target.destination);const newBase=new URL(i.newBase);
const reference=dest.origin===newBase.origin?posix.relative(posix.dirname(newBase.pathname),dest.pathname)+dest.search+dest.hash:dest.href;
return {accepted:true,reference,resolved:new URL(reference,i.newBase).href};
};
const cases=[{"label":"misleading base","input":{"reference":"../files/a","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[{"source":"https://files.example.invalid/pack/files/a","destination":"https://files.example.invalid/target/assets/a.bin"}]},"expected":{"accepted":true,"reference":"../files/a","resolved":"https://files.example.invalid/target/files/a"},"corrected":false},{"label":"corrected base","input":{"reference":"../files/a","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[{"source":"https://files.example.invalid/pack/files/a","destination":"https://files.example.invalid/target/assets/a.bin"}]},"expected":{"accepted":true,"reference":"../assets/a.bin","resolved":"https://files.example.invalid/target/assets/a.bin"},"corrected":true},{"label":"external absolute reference retained","input":{"reference":"https://outside.example.invalid/a","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[]},"expected":{"accepted":true,"reference":"https://outside.example.invalid/a","resolved":"https://outside.example.invalid/a"},"corrected":true},{"label":"unmapped internal object held","input":{"reference":"../files/missing","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[]},"expected":{"accepted":false,"error":"unmapped-internal-reference","original":"../files/missing","resolvedSource":"https://files.example.invalid/pack/files/missing"},"corrected":true},{"label":"mapped query and fragment retained","input":{"reference":"../files/a","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[{"source":"https://files.example.invalid/pack/files/a","destination":"https://files.example.invalid/target/assets/a.bin?revision=2#part"}]},"expected":{"accepted":true,"reference":"../assets/a.bin?revision=2#part","resolved":"https://files.example.invalid/target/assets/a.bin?revision=2#part"},"corrected":true}];
for(const test of cases){
const actual=run(test.input,test.corrected);
assert.deepEqual(actual,test.expected,test.label);
console.log(test.label,actual);
}
Check boundaries before the next load
An external absolute reference stays unchanged. An unmapped internal reference is held with its resolved old target. A mapped ?revision=2#part stays in both the new reference and resolved URL.
| Case | Input JSON | Expected output JSON |
|---|---|---|
| External absolute reference | {"reference":"https://outside.example.invalid/a","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[]} | {"accepted":true,"reference":"https://outside.example.invalid/a","resolved":"https://outside.example.invalid/a"} |
| Internal object lacks a map | {"reference":"../files/missing","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[]} | {"accepted":false,"error":"unmapped-internal-reference","original":"../files/missing","resolvedSource":"https://files.example.invalid/pack/files/missing"} |
| Mapped query and fragment | {"reference":"../files/a","oldBase":"https://files.example.invalid/pack/docs/record","newBase":"https://files.example.invalid/target/pages/record","mapping":[{"source":"https://files.example.invalid/pack/files/a","destination":"https://files.example.invalid/target/assets/a.bin?revision=2#part"}]} | {"accepted":true,"reference":"../assets/a.bin?revision=2#part","resolved":"https://files.example.invalid/target/assets/a.bin?revision=2#part"} |
Should the new record simply keep ../files/a?
Show the worked answer
No. Its new base changes that destination. Use the mapped ../assets/a.bin reference instead.