@neabyte/v4a-diff 0.2.0 → 0.2.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/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
- "use strict";class s{static anchorStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trim(),fuzzScore:1},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:10}];static contextStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trimEnd(),fuzzScore:1},{mapFn:e=>e.trim(),fuzzScore:100},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:1e3}];static unicodeReplacements={"\u2010":"-","\u2011":"-","\u2012":"-","\u2013":"-","\u2014":"-","\u2015":"-","\u2212":"-","\u2018":"'","\u2019":"'","\u201A":"'","\u201B":"'","\u201C":'"',"\u201D":'"',"\u201E":'"',"\u201F":'"',"\xA0":" ","\u2002":" ","\u2003":" ","\u2004":" ","\u2005":" ","\u2006":" ","\u2007":" ","\u2008":" ","\u2009":" ","\u200A":" ","\u202F":" ","\u205F":" ","\u3000":" "};static unicodePattern=new RegExp(`[${Object.keys(this.unicodeReplacements).join("")}]`,"g");static findContext(e,n,t,r){if(r){const i=this.findContextCore(e,n,Math.max(0,e.length-n.length));if(i.matchedIndex!==-1)return i;const c=this.findContextCore(e,n,t);return{matchedIndex:c.matchedIndex,fuzzScore:c.fuzzScore+1e4}}return this.findContextCore(e,n,t)}static resolveAnchor(e,n,t,r){for(const i of this.anchorStrategies){const c=i.mapFn(e);let l=!1;for(let a=0;a<t;a+=1)if(i.mapFn(n[a])===c){l=!0;break}if(l)return t;const o=this.searchLines(n,e,t,i.mapFn);if(o!==-1)return r.fuzzScore+=i.fuzzScore,o}return t}static findContextCore(e,n,t){if(!n.length)return{matchedIndex:t,fuzzScore:0};for(const r of this.contextStrategies)for(let i=t;i<e.length;i+=1)if(this.sliceEquals(e,n,i,r.mapFn))return{matchedIndex:i,fuzzScore:r.fuzzScore};return{matchedIndex:-1,fuzzScore:0}}static normalizeUnicode(e){return e.trim().replace(this.unicodePattern,n=>this.unicodeReplacements[n])}static searchLines(e,n,t,r){const i=r(n);for(let c=t;c<e.length;c+=1)if(r(e[c])===i)return c;return-1}static sliceEquals(e,n,t,r){if(t+n.length>e.length)return!1;for(let i=0;i<n.length;i+=1)if(r(e[t+i])!==r(n[i]))return!1;return!0}}class p{static endFile="*** End of File";static prefixToMode={"+":"add","-":"delete"," ":"keep"};static terminators=["*** End Patch","*** Update File:","*** Delete File:","*** Add File:","*** Move to:"];static normalizeDiffLines(e){const n=e.split(/\r?\n/);return n.at(-1)===""&&n.pop(),n}static parseCreateDiff(e){const n=this.createState(e),t=[];for(;!this.isDone(n,!1);){const r=n.lines[n.currentIndex];if(n.currentIndex+=1,!r.startsWith("+"))throw new SyntaxError(`line ${n.currentIndex} expected '+' prefix but got "${r}"`);t.push(r.slice(1))}return t.join(`
1
+ "use strict";class c{static unicodeReplacements={"\u2010":"-","\u2011":"-","\u2012":"-","\u2013":"-","\u2014":"-","\u2015":"-","\u2212":"-","\uFF0D":"-","\u2018":"'","\u2019":"'","\u201A":"'","\u201B":"'",\u02BC:"'","\uFF07":"'","\u201C":'"',"\u201D":'"',"\u201E":'"',"\u201F":'"',"\uFF02":'"',"\u2026":"...","\xA0":" ","\u2002":" ","\u2003":" ","\u2004":" ","\u2005":" ","\u2006":" ","\u2007":" ","\u2008":" ","\u2009":" ","\u200A":" ","\u202F":" ","\u205F":" ","\u3000":" "};static unicodePattern=new RegExp(`[${Object.keys(this.unicodeReplacements).join("")}]`,"g");static anchorStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trim(),fuzzScore:1},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:10}];static contextStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trimEnd(),fuzzScore:1},{mapFn:this.collapseSpace.bind(this),fuzzScore:50},{mapFn:e=>e.trim(),fuzzScore:100},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:1e3}];static findContext(e,n,t,r){if(r){const i=this.findContextCore(e,n,Math.max(0,e.length-n.length));if(i.matchedIndex!==-1)return i;const o=this.findContextCore(e,n,t);return{matchedIndex:o.matchedIndex,fuzzScore:o.fuzzScore+1e4}}return this.findContextCore(e,n,t)}static resolveAnchor(e,n,t,r){for(const i of this.anchorStrategies){const o=i.mapFn(e);let l=!1;for(let a=0;a<t;a+=1)if(i.mapFn(n[a])===o){l=!0;break}if(l)return t;const s=this.searchLines(n,e,t,i.mapFn);if(s!==-1)return r.fuzzScore+=i.fuzzScore,s}for(const i of this.anchorStrategies){const o=this.searchLines(n,e,t,i.mapFn,!0);if(o!==-1)return r.fuzzScore+=i.fuzzScore+5,o}return t}static collapseSpace(e){return e.replace(/\s+/g," ").trim()}static findContextCore(e,n,t){if(!n.length)return{matchedIndex:t,fuzzScore:0};for(const r of this.contextStrategies)for(let i=t;i<e.length;i+=1)if(this.sliceEquals(e,n,i,r.mapFn))return{matchedIndex:i,fuzzScore:r.fuzzScore};return{matchedIndex:-1,fuzzScore:0}}static normalizeUnicode(e){return e.trim().replace(this.unicodePattern,n=>this.unicodeReplacements[n])}static searchLines(e,n,t,r,i=!1){const o=r(n);if(i&&o.length===0)return-1;for(let l=t;l<e.length;l+=1){const s=r(e[l]);if(i?s.startsWith(o):s===o)return l}return-1}static sliceEquals(e,n,t,r){if(t+n.length>e.length)return!1;for(let i=0;i<n.length;i+=1)if(r(e[t+i])!==r(n[i]))return!1;return!0}}class p{static endFile="*** End of File";static prefixToMode={"+":"add","-":"delete"," ":"keep"};static terminators=["*** End Patch","*** Update File:","*** Delete File:","*** Add File:","*** Move to:"];static normalizeDiffLines(e){const n=e.split(/\r?\n/);return n.at(-1)===""&&n.pop(),n}static parseCreateDiff(e){const n=this.createState(e),t=[];for(;!this.isDone(n,!1);){const r=n.lines[n.currentIndex];if(n.currentIndex+=1,!r.startsWith("+"))throw new SyntaxError(`line ${n.currentIndex} expected '+' prefix but got "${r}"`);t.push(r.slice(1))}return t.join(`
2
2
  `)}static parseUpdateDiff(e,n){const t=this.createState(e),r=n.split(`
3
- `),i=[];let c=0;for(;!this.isDone(t,!0);){const l=this.consumePrefix(t,"@@ "),o=!l&&t.lines[t.currentIndex]==="@@";if(o&&(t.currentIndex+=1),!l&&!o&&c!==0)throw new SyntaxError(`line ${t.currentIndex+1} unexpected line "${t.lines[t.currentIndex]}"`);l?.trim()&&(c=s.resolveAnchor(l,r,c,t));const a=this.readSection(t.lines,t.currentIndex),h=s.findContext(r,a.contextLines,c,a.isEndOfFile);if(h.matchedIndex===-1){const d=a.isEndOfFile?"EOF context":"context";throw new SyntaxError(`line ${t.currentIndex+1} unmatched ${d} at source line ${c+1} "${a.contextLines[0]??""}"`)}t.fuzzScore+=h.fuzzScore;for(const d of a.diffChunks)i.push({...d,sourceIndex:d.sourceIndex+h.matchedIndex});c=h.matchedIndex+a.contextLines.length,t.currentIndex=a.endIndex}return{diffChunks:i,fuzzScore:t.fuzzScore}}static consumePrefix(e,n){return e.lines[e.currentIndex]?.startsWith(n)?(e.currentIndex+=1,e.lines[e.currentIndex-1].slice(n.length)):""}static createState(e){return{lines:[...e,this.terminators[0]],currentIndex:0,fuzzScore:0}}static isDone(e,n){return e.currentIndex>=e.lines.length||this.isTerminator(e.lines[e.currentIndex],n)}static isSectionBoundary(e){return e.startsWith("@@")||this.isTerminator(e,!0)}static isTerminator(e,n){return this.terminators.some(t=>e.startsWith(t))?!0:n&&e.startsWith(this.endFile)}static readSection(e,n){const t=[];let r=[],i=[];const c=[];let l="keep",o=n;for(;o<e.length;){const a=e[o];if(this.isSectionBoundary(a)||a==="***")break;if(a.startsWith("***"))throw new SyntaxError(`line ${o+1} unexpected marker "${a}"`);o+=1;const h=l,d=a||" ",m=this.prefixToMode[d[0]];if(!m)throw new SyntaxError(`line ${o} unexpected line prefix "${d}"`);l=m;const f=d.slice(1);l==="keep"&&h!==l&&(r.length||i.length)&&(c.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),r=[],i=[]),l==="delete"?(r.push(f),t.push(f)):l==="add"?i.push(f):t.push(f)}if((r.length||i.length)&&c.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),o<e.length&&e[o]===this.endFile)return{contextLines:t,diffChunks:c,endIndex:o+1,isEndOfFile:!0};if(o===n)throw new SyntaxError(`line ${o+1} empty section, expected content but got "${e[o]}"`);return{contextLines:t,diffChunks:c,endIndex:o,isEndOfFile:!1}}}class u{static envelopeExact=new Set(["*** Begin Patch","*** End Patch","\"]);static envelopePrefixes=["*** Add File:","*** Delete File:","*** Move to:","*** Update File:","--- a/","--- a\\","+++ b/","+++ b\\"];static apply(e,n,t="update"){if(t==="delete")return this.buildResult(e,"",e.split(`
3
+ `),i=[];let o=0;for(;!this.isDone(t,!0);){const l=this.consumePrefix(t,"@@ "),s=!l&&t.lines[t.currentIndex]==="@@";if(s&&(t.currentIndex+=1),!l&&!s&&o!==0)throw new SyntaxError(`line ${t.currentIndex+1} unexpected line "${t.lines[t.currentIndex]}"`);l?.trim()&&(o=c.resolveAnchor(l,r,o,t));const a=this.readSection(t.lines,t.currentIndex),h=c.findContext(r,a.contextLines,o,a.isEndOfFile);if(h.matchedIndex===-1){const d=a.isEndOfFile?"EOF context":"context",x=a.contextLines[0]??"",f=r[o]??"(end of file)";throw new SyntaxError(`line ${t.currentIndex+1} unmatched ${d} at source line ${o+1} expected "${x}" but found "${f}"`)}t.fuzzScore+=h.fuzzScore;for(const d of a.diffChunks)i.push({...d,sourceIndex:d.sourceIndex+h.matchedIndex});o=h.matchedIndex+a.contextLines.length,t.currentIndex=a.endIndex}return{diffChunks:i,fuzzScore:t.fuzzScore}}static consumePrefix(e,n){return e.lines[e.currentIndex]?.startsWith(n)?(e.currentIndex+=1,e.lines[e.currentIndex-1].slice(n.length)):""}static createState(e){return{lines:[...e,this.terminators[0]],currentIndex:0,fuzzScore:0}}static isDone(e,n){return e.currentIndex>=e.lines.length||this.isTerminator(e.lines[e.currentIndex],n)}static isSectionBoundary(e){return e.startsWith("@@")||this.isTerminator(e,!0)}static isTerminator(e,n){return this.terminators.some(t=>e.startsWith(t))?!0:n&&e.startsWith(this.endFile)}static readSection(e,n){const t=[];let r=[],i=[];const o=[];let l="keep",s=n;for(;s<e.length;){const a=e[s];if(this.isSectionBoundary(a)||a==="***")break;if(a.startsWith("***"))throw new SyntaxError(`line ${s+1} unexpected marker "${a}"`);s+=1;const h=l,d=a||" ",x=this.prefixToMode[d[0]];if(!x)throw new SyntaxError(`line ${s} unexpected line prefix "${d}"`);l=x;const f=d.slice(1);l==="keep"&&h!==l&&(r.length||i.length)&&(o.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),r=[],i=[]),l==="delete"?(r.push(f),t.push(f)):l==="add"?i.push(f):t.push(f)}if((r.length||i.length)&&o.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),s<e.length&&e[s]===this.endFile)return{contextLines:t,diffChunks:o,endIndex:s+1,isEndOfFile:!0};if(s===n)throw new SyntaxError(`line ${s+1} empty section, expected content but got "${e[s]}"`);return{contextLines:t,diffChunks:o,endIndex:s,isEndOfFile:!1}}}class u{static envelopeExact=new Set(["*** Begin Patch","*** End Patch","\"]);static envelopePrefixes=["*** Add File:","*** Delete File:","*** Move to:","*** Update File:","--- a/","--- a\\","+++ b/","+++ b\\"];static apply(e,n,t="update"){if(e=(e??"").replaceAll(`\r
4
+ `,`
5
+ `),n=n??"",t==="delete")return this.buildResult(e,"",e.split(`
4
6
  `),"delete");const r=this.stripLeadingEmpty(this.stripEnvelope(p.normalizeDiffLines(n)));if(t==="create"){const i=p.parseCreateDiff(r);return this.buildResult(e,i,i.split(`
5
7
  `),"add")}return this.applyChunks(e,p.parseUpdateDiff(r,e).diffChunks)}static applyChunks(e,n){const t=e.split(`
6
- `),r=[],i=[];let c=0,l=1;for(const o of n){if(o.sourceIndex>t.length)throw new RangeError(`chunk targets source line ${o.sourceIndex+1} but file only has ${t.length} lines`);if(c>o.sourceIndex)throw new RangeError(`overlapping chunk at source line ${o.sourceIndex+1} but cursor already at line ${c+1}`);for(let a=c;a<o.sourceIndex;a+=1)r.push(t[a]),i.push({type:"equal",value:t[a],oldLine:a+1,newLine:l}),l+=1;c=o.sourceIndex;for(const a of o.deletedLines)i.push({type:"delete",value:a,oldLine:c+1,newLine:null}),c+=1;for(const a of o.insertedLines)r.push(a),i.push({type:"add",value:a,oldLine:null,newLine:l}),l+=1}for(let o=c;o<t.length;o+=1)r.push(t[o]),i.push({type:"equal",value:t[o],oldLine:o+1,newLine:l}),l+=1;return{text:r.join(`
7
- `),diff:i,source:e}}static buildResult(e,n,t,r){return{text:n,source:e,diff:t.map((i,c)=>({type:r,value:i,oldLine:r==="delete"?c+1:null,newLine:r==="add"?c+1:null}))}}static stripEnvelope(e){return e.filter(n=>{const t=n.trim();return!this.envelopeExact.has(t)&&!this.envelopePrefixes.some(r=>t.startsWith(r))})}static stripLeadingEmpty(e){let n=0;for(;n<e.length&&e[n]==="";)n+=1;return n>0?e.slice(n):e}}module.exports=u;
8
+ `),r=[],i=[];let o=0,l=1;for(const s of n){if(s.sourceIndex>t.length)throw new RangeError(`chunk targets source line ${s.sourceIndex+1} but file only has ${t.length} lines`);if(o>s.sourceIndex)throw new RangeError(`overlapping chunk at source line ${s.sourceIndex+1} but cursor already at line ${o+1}`);for(let a=o;a<s.sourceIndex;a+=1)r.push(t[a]),i.push({type:"equal",value:t[a],oldLine:a+1,newLine:l}),l+=1;o=s.sourceIndex;for(const a of s.deletedLines)i.push({type:"delete",value:a,oldLine:o+1,newLine:null}),o+=1;for(const a of s.insertedLines)r.push(a),i.push({type:"add",value:a,oldLine:null,newLine:l}),l+=1}for(let s=o;s<t.length;s+=1)r.push(t[s]),i.push({type:"equal",value:t[s],oldLine:s+1,newLine:l}),l+=1;return{text:r.join(`
9
+ `),diff:i,source:e}}static buildResult(e,n,t,r){return{text:n,source:e,diff:t.map((i,o)=>({type:r,value:i,oldLine:r==="delete"?o+1:null,newLine:r==="add"?o+1:null}))}}static stripEnvelope(e){return e.filter(n=>{const t=n.trim();return!this.envelopeExact.has(t)&&!this.envelopePrefixes.some(r=>t.startsWith(r))})}static stripLeadingEmpty(e){let n=0;for(;n<e.length&&e[n]==="";)n+=1;return n>0?e.slice(n):e}}module.exports=u;
package/dist/index.mjs CHANGED
@@ -1,7 +1,9 @@
1
- class x{static anchorStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trim(),fuzzScore:1},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:10}];static contextStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trimEnd(),fuzzScore:1},{mapFn:e=>e.trim(),fuzzScore:100},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:1e3}];static unicodeReplacements={"\u2010":"-","\u2011":"-","\u2012":"-","\u2013":"-","\u2014":"-","\u2015":"-","\u2212":"-","\u2018":"'","\u2019":"'","\u201A":"'","\u201B":"'","\u201C":'"',"\u201D":'"',"\u201E":'"',"\u201F":'"',"\xA0":" ","\u2002":" ","\u2003":" ","\u2004":" ","\u2005":" ","\u2006":" ","\u2007":" ","\u2008":" ","\u2009":" ","\u200A":" ","\u202F":" ","\u205F":" ","\u3000":" "};static unicodePattern=new RegExp(`[${Object.keys(this.unicodeReplacements).join("")}]`,"g");static findContext(e,n,t,r){if(r){const i=this.findContextCore(e,n,Math.max(0,e.length-n.length));if(i.matchedIndex!==-1)return i;const o=this.findContextCore(e,n,t);return{matchedIndex:o.matchedIndex,fuzzScore:o.fuzzScore+1e4}}return this.findContextCore(e,n,t)}static resolveAnchor(e,n,t,r){for(const i of this.anchorStrategies){const o=i.mapFn(e);let c=!1;for(let u=0;u<t;u+=1)if(i.mapFn(n[u])===o){c=!0;break}if(c)return t;const s=this.searchLines(n,e,t,i.mapFn);if(s!==-1)return r.fuzzScore+=i.fuzzScore,s}return t}static findContextCore(e,n,t){if(!n.length)return{matchedIndex:t,fuzzScore:0};for(const r of this.contextStrategies)for(let i=t;i<e.length;i+=1)if(this.sliceEquals(e,n,i,r.mapFn))return{matchedIndex:i,fuzzScore:r.fuzzScore};return{matchedIndex:-1,fuzzScore:0}}static normalizeUnicode(e){return e.trim().replace(this.unicodePattern,n=>this.unicodeReplacements[n])}static searchLines(e,n,t,r){const i=r(n);for(let o=t;o<e.length;o+=1)if(r(e[o])===i)return o;return-1}static sliceEquals(e,n,t,r){if(t+n.length>e.length)return!1;for(let i=0;i<n.length;i+=1)if(r(e[t+i])!==r(n[i]))return!1;return!0}}class h{static endFile="*** End of File";static prefixToMode={"+":"add","-":"delete"," ":"keep"};static terminators=["*** End Patch","*** Update File:","*** Delete File:","*** Add File:","*** Move to:"];static normalizeDiffLines(e){const n=e.split(/\r?\n/);return n.at(-1)===""&&n.pop(),n}static parseCreateDiff(e){const n=this.createState(e),t=[];for(;!this.isDone(n,!1);){const r=n.lines[n.currentIndex];if(n.currentIndex+=1,!r.startsWith("+"))throw new SyntaxError(`line ${n.currentIndex} expected '+' prefix but got "${r}"`);t.push(r.slice(1))}return t.join(`
1
+ class x{static unicodeReplacements={"\u2010":"-","\u2011":"-","\u2012":"-","\u2013":"-","\u2014":"-","\u2015":"-","\u2212":"-","\uFF0D":"-","\u2018":"'","\u2019":"'","\u201A":"'","\u201B":"'",\u02BC:"'","\uFF07":"'","\u201C":'"',"\u201D":'"',"\u201E":'"',"\u201F":'"',"\uFF02":'"',"\u2026":"...","\xA0":" ","\u2002":" ","\u2003":" ","\u2004":" ","\u2005":" ","\u2006":" ","\u2007":" ","\u2008":" ","\u2009":" ","\u200A":" ","\u202F":" ","\u205F":" ","\u3000":" "};static unicodePattern=new RegExp(`[${Object.keys(this.unicodeReplacements).join("")}]`,"g");static anchorStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trim(),fuzzScore:1},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:10}];static contextStrategies=[{mapFn:e=>e,fuzzScore:0},{mapFn:e=>e.trimEnd(),fuzzScore:1},{mapFn:this.collapseSpace.bind(this),fuzzScore:50},{mapFn:e=>e.trim(),fuzzScore:100},{mapFn:this.normalizeUnicode.bind(this),fuzzScore:1e3}];static findContext(e,n,t,r){if(r){const i=this.findContextCore(e,n,Math.max(0,e.length-n.length));if(i.matchedIndex!==-1)return i;const o=this.findContextCore(e,n,t);return{matchedIndex:o.matchedIndex,fuzzScore:o.fuzzScore+1e4}}return this.findContextCore(e,n,t)}static resolveAnchor(e,n,t,r){for(const i of this.anchorStrategies){const o=i.mapFn(e);let c=!1;for(let u=0;u<t;u+=1)if(i.mapFn(n[u])===o){c=!0;break}if(c)return t;const s=this.searchLines(n,e,t,i.mapFn);if(s!==-1)return r.fuzzScore+=i.fuzzScore,s}for(const i of this.anchorStrategies){const o=this.searchLines(n,e,t,i.mapFn,!0);if(o!==-1)return r.fuzzScore+=i.fuzzScore+5,o}return t}static collapseSpace(e){return e.replace(/\s+/g," ").trim()}static findContextCore(e,n,t){if(!n.length)return{matchedIndex:t,fuzzScore:0};for(const r of this.contextStrategies)for(let i=t;i<e.length;i+=1)if(this.sliceEquals(e,n,i,r.mapFn))return{matchedIndex:i,fuzzScore:r.fuzzScore};return{matchedIndex:-1,fuzzScore:0}}static normalizeUnicode(e){return e.trim().replace(this.unicodePattern,n=>this.unicodeReplacements[n])}static searchLines(e,n,t,r,i=!1){const o=r(n);if(i&&o.length===0)return-1;for(let c=t;c<e.length;c+=1){const s=r(e[c]);if(i?s.startsWith(o):s===o)return c}return-1}static sliceEquals(e,n,t,r){if(t+n.length>e.length)return!1;for(let i=0;i<n.length;i+=1)if(r(e[t+i])!==r(n[i]))return!1;return!0}}class f{static endFile="*** End of File";static prefixToMode={"+":"add","-":"delete"," ":"keep"};static terminators=["*** End Patch","*** Update File:","*** Delete File:","*** Add File:","*** Move to:"];static normalizeDiffLines(e){const n=e.split(/\r?\n/);return n.at(-1)===""&&n.pop(),n}static parseCreateDiff(e){const n=this.createState(e),t=[];for(;!this.isDone(n,!1);){const r=n.lines[n.currentIndex];if(n.currentIndex+=1,!r.startsWith("+"))throw new SyntaxError(`line ${n.currentIndex} expected '+' prefix but got "${r}"`);t.push(r.slice(1))}return t.join(`
2
2
  `)}static parseUpdateDiff(e,n){const t=this.createState(e),r=n.split(`
3
- `),i=[];let o=0;for(;!this.isDone(t,!0);){const c=this.consumePrefix(t,"@@ "),s=!c&&t.lines[t.currentIndex]==="@@";if(s&&(t.currentIndex+=1),!c&&!s&&o!==0)throw new SyntaxError(`line ${t.currentIndex+1} unexpected line "${t.lines[t.currentIndex]}"`);c?.trim()&&(o=x.resolveAnchor(c,r,o,t));const u=this.readSection(t.lines,t.currentIndex),l=x.findContext(r,u.contextLines,o,u.isEndOfFile);if(l.matchedIndex===-1){const a=u.isEndOfFile?"EOF context":"context";throw new SyntaxError(`line ${t.currentIndex+1} unmatched ${a} at source line ${o+1} "${u.contextLines[0]??""}"`)}t.fuzzScore+=l.fuzzScore;for(const a of u.diffChunks)i.push({...a,sourceIndex:a.sourceIndex+l.matchedIndex});o=l.matchedIndex+u.contextLines.length,t.currentIndex=u.endIndex}return{diffChunks:i,fuzzScore:t.fuzzScore}}static consumePrefix(e,n){return e.lines[e.currentIndex]?.startsWith(n)?(e.currentIndex+=1,e.lines[e.currentIndex-1].slice(n.length)):""}static createState(e){return{lines:[...e,this.terminators[0]],currentIndex:0,fuzzScore:0}}static isDone(e,n){return e.currentIndex>=e.lines.length||this.isTerminator(e.lines[e.currentIndex],n)}static isSectionBoundary(e){return e.startsWith("@@")||this.isTerminator(e,!0)}static isTerminator(e,n){return this.terminators.some(t=>e.startsWith(t))?!0:n&&e.startsWith(this.endFile)}static readSection(e,n){const t=[];let r=[],i=[];const o=[];let c="keep",s=n;for(;s<e.length;){const u=e[s];if(this.isSectionBoundary(u)||u==="***")break;if(u.startsWith("***"))throw new SyntaxError(`line ${s+1} unexpected marker "${u}"`);s+=1;const l=c,a=u||" ",p=this.prefixToMode[a[0]];if(!p)throw new SyntaxError(`line ${s} unexpected line prefix "${a}"`);c=p;const d=a.slice(1);c==="keep"&&l!==c&&(r.length||i.length)&&(o.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),r=[],i=[]),c==="delete"?(r.push(d),t.push(d)):c==="add"?i.push(d):t.push(d)}if((r.length||i.length)&&o.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),s<e.length&&e[s]===this.endFile)return{contextLines:t,diffChunks:o,endIndex:s+1,isEndOfFile:!0};if(s===n)throw new SyntaxError(`line ${s+1} empty section, expected content but got "${e[s]}"`);return{contextLines:t,diffChunks:o,endIndex:s,isEndOfFile:!1}}}class m{static envelopeExact=new Set(["*** Begin Patch","*** End Patch","\"]);static envelopePrefixes=["*** Add File:","*** Delete File:","*** Move to:","*** Update File:","--- a/","--- a\\","+++ b/","+++ b\\"];static apply(e,n,t="update"){if(t==="delete")return this.buildResult(e,"",e.split(`
4
- `),"delete");const r=this.stripLeadingEmpty(this.stripEnvelope(h.normalizeDiffLines(n)));if(t==="create"){const i=h.parseCreateDiff(r);return this.buildResult(e,i,i.split(`
5
- `),"add")}return this.applyChunks(e,h.parseUpdateDiff(r,e).diffChunks)}static applyChunks(e,n){const t=e.split(`
3
+ `),i=[];let o=0;for(;!this.isDone(t,!0);){const c=this.consumePrefix(t,"@@ "),s=!c&&t.lines[t.currentIndex]==="@@";if(s&&(t.currentIndex+=1),!c&&!s&&o!==0)throw new SyntaxError(`line ${t.currentIndex+1} unexpected line "${t.lines[t.currentIndex]}"`);c?.trim()&&(o=x.resolveAnchor(c,r,o,t));const u=this.readSection(t.lines,t.currentIndex),l=x.findContext(r,u.contextLines,o,u.isEndOfFile);if(l.matchedIndex===-1){const a=u.isEndOfFile?"EOF context":"context",h=u.contextLines[0]??"",d=r[o]??"(end of file)";throw new SyntaxError(`line ${t.currentIndex+1} unmatched ${a} at source line ${o+1} expected "${h}" but found "${d}"`)}t.fuzzScore+=l.fuzzScore;for(const a of u.diffChunks)i.push({...a,sourceIndex:a.sourceIndex+l.matchedIndex});o=l.matchedIndex+u.contextLines.length,t.currentIndex=u.endIndex}return{diffChunks:i,fuzzScore:t.fuzzScore}}static consumePrefix(e,n){return e.lines[e.currentIndex]?.startsWith(n)?(e.currentIndex+=1,e.lines[e.currentIndex-1].slice(n.length)):""}static createState(e){return{lines:[...e,this.terminators[0]],currentIndex:0,fuzzScore:0}}static isDone(e,n){return e.currentIndex>=e.lines.length||this.isTerminator(e.lines[e.currentIndex],n)}static isSectionBoundary(e){return e.startsWith("@@")||this.isTerminator(e,!0)}static isTerminator(e,n){return this.terminators.some(t=>e.startsWith(t))?!0:n&&e.startsWith(this.endFile)}static readSection(e,n){const t=[];let r=[],i=[];const o=[];let c="keep",s=n;for(;s<e.length;){const u=e[s];if(this.isSectionBoundary(u)||u==="***")break;if(u.startsWith("***"))throw new SyntaxError(`line ${s+1} unexpected marker "${u}"`);s+=1;const l=c,a=u||" ",h=this.prefixToMode[a[0]];if(!h)throw new SyntaxError(`line ${s} unexpected line prefix "${a}"`);c=h;const d=a.slice(1);c==="keep"&&l!==c&&(r.length||i.length)&&(o.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),r=[],i=[]),c==="delete"?(r.push(d),t.push(d)):c==="add"?i.push(d):t.push(d)}if((r.length||i.length)&&o.push({sourceIndex:t.length-r.length,deletedLines:r,insertedLines:i}),s<e.length&&e[s]===this.endFile)return{contextLines:t,diffChunks:o,endIndex:s+1,isEndOfFile:!0};if(s===n)throw new SyntaxError(`line ${s+1} empty section, expected content but got "${e[s]}"`);return{contextLines:t,diffChunks:o,endIndex:s,isEndOfFile:!1}}}class m{static envelopeExact=new Set(["*** Begin Patch","*** End Patch","\"]);static envelopePrefixes=["*** Add File:","*** Delete File:","*** Move to:","*** Update File:","--- a/","--- a\\","+++ b/","+++ b\\"];static apply(e,n,t="update"){if(e=(e??"").replaceAll(`\r
4
+ `,`
5
+ `),n=n??"",t==="delete")return this.buildResult(e,"",e.split(`
6
+ `),"delete");const r=this.stripLeadingEmpty(this.stripEnvelope(f.normalizeDiffLines(n)));if(t==="create"){const i=f.parseCreateDiff(r);return this.buildResult(e,i,i.split(`
7
+ `),"add")}return this.applyChunks(e,f.parseUpdateDiff(r,e).diffChunks)}static applyChunks(e,n){const t=e.split(`
6
8
  `),r=[],i=[];let o=0,c=1;for(const s of n){if(s.sourceIndex>t.length)throw new RangeError(`chunk targets source line ${s.sourceIndex+1} but file only has ${t.length} lines`);if(o>s.sourceIndex)throw new RangeError(`overlapping chunk at source line ${s.sourceIndex+1} but cursor already at line ${o+1}`);for(let u=o;u<s.sourceIndex;u+=1)r.push(t[u]),i.push({type:"equal",value:t[u],oldLine:u+1,newLine:c}),c+=1;o=s.sourceIndex;for(const u of s.deletedLines)i.push({type:"delete",value:u,oldLine:o+1,newLine:null}),o+=1;for(const u of s.insertedLines)r.push(u),i.push({type:"add",value:u,oldLine:null,newLine:c}),c+=1}for(let s=o;s<t.length;s+=1)r.push(t[s]),i.push({type:"equal",value:t[s],oldLine:s+1,newLine:c}),c+=1;return{text:r.join(`
7
9
  `),diff:i,source:e}}static buildResult(e,n,t,r){return{text:n,source:e,diff:t.map((i,o)=>({type:r,value:i,oldLine:r==="delete"?o+1:null,newLine:r==="add"?o+1:null}))}}static stripEnvelope(e){return e.filter(n=>{const t=n.trim();return!this.envelopeExact.has(t)&&!this.envelopePrefixes.some(r=>t.startsWith(r))})}static stripLeadingEmpty(e){let n=0;for(;n<e.length&&e[n]==="";)n+=1;return n>0?e.slice(n):e}}export{m as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neabyte/v4a-diff",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Apply context-anchored file patches from LLM tool calls",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",