@feng0531/dsh-vcs 0.3.106 → 0.4.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/client.js +3 -3
- package/dist/frontend.json +1 -1
- package/dist/interface.js +3 -3
- package/dist/svn-host.mjs +1 -1
- package/dist/updates.mjs +1 -0
- package/docs/RELEASING.md +2 -0
- package/index.mjs +11 -1
- package/package.json +1 -1
- package/vcs.mjs +7 -7
package/dist/svn-host.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHandler as Ie}from"../index.mjs";import{XMLParser as he,XMLValidator as me}from"fast-xml-parser";function O(r){if(typeof r!="string"||Buffer.byteLength(r)>2097152||/<!DOCTYPE|<!ENTITY/i.test(r)||me.validate(r)!==!0)throw Error("Invalid DAV XML");let t=new he({preserveOrder:!0,ignoreAttributes:!1,parseTagValue:!1,parseAttributeValue:!1,trimValues:!1}).parse(r),o=0,e=(s,n,i=0)=>{if(i>32)throw Error("DAV XML too deep");let a=Object.create(null),f="";for(let c of s){if(++o>1e4)throw Error("DAV XML too many nodes");if(Object.hasOwn(c,"#text")){f+=c["#text"];continue}let p=Object.keys(c).filter(b=>b!==":@");if(p.length!==1)throw Error("Invalid DAV XML node");let l=p[0];if(l==="?xml"){if(i!==0)throw Error("Unexpected XML declaration");continue}let m={...n},w={};for(let[b,V]of Object.entries(c[":@"]||{}))b==="@_xmlns"?m[""]=V:b.startsWith("@_xmlns:")?m[b.slice(8)]=V:w[b]=V;let y=l.split(":");if(y.length>2)throw Error("Invalid DAV XML name");let u=y.length===2?y[0]:"",d=y.at(-1),h=m[u],g=h==="DAV:"?"D:"+d:h==="http://subversion.tigris.org/xmlns/dav/"?"S:"+d:h==="svn:"?"L:"+d:h==="http://subversion.tigris.org/xmlns/svn/"?"P:"+d:h==="http://subversion.tigris.org/xmlns/custom/"?"C:"+d:null;if(!g)throw Error("Unsupported DAV namespace");let D=["L:added-path","L:deleted-path","L:modified-path","L:replaced-path"].includes(g);if(Object.keys(w).length&&(!D||Object.keys(w).some(b=>!["@_node-kind","@_text-mods","@_prop-mods","@_copyfrom-path","@_copyfrom-rev"].includes(b))))throw Error("Unexpected DAV attribute");let v=e(c[l],m,i+1);if(D&&typeof v!="string")throw Error("Invalid DAV path text");let E=D?{...w,"#text":v}:v;if(["D:response","D:propstat","L:log-item","L:added-path","L:deleted-path","L:modified-path","L:replaced-path"].includes(g))(a[g]??=[]).push(E);else{if(Object.hasOwn(a,g))throw Error("Duplicate DAV expanded name");a[g]=E}}if(!Object.keys(a).length)return f;if(f.trim())throw Error("Mixed DAV XML content");return a};return e(t,{})}var ve=9223372036854775807n;function S(r){if(typeof r!="string"||r.length>19||!/^(0|[1-9][0-9]*)$/.test(r))throw new Error("Invalid SVN revision");let t=BigInt(r);if(t>ve)throw new Error("SVN revision exceeds supported range");return t}function H(r){let t=S(r);return t===0n?null:String(t-1n)}function j(r,{snapshot:t,cursor:o=t,limit:e=50}={}){let s=S(t),n=S(o);if(n>s||!Number.isInteger(e)||e<1||e>100||!Array.isArray(r)||r.length>e+1)throw new Error("Invalid SVN page bounds");let i=n+1n;for(let f of r){let c=S(f);if(c>n||c>=i)throw new Error("SVN revisions must descend within cursor");i=c}let a=r.slice(0,e);return{snapshot:t,revisions:a,nextRevision:r.length>e?H(a.at(-1)):null}}function J(r,t){let o=O(r);if(!o||typeof o!="object"||Object.keys(o).length!==1||!Object.hasOwn(o,"L:log-report"))throw Error("Invalid DAV log root");let e=o["L:log-report"],s;if(e==="")s=[];else{if(!e||typeof e!="object"||Object.keys(e).some(a=>a!=="L:log-item")||!Array.isArray(e["L:log-item"]))throw Error("Invalid DAV log structure");s=e["L:log-item"]}if(s.length>101)throw Error("DAV log entries exceed limit");let n=s.map(a=>{if(!a||typeof a!="object"||Array.isArray(a)||Object.keys(a).some(c=>!["D:version-name","D:comment","D:creator-displayname","L:date"].includes(c)))throw Error("Unsupported DAV log item");let f=c=>{let p=a[c]??"";if(typeof p!="string")throw Error("Invalid DAV log text");return p};return{revision:f("D:version-name"),message:f("D:comment"),author:f("D:creator-displayname"),date:f("L:date")}}),i=j(n.map(a=>a.revision),t);return{snapshot:i.snapshot,entries:n.slice(0,i.revisions.length),nextRevision:i.nextRevision}}function k(r){if(typeof r!="string"||r.length>32768||!r.startsWith("/")||/[\u0000-\u001f\u007f\\]/.test(r))throw new Error("Invalid SVN repository path");if(r!=="/"&&r.slice(1).split("/").some(t=>t===""||t==="."||t===".."))throw new Error("Noncanonical SVN repository path");return r}function P(r,t){return k(r),k(t),t==="/"||r===t||r.startsWith(t+"/")}function q(r,t){if(!Array.isArray(r)||r.length>1e4)throw new Error("Invalid SVN path nodes");let o=r.map(e=>{let s=["#text","@_action","@_kind","@_copyfrom-path","@_copyfrom-rev","@_text-mods","@_prop-mods"];if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(n=>!s.includes(n)))throw new Error("Invalid SVN path node");for(let n of["@_text-mods","@_prop-mods"])if(Object.hasOwn(e,n)&&!["true","false","unknown"].includes(e[n]))throw new Error("Invalid SVN modification flag");return{...Object.hasOwn(e,"@_text-mods")?{textModified:e["@_text-mods"]}:{},...Object.hasOwn(e,"@_prop-mods")?{propertiesModified:e["@_prop-mods"]}:{},path:e["#text"],action:e["@_action"],kind:e["@_kind"],...Object.hasOwn(e,"@_copyfrom-path")?{copyFromPath:e["@_copyfrom-path"]}:{},...Object.hasOwn(e,"@_copyfrom-rev")?{copyFromRevision:e["@_copyfrom-rev"]}:{}}});return $(o,t)}function $(r,{scope:t,revision:o}={}){k(t);let e=S(o);if(!Array.isArray(r)||r.length>1e4||e===0n&&r.length!==0)throw new Error("Invalid SVN changed paths");let s=new Set;return r.map(i=>{if(!i||typeof i!="object"||Array.isArray(i)||Object.keys(i).some(u=>!["path","action","kind","copyFromPath","copyFromRevision","copySourceOutsideScope","textModified","propertiesModified"].includes(u)))throw new Error("Invalid SVN change structure");let{path:a,action:f,kind:c,copyFromPath:p,copyFromRevision:l}=i;if(k(a),s.has(a)||!["A","D","M","R"].includes(f)||!["file","dir","unknown"].includes(c))throw new Error("Invalid SVN change");s.add(a);let m=p!==void 0||l!==void 0;if(m&&(k(p),!["A","R"].includes(f)||S(l)>=e))throw new Error("Invalid SVN copy source");let w=Object.hasOwn(i,"copySourceOutsideScope");if(w&&(i.copySourceOutsideScope!==!0||m||!["A","R"].includes(f)))throw new Error("Invalid SVN redacted copy source");let y={path:a,action:f,kind:c,...w?{copySourceOutsideScope:!0}:{}};for(let u of["textModified","propertiesModified"])if(Object.hasOwn(i,u)){if(!["true","false","unknown"].includes(i[u]))throw new Error("Invalid SVN modification flag");y[u]=i[u]}return m&&(P(p,t)?(y.copyFromPath=p,y.copyFromRevision=l):y.copySourceOutsideScope=!0),y}).filter(i=>P(i.path,t))}function G(r,{scope:t,revision:o}={}){S(o);let e=O(r),s=(p,l)=>{if(!p||typeof p!="object"||Array.isArray(p)||Object.keys(p).some(m=>!l.includes(m)))throw Error("Invalid DAV detail structure")};s(e,["L:log-report"]);let n=e["L:log-report"];if(s(n,["L:log-item"]),n["L:log-item"]?.length!==1)throw Error("One DAV detail revision required");let i=n["L:log-item"][0],a={"L:added-path":"A","L:deleted-path":"D","L:modified-path":"M","L:replaced-path":"R"};if(s(i,["D:version-name","D:comment","D:creator-displayname","L:date",...Object.keys(a)]),i["D:version-name"]!==o)throw Error("DAV detail revision mismatch");let f=p=>{let l=i[p]??"";if(typeof l!="string")throw Error("Invalid DAV detail text");return l},c=[];for(let[p,l]of Object.entries(a))for(let m of i[p]??[]){let{"@_node-kind":w,...y}=m;c.push({...y,"@_kind":w??"unknown","@_action":l})}return{revision:o,author:f("D:creator-displayname"),date:f("L:date"),message:f("D:comment"),changes:q(c,{scope:t,revision:o})}}function I(r,t,o){if(k(t),S(o),typeof r!="string"||r.length>32768||/[\x00-\x20\x7f\\?#]/.test(r))throw new Error("Invalid SVN HTTPS root");let e=new URL(r);if(e.protocol!=="https:"||e.username||e.password||e.href!==r)throw new Error("Noncanonical SVN HTTPS root");let s=e.pathname.split("/").slice(1);s.at(-1)===""&&s.pop();for(let f of s){let c=decodeURIComponent(f);if(!f||c==="."||c===".."||/[\x00-\x1f\x7f/\\]/.test(c))throw new Error("Invalid SVN root path")}let n=r.endsWith("/")?r.slice(0,-1):r,i=t==="/"?"":t.split("/").map(f=>encodeURIComponent(f).replace(/[!'()*]/g,c=>"%"+c.charCodeAt(0).toString(16).toUpperCase())).join("/"),a=n+(i||(e.pathname==="/"?"/":""))+"@"+o;if(a.length>32768)throw new Error("SVN target too long");return a}function B({root:r,scope:t,path:o=t,snapshot:e,revision:s}={}){if(S(s)>S(e))throw new Error("SVN selected revision exceeds snapshot");if(!P(o,t))throw new Error("SVN detail path outside scope");let n=I(r,o,e);return{scope:t,revision:s,snapshot:e,args:["log","--xml","--verbose","--stop-on-copy","--non-interactive","--no-auth-cache","--limit","1","-r",s+":"+s,"--",n]}}function M({root:r,scope:t,path:o=t,snapshot:e,cursor:s=e,limit:n=50}={}){if(j([],{snapshot:e,cursor:s,limit:n}),!P(o,t))throw new Error("SVN history path outside scope");let i=I(r,o,e);return{snapshot:e,cursor:s,limit:n,args:["log","--xml","--stop-on-copy","--non-interactive","--no-auth-cache","--limit",String(n+1),"-r",s+":0","--",i]}}function C(r,t){let o=r?.args;if(!Array.isArray(o)||o.length>16||o.some(l=>typeof l!="string"))throw Error("Invalid SVN command");let e=o.at(-1),s=e.lastIndexOf("@"),n=e.slice(s+1);S(n);let i=t.root.endsWith("/")?t.root.slice(0,-1):t.root;I(t.root,"/",n);let a=e.slice(0,s);if(!(a===i||a.startsWith(i+"/")))throw Error("SVN command target outside root");let f=a.slice(i.length),c=f?"/"+f.slice(1).split("/").map(l=>decodeURIComponent(l)).join("/"):"/";if(!P(c,t.scope)||I(t.root,c,n)!==e)throw Error("SVN command target outside scope or noncanonical");let p;if(o[0]==="cat"||o[0]==="proplist"){S(o[o[0]==="cat"?4:5]);let l=o[o[0]==="cat"?4:5];if(l!==n)throw Error("SVN content operative and peg mismatch");p=o[0]==="cat"?["cat","--non-interactive","--no-auth-cache","-r",l,"--",e]:["proplist","--xml","--non-interactive","--no-auth-cache","-r",l,"--",e]}else if(o[0]==="log")if(o.includes("--verbose")){let l=o[9]?.split(":")[0];p=B({...t,path:c,snapshot:n,revision:l}).args}else{let l=o[8]?.split(":")[0],m=Number(o[6])-1;p=M({...t,path:c,snapshot:n,cursor:l,limit:m}).args}else throw Error("SVN command is not readonly history");if(JSON.stringify(o)!==JSON.stringify(p))throw Error("Unsupported SVN command shape");return{path:c,pegRevision:n,args:[...p]}}function K(r,{scope:t,revision:o,path:e}={}){if(S(o)===0n)throw new Error("SVN r0 has no change comparison");let s=$(r,{scope:t,revision:o}).find(a=>a.path===e);if(!s)throw new Error("SVN path is not an authorized changed member");if(s.kind!=="file")throw new Error("Only SVN file changes can be compared");let n=H(o),i=a=>a===null?{empty:!0}:{empty:!1,path:s.path,revision:a,pegRevision:a};return{path:s.path,action:s.action,left:i(s.action==="A"?null:n),right:i(s.action==="D"?null:o)}}import{XMLParser as ge,XMLValidator as we}from"fast-xml-parser";function Q(r,{target:t,allowEmpty:o=!1}={}){if(typeof t!="string"||!t||t.length>32768)throw new Error("Invalid SVN property target");if(typeof r!="string"||Buffer.byteLength(r,"utf8")>2*1024*1024||/<!DOCTYPE|<!ENTITY/i.test(r)||we.validate(r)!==!0)throw new Error("Invalid SVN property XML");let e=new ge({ignoreAttributes:!1,parseAttributeValue:!1,parseTagValue:!1,trimValues:!1,isArray:p=>p==="target"||p==="property"}).parse(r),s=(p,l)=>{if(!p||typeof p!="object"||Array.isArray(p)||Object.keys(p).some(m=>!l.includes(m)&&!(m==="#text"&&typeof p[m]=="string"&&/^[ \t\r\n]*$/.test(p[m]))))throw new Error("Invalid SVN property structure")};if(s(e,["?xml","properties"]),o===!0&&typeof e.properties=="string"&&/^[ \t\r\n]*$/.test(e.properties))return{target:t,names:[],special:!1};s(e.properties,["target"]);let n=e.properties.target;if(!Array.isArray(n)||n.length!==1)throw new Error("Expected one SVN property target");let i=n[0];if(s(i,["@_path","property"]),i["@_path"]!==t)throw new Error("SVN property target mismatch");let a=i.property??[];if(!Array.isArray(a)||a.length>1e4)throw new Error("Too many SVN properties");let f=[],c=new Set;for(let p of a){s(p,["@_name"]);let l=p["@_name"];if(typeof l!="string"||!l||l.length>1024||/[\x00-\x20\x7f]/.test(l)||c.has(l))throw new Error("Invalid SVN property name");c.add(l),f.push(l)}return{target:t,names:f,special:c.has("svn:special")}}import{createHash as Ve}from"node:crypto";import{XMLParser as ye,XMLValidator as Se}from"fast-xml-parser";function Z(r,{scope:t,revision:o}={}){if(S(o),typeof r!="string"||Buffer.byteLength(r,"utf8")>2*1024*1024||/<!DOCTYPE|<!ENTITY/i.test(r)||Se.validate(r)!==!0)throw new Error("Invalid SVN detail XML");let e=new ye({ignoreAttributes:!1,parseAttributeValue:!1,parseTagValue:!1,trimValues:!1,isArray:c=>c==="logentry"||c==="path"}).parse(r),s=(c,p)=>{if(!c||typeof c!="object"||Array.isArray(c)||Object.keys(c).some(l=>!p.includes(l)&&!(l==="#text"&&typeof c[l]=="string"&&/^[ \t\r\n]*$/.test(c[l]))))throw new Error("Invalid SVN detail structure")};if(s(e,["?xml","log"]),s(e.log,["logentry"]),!Array.isArray(e.log.logentry)||e.log.logentry.length!==1)throw new Error("Expected one SVN revision");let n=e.log.logentry[0];if(s(n,["@_revision","author","date","msg","paths"]),n["@_revision"]!==o)throw new Error("SVN detail revision mismatch");let i=c=>{let p=n[c]??"";if(typeof p!="string")throw new Error("Invalid SVN detail text");return p},a=[];n.paths!==void 0&&!(typeof n.paths=="string"&&/^[ \t\r\n]*$/.test(n.paths))&&(s(n.paths,["path"]),a=n.paths.path??[]);let f=q(a,{scope:t,revision:o});return{revision:o,author:i("author"),date:i("date"),message:i("msg"),pathsAvailable:Object.hasOwn(n,"paths"),changes:f}}import{randomBytes as De}from"node:crypto";function ee(r){if(!r||typeof r!="object")throw Error("Invalid SVN consent identity");let{sessionId:t,cwd:o,repositoryId:e,root:s,uuid:n,scope:i}=r;for(let a of[t,o,e])if(typeof a!="string"||!a||a.length>32768||a.includes("\0"))throw Error("Invalid SVN consent scope");if(I(s,"/","0"),k(i),typeof n!="string"||!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(n))throw Error("Invalid SVN repository UUID");return JSON.stringify([t,o,e,s,n,i])}function W({now:r=Date.now,ttlMs:t=3e5,maxEntries:o=32,onRevoke:e=()=>{}}={}){if(!Number.isInteger(t)||t<1||t>3e5||!Number.isInteger(o)||o<1||o>32)throw Error("Invalid SVN consent bounds");if(typeof e!="function")throw Error("Invalid SVN revoke listener");let s=new Map,n=a=>{s.delete(a)&&e(a)},i=()=>{for(let[a,f]of s)f.expires<=r()&&n(a)};return{grant(a,{explicit:f=!1}={}){if(f!==!0)throw Error("Explicit SVN network consent required");let c=ee(a);i();for(let[l,m]of s)m.key===c&&n(l);for(;s.size>=o;)n(s.keys().next().value);let p=De(32).toString("hex");return s.set(p,{key:c,sessionId:a.sessionId,expires:r()+t}),p},assert(a,f){i();let c=ee(f),p=s.get(a);if(!p||p.key!==c)throw Object.assign(Error("SVN network consent missing, expired or identity changed"),{code:"vcs/svn-consent-required"})},revokeSession(a){for(let[f,c]of s)c.sessionId===a&&n(f)},revoke(a){n(a)},clear(){for(let a of[...s.keys()])n(a)}}}import{XMLParser as be,XMLValidator as Ee}from"fast-xml-parser";function re(r,t){let o=Ae(r),e=j(o.map(s=>s.revision),t);return{snapshot:e.snapshot,entries:o.slice(0,e.revisions.length),nextRevision:e.nextRevision}}function Ae(r){if(typeof r!="string"||Buffer.byteLength(r,"utf8")>2*1024*1024||/<!DOCTYPE|<!ENTITY/i.test(r)||Ee.validate(r)!==!0)throw new Error("Invalid SVN log XML");let o=new be({ignoreAttributes:!1,parseAttributeValue:!1,parseTagValue:!1,trimValues:!1,isArray:n=>n==="logentry"}).parse(r);if(!Object.hasOwn(o,"log")||Object.keys(o).some(n=>n!=="log"&&n!=="?xml"))throw new Error("Invalid SVN log root");let e=o.log;if(typeof e=="string"&&/^[ \t\r\n]*$/.test(e))return[];if(e&&typeof e=="object"&&typeof e["#text"]=="string"&&/^[ \t\r\n]*$/.test(e["#text"])&&delete e["#text"],!e||typeof e!="object"||Object.keys(e).some(n=>n!=="logentry")||!Array.isArray(e.logentry)||e.logentry.length>101)throw new Error("Invalid SVN log entries");let s=new Set;return e.logentry.map(n=>{if(!n||typeof n!="object"||Array.isArray(n)||Object.keys(n).some(f=>!["@_revision","author","date","msg","#text"].includes(f))||"#text"in n&&(typeof n["#text"]!="string"||!/^[ \t\r\n]*$/.test(n["#text"])))throw new Error("Unsupported SVN log entry structure");let i=n["@_revision"];if(S(i),s.has(i))throw new Error("Duplicate SVN revision");s.add(i);let a=f=>{let c=n[f]??"";if(typeof c!="string")throw new Error("Invalid SVN log text");return c};return{revision:i,author:a("author"),date:a("date"),message:a("msg")}})}function te({resolveIdentity:r,transport:t,now:o=Date.now,timeoutMs:e=15e3,transportFormat:s="cli"}={}){if(!["cli","dav"].includes(s))throw Error("Invalid SVN transport format");if(!Number.isInteger(e)||e<1||e>15e3)throw Error("Invalid SVN runtime deadline");if(typeof r!="function")throw Error("Local identity resolver required");let n=W({now:o}),i=W({now:o,onRevoke:u=>m(u)}),a=!1,f=()=>{if(a)throw new DOMException("SVN runtime disposed","AbortError")},c=async(u,d)=>{f(),d?.throwIfAborted();let h=await r(u,d);return f(),d?.throwIfAborted(),{...h,sessionId:u.sessionId,cwd:h.cwd,repositoryId:u.repositoryId}},p=u=>u.revision===void 0?{}:(S(u.revision),{snapshot:u.revision}),l=new Map,m=u=>{for(let d of l.get(u)||[])d.controller.abort(new DOMException("SVN consent revoked","AbortError"))},w=async(u,d)=>{f(),C(u,d.identity),d.signal?.throwIfAborted(),i.assert(d.token,d.identity);let h=new AbortController,g=()=>h.abort(d.signal.reason);d.signal?.addEventListener("abort",g,{once:!0});let D={controller:h,sessionId:d.identity.sessionId};l.has(d.token)||l.set(d.token,new Set),l.get(d.token).add(D);let v=setTimeout(()=>h.abort(new DOMException("SVN transport deadline exceeded","TimeoutError")),e);try{let E=async()=>{h.signal.throwIfAborted();let V=await c({sessionId:d.identity.sessionId,repositoryId:d.identity.repositoryId},h.signal);i.assert(d.token,V),h.signal.throwIfAborted()},b=await t(u,{...d,timeoutMs:e,signal:h.signal,assertAuthorized:E});if(h.signal.throwIfAborted(),!(typeof b=="string"||Buffer.isBuffer(b))||Buffer.byteLength(b)>d.maxBytes)throw Error("SVN transport output exceeds byte limit or has invalid type");return Buffer.isBuffer(b)&&u.args[0]!=="cat"?new TextDecoder("utf-8",{fatal:!0}).decode(b):b}catch(E){throw h.signal.aborted?h.signal.reason:E}finally{let E=l.get(d.token);E?.delete(D),E?.size||l.delete(d.token),clearTimeout(v),d.signal?.removeEventListener("abort",g)}},y={async describe(u,{signal:d}={}){let h=await c(u,d),g=p(h),D=n.grant(h,{explicit:!0});return{...g,offer:D,root:h.root,uuid:h.uuid,scope:h.scope,origin:new URL(h.root).origin,expiresAt:o()+3e5,remoteEnabled:typeof t=="function"}},async approve(u,{offer:d,explicit:h=!1,signal:g}={}){if(h!==!0)throw Error("Explicit SVN network consent required");let D=await c(u,g);if(n.assert(d,D),typeof t!="function")throw Error("SVN remote transport disabled");let v=p(D),E=i.grant(D,{explicit:!0});return n.revoke(d),{...v,token:E,expiresAt:o()+3e5}},async log(u,{token:d,snapshot:h,cursor:g,limit:D,path:v,signal:E}={}){let b=await c(u,E);if(i.assert(d,b),typeof t!="function")throw Error("SVN remote transport disabled");let V=M({...b,snapshot:h,cursor:g,limit:D,path:v});E?.throwIfAborted();let A=await w(V,{identity:b,token:d,signal:E,maxBytes:2097152,timeoutMs:15e3});E?.throwIfAborted();let x=await c(u,E);return i.assert(d,x),(s==="dav"?J:re)(A,V)},async detail(u,{token:d,snapshot:h,revision:g,signal:D}={}){let v=await c(u,D);if(i.assert(d,v),typeof t!="function")throw Error("SVN remote transport disabled");let E=B({...v,snapshot:h,revision:g}),b=await w(E,{identity:v,token:d,signal:D,maxBytes:2097152,timeoutMs:15e3});D?.throwIfAborted(),i.assert(d,await c(u,D));let V=s==="dav"?{...G(b,E),pathsAvailable:!0}:Z(b,E);return{...V,snapshot:h,changes:V.changes.map(A=>({...A,id:Ve("sha256").update(JSON.stringify([v.root,v.uuid,v.scope,h,g,A])).digest("hex")}))}},async compare(u,{token:d,snapshot:h,revision:g,id:D,signal:v}={}){if(typeof D!="string"||!/^[a-f0-9]{64}$/.test(D))throw Error("Invalid SVN change ID");let E=await y.detail(u,{token:d,snapshot:h,revision:g,signal:v}),b=E.changes.find(N=>N.id===D);if(!b)throw Error("SVN change is not selected revision member");let V=await c(u,v);i.assert(d,V);let A=K(E.changes.map(({id:N,..._})=>_),{scope:V.scope,revision:g,path:b.path}),x=async N=>{if(N.empty)return{label:"empty",text:"",absent:!0};let _=await c(u,v);i.assert(d,_);let U=I(_.root,N.path,N.pegRevision),z={identity:_,token:d,signal:v,maxBytes:2097152,timeoutMs:15e3},ue=await w({args:["proplist","--xml","--non-interactive","--no-auth-cache","-r",N.revision,"--",U]},z);if(v?.throwIfAborted(),i.assert(d,await c(u,v)),Q(ue,{target:U.slice(0,U.lastIndexOf("@")),allowEmpty:!0}).special)throw Error("SVN special files cannot be compared");let X=await w({args:["cat","--non-interactive","--no-auth-cache","-r",N.revision,"--",U]},z);v?.throwIfAborted(),i.assert(d,await c(u,v));let F=Buffer.isBuffer(X)?X:Buffer.from(X);if(F.length>2097152)throw Error("SVN text exceeds 2 MiB");if(F.includes(0))return{label:"r"+N.revision,text:"",binary:!0};let Y;try{Y=new TextDecoder("utf-8",{fatal:!0}).decode(F)}catch{return{label:"r"+N.revision,text:"",binary:!0}}return{label:"r"+N.revision,text:Y}},T=await x(A.left),R=await x(A.right);return{path:b.path,revision:g,snapshot:h,left:T,right:R,binary:!!(T.binary||R.binary)}},async trace(u,{token:d,snapshot:h,revision:g,id:D,cursor:v,limit:E=50,signal:b}={}){if(typeof D!="string"||!/^[a-f0-9]{64}$/.test(D))throw Error("Invalid SVN change ID");let V=await y.detail(u,{token:d,snapshot:h,revision:g,signal:b}),A=V.changes.find(R=>R.id===D);if(!A||A.kind!=="file")throw Error("SVN tracing requires selected file member");let x=A.action==="D"?H(g):g;return{...await y.log(u,{token:d,snapshot:x,cursor:v??x,limit:E,path:A.path,signal:b}),selectionSnapshot:h,selectionRevision:g,path:A.path,pegRevision:x,stopOnCopy:!0,copySourceOutsideScope:!!A.copySourceOutsideScope}},async copytrace(u,{token:d,snapshot:h,revision:g,id:D,cursor:v,limit:E=50,signal:b}={}){if(typeof D!="string"||!/^[a-f0-9]{64}$/.test(D))throw Error("Invalid SVN change ID");let V=await y.detail(u,{token:d,snapshot:h,revision:g,signal:b}),A=V.changes.find(R=>R.id===D);if(!A||A.kind!=="file"||A.copySourceOutsideScope||A.copyFromPath===void 0||A.copyFromRevision===void 0)throw Error("Selected file has no authorized copy source");let x=A.copyFromRevision;return{...await y.log(u,{token:d,snapshot:x,cursor:v??x,limit:E,path:A.copyFromPath,signal:b}),selectionSnapshot:h,selectionRevision:g,path:A.copyFromPath,pegRevision:x,sourceForPath:A.path,stopOnCopy:!0}},async revoke(u,{token:d,signal:h}={}){let g=await c(u,h);return i.assert(d,g),i.revoke(d),m(d),{revoked:!0}},dispose(){a=!0,n.clear(),i.clear();for(let u of l.keys())m(u)},revokeSession(u){n.revokeSession(u),i.revokeSession(u);for(let[d,h]of l)[...h].some(g=>g.sessionId===u)&&m(d)}};return y}var oe={revoke:["token"],describe:[],approve:["offer","explicit"],log:["token","snapshot","cursor","limit"],detail:["token","snapshot","revision"],compare:["token","snapshot","revision","id"],trace:["token","snapshot","revision","id","cursor","limit"],copytrace:["token","snapshot","revision","id","cursor","limit"]};function xe(r,t){if(!t||typeof t!="object"||Array.isArray(t))throw Error("Object payload required");if(Object.keys(t).some(o=>!["sessionId","repositoryId",...oe[r]].includes(o)))throw Error("Unknown SVN request field");for(let o of["sessionId","repositoryId"])if(typeof t[o]!="string"||!t[o]||t[o].length>1024||t[o].includes("\0"))throw Error("Invalid SVN Session address");if(r==="approve"&&(t.explicit!==!0||typeof t.offer!="string"||!/^[a-f0-9]{64}$/.test(t.offer)))throw Error("Explicit scoped offer required");if(!["describe","approve"].includes(r)){if(typeof t.token!="string"||!/^[a-f0-9]{64}$/.test(t.token))throw Error("SVN consent token required");if(r!=="revoke"&&S(t.snapshot),t.revision!==void 0&&S(t.revision),["detail","compare","trace","copytrace"].includes(r)&&t.revision===void 0)throw Error("Selected SVN revision required");if(t.cursor!==void 0&&S(t.cursor),t.limit!==void 0&&(!Number.isInteger(t.limit)||t.limit<1||t.limit>100))throw Error("Invalid SVN page size");if(["compare","trace","copytrace"].includes(r)&&(typeof t.id!="string"||!/^[a-f0-9]{64}$/.test(t.id)))throw Error("Selected SVN change ID required")}}function ne(r,{timeoutMs:t=15e3}={}){if(!Number.isInteger(t)||t<1||t>15e3)throw Error("Invalid SVN RPC deadline");let o=0;return async(e,s,n)=>{let i=e.startsWith("vcs/svn-")?e.slice(8):"";if(!Object.hasOwn(oe,i))return{ok:!1,error:{code:"vcs/not-found",message:"Unknown SVN endpoint"}};if(i!=="revoke"&&o>=2)return{ok:!1,error:{code:"vcs/busy",message:"SVN history requests busy"}};i!=="revoke"&&o++;let a=new AbortController,f=()=>a.abort(n.reason);n?.addEventListener("abort",f,{once:!0}),n?.aborted&&f();let c=setTimeout(()=>a.abort(new DOMException("SVN operation deadline exceeded","TimeoutError")),t);try{n?.throwIfAborted(),xe(i,s);let{sessionId:p,repositoryId:l,...m}=s,w=await r[i]({sessionId:p,repositoryId:l},{...m,signal:a.signal});return a.signal.throwIfAborted(),{ok:!0,value:w}}catch(p){let l=n?.aborted||p?.name==="AbortError"?"vcs/cancelled":["vcs/rediscover-required","vcs/svn-consent-required"].includes(p?.code)?p.code:"vcs/operation-failed",m=a.signal.aborted?a.signal.reason:p,w=m instanceof Error?m.message:String(m);return{ok:!1,error:{code:l,message:w}}}finally{clearTimeout(c),n?.removeEventListener("abort",f),i!=="revoke"&&o--}}}var Ne=new Set(["describe","approve","log","detail","compare","trace","copytrace","revoke"]);function se(r,t,{transport:o,transportFormat:e="cli",now:s=Date.now,timeoutMs:n=15e3}={}){let i,a=Ie(r,t,4,{now:s,bindSvnIdentityResolver:p=>{i=p}}),f=te({resolveIdentity:i,transport:o,transportFormat:e,now:s,timeoutMs:n}),c=ne(f,{timeoutMs:n});return{handle(p,l,m){let w=p.startsWith("vcs/svn-")?p.slice(8):"";return Ne.has(w)?c(p,l,m):a(p,l,m)},revokeSession:p=>f.revokeSession(p),dispose:()=>f.dispose()}}import{request as Oe}from"node:https";function ie({request:r=Oe}={}){return async(t,{origin:o,method:e="GET",body:s="",depth:n,label:i,signal:a,maxBytes:f=2097152,timeoutMs:c=15e3}={})=>{let p=new URL(t);if(p.protocol!=="https:"||p.origin!==o||p.username||p.password||p.hash||p.href!==t||!["GET","PROPFIND","REPORT"].includes(e)||typeof s!="string"||Buffer.byteLength(s)>65536||!Number.isInteger(f)||f<1||f>2097152||!Number.isInteger(c)||c<1||c>15e3)throw Error("Invalid bounded HTTPS request");if(n!==void 0&&(e!=="PROPFIND"||n!==0))throw Error("Invalid DAV depth");if(i!==void 0){if(e!=="PROPFIND"||n!==0)throw Error("Invalid DAV label");S(i)}let l={...n===0?{Depth:"0"}:{},...i!==void 0?{Label:i}:{}};return a?.throwIfAborted(),new Promise((m,w)=>{let y,u,d=null,h=null,g=0,D=[],v=A=>{d??=A,u?.destroy(),y?.destroy()},E=()=>v(a.reason??new DOMException("HTTPS cancelled","AbortError")),b=setTimeout(()=>v(new DOMException("HTTPS deadline exceeded","TimeoutError")),c),V=()=>{clearTimeout(b),a?.removeEventListener("abort",E)};try{y=r(p,{method:e,agent:!1,rejectUnauthorized:!0,maxHeaderSize:16384,headers:{...l,"Accept-Encoding":"identity","Content-Type":"text/xml; charset=utf-8","Content-Length":String(Buffer.byteLength(s))}},A=>{u=A;let x=u.statusCode??0;if(x>=300&&x<400){v(Error("HTTPS redirects forbidden"));return}if(x<200||x>=300){v(Error("HTTPS status "+x));return}if(u.headers["content-encoding"]&&u.headers["content-encoding"]!=="identity"){v(Error("HTTPS compressed responses forbidden"));return}let T=u.headers["content-length"];if(T!==void 0&&(!/^[0-9]+$/.test(T)||Number(T)>f)){v(Error("HTTPS response exceeds byte limit"));return}u.on("data",R=>{g+=R.length,g>f?v(Error("HTTPS response exceeds byte limit")):d||D.push(R)}),u.on("error",v),u.on("end",()=>{if(!u.complete){v(Error("HTTPS response incomplete"));return}h=Buffer.concat(D)})}),y.on("error",A=>{d??=A}),y.once("close",()=>{V(),d?w(d):h!==null?m(h):w(Error("HTTPS response closed before completion"))}),a?.addEventListener("abort",E,{once:!0}),a?.aborted?E():y.end(s)}catch(A){V(),y?.destroy(),w(A)}})}}var ke=r=>r.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'");function ae(r,t){let o=C(r,t),e=o.args;if(e[0]!=="log")throw Error("DAV log report requires readonly log plan");let s=e.includes("--verbose"),n=e[s?9:8].split(":"),i=e[s?7:6],a=o.path==="/"?"":o.path.slice(1),f='<?xml version="1.0" encoding="utf-8"?><S:log-report xmlns:S="svn:" xmlns:D="DAV:"><S:start-revision>'+n[0]+"</S:start-revision><S:end-revision>"+n[1]+"</S:end-revision><S:limit>"+i+"</S:limit><S:strict-node-history/>"+(s?"<S:discover-changed-paths/>":"")+"<S:path>"+ke(a)+"</S:path><S:revprop>svn:author</S:revprop><S:revprop>svn:date</S:revprop><S:revprop>svn:log</S:revprop></S:log-report>";if(Buffer.byteLength(f)>65536)throw Error("DAV request body exceeds limit");return{method:"REPORT",body:f,pegRevision:o.pegRevision,path:o.path,requiresVerifiedBaseline:!0}}function L(r,{root:t}={}){if(I(t,"/","0"),typeof r!="string"||!r||r.length>32768||/[\\?#]/.test(r)||[...r].some(n=>n.charCodeAt(0)<=32||n.charCodeAt(0)===127))throw Error("Invalid DAV href");if(!(r.startsWith("/")&&!r.startsWith("//"))&&!r.startsWith("https://"))throw Error("DAV href must be absolute path or HTTPS URL");let o=new URL(r,new URL(t).origin);if(o.protocol!=="https:"||o.origin!==new URL(t).origin||o.username||o.password||o.search||o.hash)throw Error("DAV href outside authorized origin");if(r.startsWith("https://")&&o.href!==r||r.startsWith("/")&&o.pathname!==r)throw Error("Noncanonical DAV href");let e=o.pathname.split("/").slice(1);e.at(-1)===""&&e.pop();for(let n of e){let i=decodeURIComponent(n);if(!n||i==="."||i===".."||[...i].some(a=>a.charCodeAt(0)<32||a.charCodeAt(0)===127||a==="/"||a.charCodeAt(0)===92))throw Error("Unsafe DAV href path")}let s=new URL(t).pathname.replace(/[/]$/,"");if(s&&o.pathname!==s&&!o.pathname.startsWith(s+"/"))throw Error("DAV href outside repository root");return o.href}function ce(r,{root:t,uuid:o,target:e}={}){let s=O(r),n=(l,m)=>{if(!l||typeof l!="object"||Array.isArray(l)||Object.keys(l).some(w=>!m.includes(w)&&!(w==="#text"&&typeof l[w]=="string"&&l[w].trim()==="")))throw Error("Unsupported DAV discovery structure")};n(s,["?xml","D:multistatus"]);let i=s["D:multistatus"];if(n(i,["D:response"]),!Array.isArray(i["D:response"])||i["D:response"].length!==1)throw Error("DAV discovery requires one response");let a=i["D:response"][0];if(n(a,["D:href","D:propstat"]),L(a["D:href"],{root:t})!==e)throw Error("DAV response target mismatch");if(!Array.isArray(a["D:propstat"])||a["D:propstat"].length!==1)throw Error("DAV discovery requires one property status");let f=a["D:propstat"][0];if(n(f,["D:prop","D:status"]),!/^HTTP[/]1[.][01] 200(?: OK)?$/.test(f["D:status"]))throw Error("DAV discovery properties unavailable");let c=f["D:prop"];if(n(c,["S:repository-uuid","D:version-controlled-configuration"]),c["S:repository-uuid"]!==o||typeof o!="string"||!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(o))throw Error("DAV UUID does not match local identity");let p=c["D:version-controlled-configuration"];return n(p,["D:href"]),{uuid:o,vcc:L(p["D:href"],{root:t}),target:e}}function pe(r,{root:t,revision:o}={}){S(o);let e=O(r),s=(l,m)=>{if(!l||typeof l!="object"||Array.isArray(l)||Object.keys(l).some(w=>!m.includes(w)))throw Error("Invalid DAV baseline structure")};s(e,["D:multistatus"]);let n=e["D:multistatus"];if(s(n,["D:response"]),n["D:response"]?.length!==1)throw Error("One DAV baseline response required");let i=n["D:response"][0];s(i,["D:href","D:propstat"]);let a=L(i["D:href"],{root:t});if(i["D:propstat"]?.length!==1)throw Error("One DAV baseline status required");let f=i["D:propstat"][0];if(s(f,["D:status","D:prop"]),f["D:status"]!=="HTTP/1.1 200 OK")throw Error("DAV baseline status unavailable");let c=f["D:prop"];if(s(c,["D:version-name","D:baseline-collection"]),c["D:version-name"]!==o)throw Error("DAV baseline revision mismatch");s(c["D:baseline-collection"],["D:href"]);let p=L(c["D:baseline-collection"]["D:href"],{root:t});if(!p.endsWith("/"))throw Error("DAV baseline collection must end in slash");return{revision:o,baseline:a,collection:p}}function fe({request:r,assertAuthorized:t}={}){if(typeof r!="function"||typeof t!="function")throw Error("DAV chain requires transport and authorization");return async(o,{revision:e,signal:s}={})=>{S(e),I(o.root,o.scope,e);let n=o.root,i=new URL(n).origin,a=n.endsWith("/")?n:n+"/",f=async(m,w)=>{s?.throwIfAborted(),await t(o,{signal:s}),s?.throwIfAborted();let y=await r(m,{origin:i,signal:s,...w});if(s?.throwIfAborted(),await t(o,{signal:s}),s?.throwIfAborted(),!Buffer.isBuffer(y)||y.length>2097152)throw Error("Invalid DAV response bytes");return new TextDecoder("utf-8",{fatal:!0}).decode(y)},c=await f(a,{method:"PROPFIND",depth:0,body:'<D:propfind xmlns:D="DAV:" xmlns:S="http://subversion.tigris.org/xmlns/dav/"><D:prop><S:repository-uuid/><D:version-controlled-configuration/></D:prop></D:propfind>'}),{vcc:p}=ce(c,{root:n,target:a,uuid:o.uuid}),l=await f(p,{method:"PROPFIND",depth:0,label:e,body:'<D:propfind xmlns:D="DAV:"><D:prop><D:baseline-collection/><D:version-name/></D:prop></D:propfind>'});return{...pe(l,{root:n,revision:e}),uuid:o.uuid}}}function le(r,{root:t,target:o}={}){let e=O(r),s=(p,l)=>{if(!p||typeof p!="object"||Array.isArray(p)||Object.keys(p).some(m=>!l.includes(m)))throw Error("Invalid DAV property names structure")};s(e,["D:multistatus"]);let n=e["D:multistatus"];if(s(n,["D:response"]),n["D:response"]?.length!==1)throw Error("One DAV property response required");let i=n["D:response"][0];if(s(i,["D:href","D:propstat"]),L(i["D:href"],{root:t})!==o)throw Error("DAV property target mismatch");if(i["D:propstat"]?.length!==1)throw Error("One DAV property status required");let a=i["D:propstat"][0];if(s(a,["D:status","D:prop"]),a["D:status"]!=="HTTP/1.1 200 OK")throw Error("DAV property names unavailable");let f=a["D:prop"];if(f!==""&&(!f||typeof f!="object"||Array.isArray(f)||Object.keys(f).length>1e3||Object.entries(f).some(([p,l])=>!/^[DSPC]:[^:]+$/.test(p)||l!=="")))throw Error("DAV property names must be empty");let c=f===""?[]:Object.keys(f);return{names:c,special:c.includes("P:special")}}var Le=r=>r.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""");function de({request:r}={}){if(typeof r!="function")throw Error("Bounded DAV request required");return async(t,{identity:o,assertAuthorized:e,signal:s}={})=>{let n=C(t,o),i=n.args[0],a=fe({request:r,assertAuthorized:e}),f=await a(o,{revision:n.pegRevision,signal:s}),c=async(y,u)=>{s?.throwIfAborted(),await e(),s?.throwIfAborted();let d=await r(y,{origin:new URL(o.root).origin,...u,signal:s});if(s?.throwIfAborted(),await e(),s?.throwIfAborted(),!Buffer.isBuffer(d)||d.length>2097152)throw Error("Invalid DAV transport bytes");return d};if(i==="log"){let y=ae(t,o);return c(f.collection,{method:"REPORT",body:y.body})}let p=n.path.slice(1).split("/").map(y=>encodeURIComponent(y).replace(/[!'()*]/g,u=>"%"+u.charCodeAt(0).toString(16).toUpperCase())).join("/"),l=L(f.collection+p,{root:o.root}),m=await c(l,{method:"PROPFIND",depth:0,body:'<D:propfind xmlns:D="DAV:"><D:propname/></D:propfind>'}),w=le(new TextDecoder("utf-8",{fatal:!0}).decode(m),{root:o.root,target:l});if(i==="proplist")return'<properties><target path="'+Le(n.args.at(-1).slice(0,n.args.at(-1).lastIndexOf("@")))+'">'+(w.special?'<property name="svn:special"/>':"")+"</target></properties>";if(w.special)throw Error("SVN special content forbidden");return c(l,{method:"GET"})}}function gt(r,t){return se(r,t,{transportFormat:"dav",transport:de({request:ie()})})}export{gt as createSvnHttpsHost};
|
|
1
|
+
import{createHandler as Te}from"../index.mjs";import{XMLParser as we,XMLValidator as ye}from"fast-xml-parser";function O(t){if(typeof t!="string"||Buffer.byteLength(t)>2097152||/<!DOCTYPE|<!ENTITY/i.test(t)||ye.validate(t)!==!0)throw Error("Invalid DAV XML");let n=new we({preserveOrder:!0,ignoreAttributes:!1,parseTagValue:!1,parseAttributeValue:!1,trimValues:!1}).parse(t),r=0,e=(s,o,i=0)=>{if(i>32)throw Error("DAV XML too deep");let a=Object.create(null),f="";for(let c of s){if(++r>1e4)throw Error("DAV XML too many nodes");if(Object.hasOwn(c,"#text")){f+=c["#text"];continue}let p=Object.keys(c).filter(S=>S!==":@");if(p.length!==1)throw Error("Invalid DAV XML node");let l=p[0];if(l==="?xml"){if(i!==0)throw Error("Unexpected XML declaration");continue}let m={...o},w={};for(let[S,V]of Object.entries(c[":@"]||{}))S==="@_xmlns"?m[""]=V:S.startsWith("@_xmlns:")?m[S.slice(8)]=V:w[S]=V;let g=l.split(":");if(g.length>2)throw Error("Invalid DAV XML name");let u=g.length===2?g[0]:"",d=g.at(-1),h=m[u],y=h==="DAV:"?"D:"+d:h==="http://subversion.tigris.org/xmlns/dav/"?"S:"+d:h==="svn:"?"L:"+d:h==="http://subversion.tigris.org/xmlns/svn/"?"P:"+d:h==="http://subversion.tigris.org/xmlns/custom/"?"C:"+d:null;if(!y)throw Error("Unsupported DAV namespace");let b=["L:added-path","L:deleted-path","L:modified-path","L:replaced-path"].includes(y);if(Object.keys(w).length&&(!b||Object.keys(w).some(S=>!["@_node-kind","@_text-mods","@_prop-mods","@_copyfrom-path","@_copyfrom-rev"].includes(S))))throw Error("Unexpected DAV attribute");let v=e(c[l],m,i+1);if(b&&typeof v!="string")throw Error("Invalid DAV path text");let E=b?{...w,"#text":v}:v;if(["D:response","D:propstat","L:log-item","L:added-path","L:deleted-path","L:modified-path","L:replaced-path"].includes(y))(a[y]??=[]).push(E);else{if(Object.hasOwn(a,y))throw Error("Duplicate DAV expanded name");a[y]=E}}if(!Object.keys(a).length)return f;if(f.trim())throw Error("Mixed DAV XML content");return a};return e(n,{})}var Se=9223372036854775807n;function D(t){if(typeof t!="string"||t.length>19||!/^(0|[1-9][0-9]*)$/.test(t))throw new Error("Invalid SVN revision");let n=BigInt(t);if(n>Se)throw new Error("SVN revision exceeds supported range");return n}function U(t){let n=D(t);return n===0n?null:String(n-1n)}function _(t,{snapshot:n,cursor:r=n,limit:e=50}={}){let s=D(n),o=D(r);if(o>s||!Number.isInteger(e)||e<1||e>100||!Array.isArray(t)||t.length>e+1)throw new Error("Invalid SVN page bounds");let i=o+1n;for(let f of t){let c=D(f);if(c>o||c>=i)throw new Error("SVN revisions must descend within cursor");i=c}let a=t.slice(0,e);return{snapshot:n,revisions:a,nextRevision:t.length>e?U(a.at(-1)):null}}function G(t,n){let r=O(t);if(!r||typeof r!="object"||Object.keys(r).length!==1||!Object.hasOwn(r,"L:log-report"))throw Error("Invalid DAV log root");let e=r["L:log-report"],s;if(e==="")s=[];else{if(!e||typeof e!="object"||Object.keys(e).some(a=>a!=="L:log-item")||!Array.isArray(e["L:log-item"]))throw Error("Invalid DAV log structure");s=e["L:log-item"]}if(s.length>101)throw Error("DAV log entries exceed limit");let o=s.map(a=>{if(!a||typeof a!="object"||Array.isArray(a)||Object.keys(a).some(c=>!["D:version-name","D:comment","D:creator-displayname","L:date"].includes(c)))throw Error("Unsupported DAV log item");let f=c=>{let p=a[c]??"";if(typeof p!="string")throw Error("Invalid DAV log text");return p};return{revision:f("D:version-name"),message:f("D:comment"),author:f("D:creator-displayname"),date:f("L:date")}}),i=_(o.map(a=>a.revision),n);return{snapshot:i.snapshot,entries:o.slice(0,i.revisions.length),nextRevision:i.nextRevision}}function R(t){if(typeof t!="string"||t.length>32768||!t.startsWith("/")||/[\u0000-\u001f\u007f\\]/.test(t))throw new Error("Invalid SVN repository path");if(t!=="/"&&t.slice(1).split("/").some(n=>n===""||n==="."||n===".."))throw new Error("Noncanonical SVN repository path");return t}function j(t,n){return R(t),R(n),n==="/"||t===n||t.startsWith(n+"/")}function B(t,n){if(!Array.isArray(t)||t.length>1e4)throw new Error("Invalid SVN path nodes");let r=t.map(e=>{let s=["#text","@_action","@_kind","@_copyfrom-path","@_copyfrom-rev","@_text-mods","@_prop-mods"];if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(o=>!s.includes(o)))throw new Error("Invalid SVN path node");for(let o of["@_text-mods","@_prop-mods"])if(Object.hasOwn(e,o)&&!["true","false","unknown"].includes(e[o]))throw new Error("Invalid SVN modification flag");return{...Object.hasOwn(e,"@_text-mods")?{textModified:e["@_text-mods"]}:{},...Object.hasOwn(e,"@_prop-mods")?{propertiesModified:e["@_prop-mods"]}:{},path:e["#text"],action:e["@_action"],kind:e["@_kind"],...Object.hasOwn(e,"@_copyfrom-path")?{copyFromPath:e["@_copyfrom-path"]}:{},...Object.hasOwn(e,"@_copyfrom-rev")?{copyFromRevision:e["@_copyfrom-rev"]}:{}}});return W(r,n)}function W(t,{scope:n,revision:r}={}){R(n);let e=D(r);if(!Array.isArray(t)||t.length>1e4||e===0n&&t.length!==0)throw new Error("Invalid SVN changed paths");let s=new Set;return t.map(i=>{if(!i||typeof i!="object"||Array.isArray(i)||Object.keys(i).some(u=>!["path","action","kind","copyFromPath","copyFromRevision","copySourceOutsideScope","textModified","propertiesModified"].includes(u)))throw new Error("Invalid SVN change structure");let{path:a,action:f,kind:c,copyFromPath:p,copyFromRevision:l}=i;if(R(a),s.has(a)||!["A","D","M","R"].includes(f)||!["file","dir","unknown"].includes(c))throw new Error("Invalid SVN change");s.add(a);let m=p!==void 0||l!==void 0;if(m&&(R(p),!["A","R"].includes(f)||D(l)>=e))throw new Error("Invalid SVN copy source");let w=Object.hasOwn(i,"copySourceOutsideScope");if(w&&(i.copySourceOutsideScope!==!0||m||!["A","R"].includes(f)))throw new Error("Invalid SVN redacted copy source");let g={path:a,action:f,kind:c,...w?{copySourceOutsideScope:!0}:{}};for(let u of["textModified","propertiesModified"])if(Object.hasOwn(i,u)){if(!["true","false","unknown"].includes(i[u]))throw new Error("Invalid SVN modification flag");g[u]=i[u]}return m&&(j(p,n)?(g.copyFromPath=p,g.copyFromRevision=l):g.copySourceOutsideScope=!0),g}).filter(i=>j(i.path,n))}function Q(t,{scope:n,revision:r}={}){D(r);let e=O(t),s=(p,l)=>{if(!p||typeof p!="object"||Array.isArray(p)||Object.keys(p).some(m=>!l.includes(m)))throw Error("Invalid DAV detail structure")};s(e,["L:log-report"]);let o=e["L:log-report"];if(s(o,["L:log-item"]),o["L:log-item"]?.length!==1)throw Error("One DAV detail revision required");let i=o["L:log-item"][0],a={"L:added-path":"A","L:deleted-path":"D","L:modified-path":"M","L:replaced-path":"R"};if(s(i,["D:version-name","D:comment","D:creator-displayname","L:date",...Object.keys(a)]),i["D:version-name"]!==r)throw Error("DAV detail revision mismatch");let f=p=>{let l=i[p]??"";if(typeof l!="string")throw Error("Invalid DAV detail text");return l},c=[];for(let[p,l]of Object.entries(a))for(let m of i[p]??[]){let{"@_node-kind":w,...g}=m;c.push({...g,"@_kind":w??"unknown","@_action":l})}return{revision:r,author:f("D:creator-displayname"),date:f("L:date"),message:f("D:comment"),changes:B(c,{scope:n,revision:r})}}import{XMLParser as De,XMLValidator as be}from"fast-xml-parser";function N(t,n,r){if(R(n),D(r),typeof t!="string"||t.length>32768||/[\x00-\x20\x7f\\?#]/.test(t))throw new Error("Invalid SVN HTTPS root");let e=new URL(t);if(e.protocol!=="https:"||e.username||e.password||e.href!==t)throw new Error("Noncanonical SVN HTTPS root");let s=e.pathname.split("/").slice(1);s.at(-1)===""&&s.pop();for(let f of s){let c=decodeURIComponent(f);if(!f||c==="."||c===".."||/[\x00-\x1f\x7f/\\]/.test(c))throw new Error("Invalid SVN root path")}let o=t.endsWith("/")?t.slice(0,-1):t,i=n==="/"?"":n.split("/").map(f=>encodeURIComponent(f).replace(/[!'()*]/g,c=>"%"+c.charCodeAt(0).toString(16).toUpperCase())).join("/"),a=o+(i||(e.pathname==="/"?"/":""))+"@"+r;if(a.length>32768)throw new Error("SVN target too long");return a}function L(t,{root:n}={}){if(N(n,"/","0"),typeof t!="string"||!t||t.length>32768||/[\\?#]/.test(t)||[...t].some(o=>o.charCodeAt(0)<=32||o.charCodeAt(0)===127))throw Error("Invalid DAV href");if(!(t.startsWith("/")&&!t.startsWith("//"))&&!t.startsWith("https://"))throw Error("DAV href must be absolute path or HTTPS URL");let r=new URL(t,new URL(n).origin);if(r.protocol!=="https:"||r.origin!==new URL(n).origin||r.username||r.password||r.search||r.hash)throw Error("DAV href outside authorized origin");if(t.startsWith("https://")&&r.href!==t||t.startsWith("/")&&r.pathname!==t)throw Error("Noncanonical DAV href");let e=r.pathname.split("/").slice(1);e.at(-1)===""&&e.pop();for(let o of e){let i=decodeURIComponent(o);if(!o||i==="."||i===".."||[...i].some(a=>a.charCodeAt(0)<32||a.charCodeAt(0)===127||a==="/"||a.charCodeAt(0)===92))throw Error("Unsafe DAV href path")}let s=new URL(n).pathname.replace(/[/]$/,"");if(s&&r.pathname!==s&&!r.pathname.startsWith(s+"/"))throw Error("DAV href outside repository root");return r.href}function Z(t,{target:n,revision:r,root:e,uuid:s}={}){if(typeof t!="string"||Buffer.byteLength(t)>2097152||/<!DOCTYPE|<!ENTITY/i.test(t)||be.validate(t)!==!0)throw Error("Invalid SVN node kind XML");let i=new De({ignoreAttributes:!1,parseAttributeValue:!1,parseTagValue:!1,trimValues:!1,isArray:f=>f==="entry"}).parse(t)?.info?.entry;if(!Array.isArray(i)||i.length!==1)throw Error("Expected one SVN node kind entry");let a=i[0];if(a.url!==n||a["@_revision"]!==r||a.repository?.root!==e||a.repository?.uuid!==s)throw Error("SVN node kind identity mismatch");if(!["file","dir"].includes(a["@_kind"]))throw Error("Unknown SVN node kind");return a["@_kind"]}function ee(t,{root:n,target:r}={}){let e=O(t),s=(p,l)=>{if(!p||typeof p!="object"||Array.isArray(p)||Object.keys(p).some(m=>!l.includes(m)))throw Error("Invalid DAV node kind structure")};s(e,["D:multistatus"]),s(e["D:multistatus"],["D:response"]);let o=e["D:multistatus"]["D:response"];if(o?.length!==1)throw Error("Expected one DAV node kind response");let i=o[0];s(i,["D:href","D:propstat"]);let a=L(i["D:href"],{root:n});if(i["D:propstat"]?.length!==1)throw Error("Expected one DAV node kind status");let f=i["D:propstat"][0];if(s(f,["D:status","D:prop"]),!/^HTTP[/]1[.][01] 200(?: OK)?$/.test(f["D:status"]))throw Error("DAV node kind unavailable");s(f["D:prop"],["D:resourcetype"]);let c=f["D:prop"]["D:resourcetype"];if(c===""){if(a!==r)throw Error("DAV node kind target mismatch");return"file"}if(s(c,["D:collection"]),c["D:collection"]!=="")throw Error("Unknown DAV node kind");if(a!==r&&a!==r+"/")throw Error("DAV node kind target mismatch");return"dir"}function M({root:t,scope:n,path:r=n,snapshot:e,revision:s}={}){if(D(s)>D(e))throw new Error("SVN selected revision exceeds snapshot");if(!j(r,n))throw new Error("SVN detail path outside scope");let o=N(t,r,e);return{scope:n,revision:s,snapshot:e,args:["log","--xml","--verbose","--stop-on-copy","--non-interactive","--no-auth-cache","--limit","1","-r",s+":"+s,"--",o]}}function q({root:t,scope:n,path:r=n,snapshot:e,cursor:s=e,limit:o=50}={}){if(_([],{snapshot:e,cursor:s,limit:o}),!j(r,n))throw new Error("SVN history path outside scope");let i=N(t,r,e);return{snapshot:e,cursor:s,limit:o,args:["log","--xml","--stop-on-copy","--non-interactive","--no-auth-cache","--limit",String(o+1),"-r",s+":0","--",i]}}function H(t,n){let r=t?.args;if(!Array.isArray(r)||r.length>16||r.some(l=>typeof l!="string"))throw Error("Invalid SVN command");let e=r.at(-1),s=e.lastIndexOf("@"),o=e.slice(s+1);D(o);let i=n.root.endsWith("/")?n.root.slice(0,-1):n.root;N(n.root,"/",o);let a=e.slice(0,s);if(!(a===i||a.startsWith(i+"/")))throw Error("SVN command target outside root");let f=a.slice(i.length),c=f?"/"+f.slice(1).split("/").map(l=>decodeURIComponent(l)).join("/"):"/";if(!j(c,n.scope)||N(n.root,c,o)!==e)throw Error("SVN command target outside scope or noncanonical");let p;if(r[0]==="cat"||r[0]==="proplist"||r[0]==="info"){D(r[r[0]==="cat"?4:5]);let l=r[r[0]==="cat"?4:5];if(l!==o)throw Error("SVN content operative and peg mismatch");p=r[0]==="cat"?["cat","--non-interactive","--no-auth-cache","-r",l,"--",e]:[r[0],"--xml","--non-interactive","--no-auth-cache","-r",l,"--",e]}else if(r[0]==="log")if(r.includes("--verbose")){let l=r[9]?.split(":")[0];p=M({...n,path:c,snapshot:o,revision:l}).args}else{let l=r[8]?.split(":")[0],m=Number(r[6])-1;p=q({...n,path:c,snapshot:o,cursor:l,limit:m}).args}else throw Error("SVN command is not readonly history");if(JSON.stringify(r)!==JSON.stringify(p))throw Error("Unsupported SVN command shape");return{path:c,pegRevision:o,args:[...p]}}function re(t,{scope:n,revision:r,path:e}={}){if(D(r)===0n)throw new Error("SVN r0 has no change comparison");let s=W(t,{scope:n,revision:r}).find(a=>a.path===e);if(!s)throw new Error("SVN path is not an authorized changed member");if(s.kind!=="file")throw new Error("Only SVN file changes can be compared");let o=U(r),i=a=>a===null?{empty:!0}:{empty:!1,path:s.path,revision:a,pegRevision:a};return{path:s.path,action:s.action,left:i(s.action==="A"?null:o),right:i(s.action==="D"?null:r)}}import{XMLParser as Ee,XMLValidator as Ae}from"fast-xml-parser";function te(t,{target:n,allowEmpty:r=!1}={}){if(typeof n!="string"||!n||n.length>32768)throw new Error("Invalid SVN property target");if(typeof t!="string"||Buffer.byteLength(t,"utf8")>2*1024*1024||/<!DOCTYPE|<!ENTITY/i.test(t)||Ae.validate(t)!==!0)throw new Error("Invalid SVN property XML");let e=new Ee({ignoreAttributes:!1,parseAttributeValue:!1,parseTagValue:!1,trimValues:!1,isArray:p=>p==="target"||p==="property"}).parse(t),s=(p,l)=>{if(!p||typeof p!="object"||Array.isArray(p)||Object.keys(p).some(m=>!l.includes(m)&&!(m==="#text"&&typeof p[m]=="string"&&/^[ \t\r\n]*$/.test(p[m]))))throw new Error("Invalid SVN property structure")};if(s(e,["?xml","properties"]),r===!0&&typeof e.properties=="string"&&/^[ \t\r\n]*$/.test(e.properties))return{target:n,names:[],special:!1};s(e.properties,["target"]);let o=e.properties.target;if(!Array.isArray(o)||o.length!==1)throw new Error("Expected one SVN property target");let i=o[0];if(s(i,["@_path","property"]),i["@_path"]!==n)throw new Error("SVN property target mismatch");let a=i.property??[];if(!Array.isArray(a)||a.length>1e4)throw new Error("Too many SVN properties");let f=[],c=new Set;for(let p of a){s(p,["@_name"]);let l=p["@_name"];if(typeof l!="string"||!l||l.length>1024||/[\x00-\x20\x7f]/.test(l)||c.has(l))throw new Error("Invalid SVN property name");c.add(l),f.push(l)}return{target:n,names:f,special:c.has("svn:special")}}import{createHash as Le}from"node:crypto";import{XMLParser as Ve,XMLValidator as xe}from"fast-xml-parser";function oe(t,{scope:n,revision:r}={}){if(D(r),typeof t!="string"||Buffer.byteLength(t,"utf8")>2*1024*1024||/<!DOCTYPE|<!ENTITY/i.test(t)||xe.validate(t)!==!0)throw new Error("Invalid SVN detail XML");let e=new Ve({ignoreAttributes:!1,parseAttributeValue:!1,parseTagValue:!1,trimValues:!1,isArray:c=>c==="logentry"||c==="path"}).parse(t),s=(c,p)=>{if(!c||typeof c!="object"||Array.isArray(c)||Object.keys(c).some(l=>!p.includes(l)&&!(l==="#text"&&typeof c[l]=="string"&&/^[ \t\r\n]*$/.test(c[l]))))throw new Error("Invalid SVN detail structure")};if(s(e,["?xml","log"]),s(e.log,["logentry"]),!Array.isArray(e.log.logentry)||e.log.logentry.length!==1)throw new Error("Expected one SVN revision");let o=e.log.logentry[0];if(s(o,["@_revision","author","date","msg","paths"]),o["@_revision"]!==r)throw new Error("SVN detail revision mismatch");let i=c=>{let p=o[c]??"";if(typeof p!="string")throw new Error("Invalid SVN detail text");return p},a=[];o.paths!==void 0&&!(typeof o.paths=="string"&&/^[ \t\r\n]*$/.test(o.paths))&&(s(o.paths,["path"]),a=o.paths.path??[]);let f=B(a,{scope:n,revision:r});return{revision:r,author:i("author"),date:i("date"),message:i("msg"),pathsAvailable:Object.hasOwn(o,"paths"),changes:f}}import{randomBytes as Ie}from"node:crypto";function ne(t){if(!t||typeof t!="object")throw Error("Invalid SVN consent identity");let{sessionId:n,cwd:r,repositoryId:e,root:s,uuid:o,scope:i}=t;for(let a of[n,r,e])if(typeof a!="string"||!a||a.length>32768||a.includes("\0"))throw Error("Invalid SVN consent scope");if(N(s,"/","0"),R(i),typeof o!="string"||!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(o))throw Error("Invalid SVN repository UUID");return JSON.stringify([n,r,e,s,o,i])}function Y({now:t=Date.now,ttlMs:n=3e5,maxEntries:r=32,onRevoke:e=()=>{}}={}){if(!Number.isInteger(n)||n<1||n>3e5||!Number.isInteger(r)||r<1||r>32)throw Error("Invalid SVN consent bounds");if(typeof e!="function")throw Error("Invalid SVN revoke listener");let s=new Map,o=a=>{s.delete(a)&&e(a)},i=()=>{for(let[a,f]of s)f.expires<=t()&&o(a)};return{grant(a,{explicit:f=!1}={}){if(f!==!0)throw Error("Explicit SVN network consent required");let c=ne(a);i();for(let[l,m]of s)m.key===c&&o(l);for(;s.size>=r;)o(s.keys().next().value);let p=Ie(32).toString("hex");return s.set(p,{key:c,sessionId:a.sessionId,expires:t()+n}),p},assert(a,f){i();let c=ne(f),p=s.get(a);if(!p||p.key!==c)throw Object.assign(Error("SVN network consent missing, expired or identity changed"),{code:"vcs/svn-consent-required"})},revokeSession(a){for(let[f,c]of s)c.sessionId===a&&o(f)},revoke(a){o(a)},clear(){for(let a of[...s.keys()])o(a)}}}import{XMLParser as Ne,XMLValidator as ke}from"fast-xml-parser";function se(t,n){let r=Oe(t),e=_(r.map(s=>s.revision),n);return{snapshot:e.snapshot,entries:r.slice(0,e.revisions.length),nextRevision:e.nextRevision}}function Oe(t){if(typeof t!="string"||Buffer.byteLength(t,"utf8")>2*1024*1024||/<!DOCTYPE|<!ENTITY/i.test(t)||ke.validate(t)!==!0)throw new Error("Invalid SVN log XML");let r=new Ne({ignoreAttributes:!1,parseAttributeValue:!1,parseTagValue:!1,trimValues:!1,isArray:o=>o==="logentry"}).parse(t);if(!Object.hasOwn(r,"log")||Object.keys(r).some(o=>o!=="log"&&o!=="?xml"))throw new Error("Invalid SVN log root");let e=r.log;if(typeof e=="string"&&/^[ \t\r\n]*$/.test(e))return[];if(e&&typeof e=="object"&&typeof e["#text"]=="string"&&/^[ \t\r\n]*$/.test(e["#text"])&&delete e["#text"],!e||typeof e!="object"||Object.keys(e).some(o=>o!=="logentry")||!Array.isArray(e.logentry)||e.logentry.length>101)throw new Error("Invalid SVN log entries");let s=new Set;return e.logentry.map(o=>{if(!o||typeof o!="object"||Array.isArray(o)||Object.keys(o).some(f=>!["@_revision","author","date","msg","#text"].includes(f))||"#text"in o&&(typeof o["#text"]!="string"||!/^[ \t\r\n]*$/.test(o["#text"])))throw new Error("Unsupported SVN log entry structure");let i=o["@_revision"];if(D(i),s.has(i))throw new Error("Duplicate SVN revision");s.add(i);let a=f=>{let c=o[f]??"";if(typeof c!="string")throw new Error("Invalid SVN log text");return c};return{revision:i,author:a("author"),date:a("date"),message:a("msg")}})}function ie({resolveIdentity:t,transport:n,now:r=Date.now,timeoutMs:e=15e3,transportFormat:s="cli"}={}){if(!["cli","dav"].includes(s))throw Error("Invalid SVN transport format");if(!Number.isInteger(e)||e<1||e>15e3)throw Error("Invalid SVN runtime deadline");if(typeof t!="function")throw Error("Local identity resolver required");let o=Y({now:r}),i=Y({now:r,onRevoke:u=>m(u)}),a=!1,f=()=>{if(a)throw new DOMException("SVN runtime disposed","AbortError")},c=async(u,d)=>{f(),d?.throwIfAborted();let h=await t(u,d);return f(),d?.throwIfAborted(),{...h,sessionId:u.sessionId,cwd:h.cwd,repositoryId:u.repositoryId}},p=u=>u.revision===void 0?{}:(D(u.revision),{snapshot:u.revision}),l=new Map,m=u=>{for(let d of l.get(u)||[])d.controller.abort(new DOMException("SVN consent revoked","AbortError"))},w=async(u,d)=>{f(),H(u,d.identity),d.signal?.throwIfAborted(),i.assert(d.token,d.identity);let h=new AbortController,y=()=>h.abort(d.signal.reason);d.signal?.addEventListener("abort",y,{once:!0});let b={controller:h,sessionId:d.identity.sessionId};l.has(d.token)||l.set(d.token,new Set),l.get(d.token).add(b);let v=setTimeout(()=>h.abort(new DOMException("SVN transport deadline exceeded","TimeoutError")),e);try{let E=async()=>{h.signal.throwIfAborted();let V=await c({sessionId:d.identity.sessionId,repositoryId:d.identity.repositoryId},h.signal);i.assert(d.token,V),h.signal.throwIfAborted()},S=await n(u,{...d,timeoutMs:e,signal:h.signal,assertAuthorized:E});if(h.signal.throwIfAborted(),!(typeof S=="string"||Buffer.isBuffer(S))||Buffer.byteLength(S)>d.maxBytes)throw Error("SVN transport output exceeds byte limit or has invalid type");return Buffer.isBuffer(S)&&u.args[0]!=="cat"?new TextDecoder("utf-8",{fatal:!0}).decode(S):S}catch(E){throw h.signal.aborted?h.signal.reason:E}finally{let E=l.get(d.token);E?.delete(b),E?.size||l.delete(d.token),clearTimeout(v),d.signal?.removeEventListener("abort",y)}},g={async describe(u,{signal:d}={}){let h=await c(u,d),y=p(h),b=o.grant(h,{explicit:!0});return{...y,offer:b,root:h.root,uuid:h.uuid,scope:h.scope,origin:new URL(h.root).origin,expiresAt:r()+3e5,remoteEnabled:typeof n=="function"}},async approve(u,{offer:d,explicit:h=!1,signal:y}={}){if(h!==!0)throw Error("Explicit SVN network consent required");let b=await c(u,y);if(o.assert(d,b),typeof n!="function")throw Error("SVN remote transport disabled");let v=p(b),E=i.grant(b,{explicit:!0});return o.revoke(d),{...v,token:E,expiresAt:r()+3e5}},async log(u,{token:d,snapshot:h,cursor:y,limit:b,path:v,signal:E}={}){let S=await c(u,E);if(i.assert(d,S),typeof n!="function")throw Error("SVN remote transport disabled");let V=q({...S,snapshot:h,cursor:y,limit:b,path:v});E?.throwIfAborted();let A=await w(V,{identity:S,token:d,signal:E,maxBytes:2097152,timeoutMs:15e3});E?.throwIfAborted();let x=await c(u,E);return i.assert(d,x),(s==="dav"?G:se)(A,V)},async detail(u,{token:d,snapshot:h,revision:y,signal:b}={}){let v=await c(u,b);if(i.assert(d,v),typeof n!="function")throw Error("SVN remote transport disabled");let E=M({...v,snapshot:h,revision:y}),S=await w(E,{identity:v,token:d,signal:b,maxBytes:2097152,timeoutMs:15e3});b?.throwIfAborted(),i.assert(d,await c(u,b));let V=s==="dav"?{...Q(S,E),pathsAvailable:!0}:oe(S,E);return{...V,snapshot:h,changes:V.changes.map(A=>({...A,id:Le("sha256").update(JSON.stringify([v.root,v.uuid,v.scope,h,y,A])).digest("hex")}))}},async compare(u,{token:d,snapshot:h,revision:y,id:b,signal:v}={}){if(typeof b!="string"||!/^[a-f0-9]{64}$/.test(b))throw Error("Invalid SVN change ID");let E=await g.detail(u,{token:d,snapshot:h,revision:y,signal:v}),S=E.changes.find(I=>I.id===b);if(!S)throw Error("SVN change is not selected revision member");let V=await c(u,v);i.assert(d,V);let A=re(E.changes.map(({id:I,...P})=>P),{scope:V.scope,revision:y,path:S.path}),x=async I=>{if(I.empty)return{label:"empty",text:"",absent:!0};let P=await c(u,v);i.assert(d,P);let C=N(P.root,I.path,I.pegRevision),K={identity:P,token:d,signal:v,maxBytes:2097152,timeoutMs:15e3},ge=await w({args:["proplist","--xml","--non-interactive","--no-auth-cache","-r",I.revision,"--",C]},K);if(v?.throwIfAborted(),i.assert(d,await c(u,v)),te(ge,{target:C.slice(0,C.lastIndexOf("@")),allowEmpty:!0}).special)throw Error("SVN special files cannot be compared");let F=await w({args:["cat","--non-interactive","--no-auth-cache","-r",I.revision,"--",C]},K);v?.throwIfAborted(),i.assert(d,await c(u,v));let $=Buffer.isBuffer(F)?F:Buffer.from(F);if($.length>2097152)throw Error("SVN text exceeds 2 MiB");if($.includes(0))return{label:"r"+I.revision,text:"",binary:!0};let J;try{J=new TextDecoder("utf-8",{fatal:!0}).decode($)}catch{return{label:"r"+I.revision,text:"",binary:!0}}return{label:"r"+I.revision,text:J}},k=A.left;if(S.action==="R"&&!k.empty){let I=await c(u,v);i.assert(d,I);let P=N(I.root,k.path,k.pegRevision),C=await w({args:["info","--xml","--non-interactive","--no-auth-cache","-r",k.revision,"--",P]},{identity:I,token:d,signal:v,maxBytes:2097152,timeoutMs:15e3});v?.throwIfAborted(),i.assert(d,await c(u,v)),Z(C,{target:P.slice(0,P.lastIndexOf("@")),revision:k.revision,root:I.root,uuid:I.uuid})==="dir"&&(k={empty:!0})}let T=await x(k),z=await x(A.right);return{path:S.path,revision:y,snapshot:h,left:T,right:z,binary:!!(T.binary||z.binary)}},async trace(u,{token:d,snapshot:h,revision:y,id:b,cursor:v,limit:E=50,signal:S}={}){if(typeof b!="string"||!/^[a-f0-9]{64}$/.test(b))throw Error("Invalid SVN change ID");let V=await g.detail(u,{token:d,snapshot:h,revision:y,signal:S}),A=V.changes.find(T=>T.id===b);if(!A||A.kind!=="file")throw Error("SVN tracing requires selected file member");let x=A.action==="D"?U(y):y;return{...await g.log(u,{token:d,snapshot:x,cursor:v??x,limit:E,path:A.path,signal:S}),selectionSnapshot:h,selectionRevision:y,path:A.path,pegRevision:x,stopOnCopy:!0,copySourceOutsideScope:!!A.copySourceOutsideScope}},async copytrace(u,{token:d,snapshot:h,revision:y,id:b,cursor:v,limit:E=50,signal:S}={}){if(typeof b!="string"||!/^[a-f0-9]{64}$/.test(b))throw Error("Invalid SVN change ID");let V=await g.detail(u,{token:d,snapshot:h,revision:y,signal:S}),A=V.changes.find(T=>T.id===b);if(!A||A.kind!=="file"||A.copySourceOutsideScope||A.copyFromPath===void 0||A.copyFromRevision===void 0)throw Error("Selected file has no authorized copy source");let x=A.copyFromRevision;return{...await g.log(u,{token:d,snapshot:x,cursor:v??x,limit:E,path:A.copyFromPath,signal:S}),selectionSnapshot:h,selectionRevision:y,path:A.copyFromPath,pegRevision:x,sourceForPath:A.path,stopOnCopy:!0}},async revoke(u,{token:d,signal:h}={}){let y=await c(u,h);return i.assert(d,y),i.revoke(d),m(d),{revoked:!0}},dispose(){a=!0,o.clear(),i.clear();for(let u of l.keys())m(u)},revokeSession(u){o.revokeSession(u),i.revokeSession(u);for(let[d,h]of l)[...h].some(y=>y.sessionId===u)&&m(d)}};return g}var ae={revoke:["token"],describe:[],approve:["offer","explicit"],log:["token","snapshot","cursor","limit"],detail:["token","snapshot","revision"],compare:["token","snapshot","revision","id"],trace:["token","snapshot","revision","id","cursor","limit"],copytrace:["token","snapshot","revision","id","cursor","limit"]};function Re(t,n){if(!n||typeof n!="object"||Array.isArray(n))throw Error("Object payload required");if(Object.keys(n).some(r=>!["sessionId","repositoryId",...ae[t]].includes(r)))throw Error("Unknown SVN request field");for(let r of["sessionId","repositoryId"])if(typeof n[r]!="string"||!n[r]||n[r].length>1024||n[r].includes("\0"))throw Error("Invalid SVN Session address");if(t==="approve"&&(n.explicit!==!0||typeof n.offer!="string"||!/^[a-f0-9]{64}$/.test(n.offer)))throw Error("Explicit scoped offer required");if(!["describe","approve"].includes(t)){if(typeof n.token!="string"||!/^[a-f0-9]{64}$/.test(n.token))throw Error("SVN consent token required");if(t!=="revoke"&&D(n.snapshot),n.revision!==void 0&&D(n.revision),["detail","compare","trace","copytrace"].includes(t)&&n.revision===void 0)throw Error("Selected SVN revision required");if(n.cursor!==void 0&&D(n.cursor),n.limit!==void 0&&(!Number.isInteger(n.limit)||n.limit<1||n.limit>100))throw Error("Invalid SVN page size");if(["compare","trace","copytrace"].includes(t)&&(typeof n.id!="string"||!/^[a-f0-9]{64}$/.test(n.id)))throw Error("Selected SVN change ID required")}}function ce(t,{timeoutMs:n=15e3}={}){if(!Number.isInteger(n)||n<1||n>15e3)throw Error("Invalid SVN RPC deadline");let r=0;return async(e,s,o)=>{let i=e.startsWith("vcs/svn-")?e.slice(8):"";if(!Object.hasOwn(ae,i))return{ok:!1,error:{code:"vcs/not-found",message:"Unknown SVN endpoint"}};if(i!=="revoke"&&r>=2)return{ok:!1,error:{code:"vcs/busy",message:"SVN history requests busy"}};i!=="revoke"&&r++;let a=new AbortController,f=()=>a.abort(o.reason);o?.addEventListener("abort",f,{once:!0}),o?.aborted&&f();let c=setTimeout(()=>a.abort(new DOMException("SVN operation deadline exceeded","TimeoutError")),n);try{o?.throwIfAborted(),Re(i,s);let{sessionId:p,repositoryId:l,...m}=s,w=await t[i]({sessionId:p,repositoryId:l},{...m,signal:a.signal});return a.signal.throwIfAborted(),{ok:!0,value:w}}catch(p){let l=o?.aborted||p?.name==="AbortError"?"vcs/cancelled":["vcs/rediscover-required","vcs/svn-consent-required"].includes(p?.code)?p.code:"vcs/operation-failed",m=a.signal.aborted?a.signal.reason:p,w=m instanceof Error?m.message:String(m);return{ok:!1,error:{code:l,message:w}}}finally{clearTimeout(c),o?.removeEventListener("abort",f),i!=="revoke"&&r--}}}var Pe=new Set(["describe","approve","log","detail","compare","trace","copytrace","revoke"]);function pe(t,n,{transport:r,transportFormat:e="cli",now:s=Date.now,timeoutMs:o=15e3}={}){let i,a=Te(t,n,4,{now:s,bindSvnIdentityResolver:p=>{i=p}}),f=ie({resolveIdentity:i,transport:r,transportFormat:e,now:s,timeoutMs:o}),c=ce(f,{timeoutMs:o});return{handle(p,l,m){let w=p.startsWith("vcs/svn-")?p.slice(8):"";return Pe.has(w)?c(p,l,m):a(p,l,m)},revokeSession:p=>f.revokeSession(p),dispose:()=>f.dispose()}}import{request as je}from"node:https";function fe({request:t=je}={}){return async(n,{origin:r,method:e="GET",body:s="",depth:o,label:i,signal:a,maxBytes:f=2097152,timeoutMs:c=15e3}={})=>{let p=new URL(n);if(p.protocol!=="https:"||p.origin!==r||p.username||p.password||p.hash||p.href!==n||!["GET","PROPFIND","REPORT"].includes(e)||typeof s!="string"||Buffer.byteLength(s)>65536||!Number.isInteger(f)||f<1||f>2097152||!Number.isInteger(c)||c<1||c>15e3)throw Error("Invalid bounded HTTPS request");if(o!==void 0&&(e!=="PROPFIND"||o!==0))throw Error("Invalid DAV depth");if(i!==void 0){if(e!=="PROPFIND"||o!==0)throw Error("Invalid DAV label");D(i)}let l={...o===0?{Depth:"0"}:{},...i!==void 0?{Label:i}:{}};return a?.throwIfAborted(),new Promise((m,w)=>{let g,u,d=null,h=null,y=0,b=[],v=A=>{d??=A,u?.destroy(),g?.destroy()},E=()=>v(a.reason??new DOMException("HTTPS cancelled","AbortError")),S=setTimeout(()=>v(new DOMException("HTTPS deadline exceeded","TimeoutError")),c),V=()=>{clearTimeout(S),a?.removeEventListener("abort",E)};try{g=t(p,{method:e,agent:!1,rejectUnauthorized:!0,maxHeaderSize:16384,headers:{...l,"Accept-Encoding":"identity","Content-Type":"text/xml; charset=utf-8","Content-Length":String(Buffer.byteLength(s))}},A=>{u=A;let x=u.statusCode??0;if(x>=300&&x<400){v(Error("HTTPS redirects forbidden"));return}if(x<200||x>=300){v(Error("HTTPS status "+x));return}if(u.headers["content-encoding"]&&u.headers["content-encoding"]!=="identity"){v(Error("HTTPS compressed responses forbidden"));return}let k=u.headers["content-length"];if(k!==void 0&&(!/^[0-9]+$/.test(k)||Number(k)>f)){v(Error("HTTPS response exceeds byte limit"));return}u.on("data",T=>{y+=T.length,y>f?v(Error("HTTPS response exceeds byte limit")):d||b.push(T)}),u.on("error",v),u.on("end",()=>{if(!u.complete){v(Error("HTTPS response incomplete"));return}h=Buffer.concat(b)})}),g.on("error",A=>{d??=A}),g.once("close",()=>{V(),d?w(d):h!==null?m(h):w(Error("HTTPS response closed before completion"))}),a?.addEventListener("abort",E,{once:!0}),a?.aborted?E():g.end(s)}catch(A){V(),g?.destroy(),w(A)}})}}var Ce=t=>t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'");function le(t,n){let r=H(t,n),e=r.args;if(e[0]!=="log")throw Error("DAV log report requires readonly log plan");let s=e.includes("--verbose"),o=e[s?9:8].split(":"),i=e[s?7:6],a=r.path==="/"?"":r.path.slice(1),f='<?xml version="1.0" encoding="utf-8"?><S:log-report xmlns:S="svn:" xmlns:D="DAV:"><S:start-revision>'+o[0]+"</S:start-revision><S:end-revision>"+o[1]+"</S:end-revision><S:limit>"+i+"</S:limit><S:strict-node-history/>"+(s?"<S:discover-changed-paths/>":"")+"<S:path>"+Ce(a)+"</S:path><S:revprop>svn:author</S:revprop><S:revprop>svn:date</S:revprop><S:revprop>svn:log</S:revprop></S:log-report>";if(Buffer.byteLength(f)>65536)throw Error("DAV request body exceeds limit");return{method:"REPORT",body:f,pegRevision:r.pegRevision,path:r.path,requiresVerifiedBaseline:!0}}function de(t,{root:n,uuid:r,target:e}={}){let s=O(t),o=(l,m)=>{if(!l||typeof l!="object"||Array.isArray(l)||Object.keys(l).some(w=>!m.includes(w)&&!(w==="#text"&&typeof l[w]=="string"&&l[w].trim()==="")))throw Error("Unsupported DAV discovery structure")};o(s,["?xml","D:multistatus"]);let i=s["D:multistatus"];if(o(i,["D:response"]),!Array.isArray(i["D:response"])||i["D:response"].length!==1)throw Error("DAV discovery requires one response");let a=i["D:response"][0];if(o(a,["D:href","D:propstat"]),L(a["D:href"],{root:n})!==e)throw Error("DAV response target mismatch");if(!Array.isArray(a["D:propstat"])||a["D:propstat"].length!==1)throw Error("DAV discovery requires one property status");let f=a["D:propstat"][0];if(o(f,["D:prop","D:status"]),!/^HTTP[/]1[.][01] 200(?: OK)?$/.test(f["D:status"]))throw Error("DAV discovery properties unavailable");let c=f["D:prop"];if(o(c,["S:repository-uuid","D:version-controlled-configuration"]),c["S:repository-uuid"]!==r||typeof r!="string"||!/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(r))throw Error("DAV UUID does not match local identity");let p=c["D:version-controlled-configuration"];return o(p,["D:href"]),{uuid:r,vcc:L(p["D:href"],{root:n}),target:e}}function ue(t,{root:n,revision:r}={}){D(r);let e=O(t),s=(l,m)=>{if(!l||typeof l!="object"||Array.isArray(l)||Object.keys(l).some(w=>!m.includes(w)))throw Error("Invalid DAV baseline structure")};s(e,["D:multistatus"]);let o=e["D:multistatus"];if(s(o,["D:response"]),o["D:response"]?.length!==1)throw Error("One DAV baseline response required");let i=o["D:response"][0];s(i,["D:href","D:propstat"]);let a=L(i["D:href"],{root:n});if(i["D:propstat"]?.length!==1)throw Error("One DAV baseline status required");let f=i["D:propstat"][0];if(s(f,["D:status","D:prop"]),f["D:status"]!=="HTTP/1.1 200 OK")throw Error("DAV baseline status unavailable");let c=f["D:prop"];if(s(c,["D:version-name","D:baseline-collection"]),c["D:version-name"]!==r)throw Error("DAV baseline revision mismatch");s(c["D:baseline-collection"],["D:href"]);let p=L(c["D:baseline-collection"]["D:href"],{root:n});if(!p.endsWith("/"))throw Error("DAV baseline collection must end in slash");return{revision:r,baseline:a,collection:p}}function he({request:t,assertAuthorized:n}={}){if(typeof t!="function"||typeof n!="function")throw Error("DAV chain requires transport and authorization");return async(r,{revision:e,signal:s}={})=>{D(e),N(r.root,r.scope,e);let o=r.root,i=new URL(o).origin,a=o.endsWith("/")?o:o+"/",f=async(m,w)=>{s?.throwIfAborted(),await n(r,{signal:s}),s?.throwIfAborted();let g=await t(m,{origin:i,signal:s,...w});if(s?.throwIfAborted(),await n(r,{signal:s}),s?.throwIfAborted(),!Buffer.isBuffer(g)||g.length>2097152)throw Error("Invalid DAV response bytes");return new TextDecoder("utf-8",{fatal:!0}).decode(g)},c=await f(a,{method:"PROPFIND",depth:0,body:'<D:propfind xmlns:D="DAV:" xmlns:S="http://subversion.tigris.org/xmlns/dav/"><D:prop><S:repository-uuid/><D:version-controlled-configuration/></D:prop></D:propfind>'}),{vcc:p}=de(c,{root:o,target:a,uuid:r.uuid}),l=await f(p,{method:"PROPFIND",depth:0,label:e,body:'<D:propfind xmlns:D="DAV:"><D:prop><D:baseline-collection/><D:version-name/></D:prop></D:propfind>'});return{...ue(l,{root:o,revision:e}),uuid:r.uuid}}}function me(t,{root:n,target:r}={}){let e=O(t),s=(p,l)=>{if(!p||typeof p!="object"||Array.isArray(p)||Object.keys(p).some(m=>!l.includes(m)))throw Error("Invalid DAV property names structure")};s(e,["D:multistatus"]);let o=e["D:multistatus"];if(s(o,["D:response"]),o["D:response"]?.length!==1)throw Error("One DAV property response required");let i=o["D:response"][0];if(s(i,["D:href","D:propstat"]),L(i["D:href"],{root:n})!==r)throw Error("DAV property target mismatch");if(i["D:propstat"]?.length!==1)throw Error("One DAV property status required");let a=i["D:propstat"][0];if(s(a,["D:status","D:prop"]),a["D:status"]!=="HTTP/1.1 200 OK")throw Error("DAV property names unavailable");let f=a["D:prop"];if(f!==""&&(!f||typeof f!="object"||Array.isArray(f)||Object.keys(f).length>1e3||Object.entries(f).some(([p,l])=>!/^[DSPC]:[^:]+$/.test(p)||l!=="")))throw Error("DAV property names must be empty");let c=f===""?[]:Object.keys(f);return{names:c,special:c.includes("P:special")}}var X=t=>t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""");function ve({request:t}={}){if(typeof t!="function")throw Error("Bounded DAV request required");return async(n,{identity:r,assertAuthorized:e,signal:s}={})=>{let o=H(n,r),i=o.args[0],a=he({request:t,assertAuthorized:e}),f=await a(r,{revision:o.pegRevision,signal:s}),c=async(g,u)=>{s?.throwIfAborted(),await e(),s?.throwIfAborted();let d=await t(g,{origin:new URL(r.root).origin,...u,signal:s});if(s?.throwIfAborted(),await e(),s?.throwIfAborted(),!Buffer.isBuffer(d)||d.length>2097152)throw Error("Invalid DAV transport bytes");return d};if(i==="log"){let g=le(n,r);return c(f.collection,{method:"REPORT",body:g.body})}let p=o.path.slice(1).split("/").map(g=>encodeURIComponent(g).replace(/[!'()*]/g,u=>"%"+u.charCodeAt(0).toString(16).toUpperCase())).join("/"),l=L(f.collection+p,{root:r.root});if(i==="info"){let g=await c(l,{method:"PROPFIND",depth:0,body:'<D:propfind xmlns:D="DAV:"><D:prop><D:resourcetype/></D:prop></D:propfind>'}),u=ee(new TextDecoder("utf-8",{fatal:!0}).decode(g),{root:r.root,target:l}),d=o.args.at(-1).slice(0,o.args.at(-1).lastIndexOf("@"));return'<info><entry kind="'+u+'" revision="'+o.pegRevision+'"><url>'+X(d)+"</url><repository><root>"+X(r.root)+"</root><uuid>"+X(r.uuid)+"</uuid></repository></entry></info>"}let m=await c(l,{method:"PROPFIND",depth:0,body:'<D:propfind xmlns:D="DAV:"><D:propname/></D:propfind>'}),w=me(new TextDecoder("utf-8",{fatal:!0}).decode(m),{root:r.root,target:l});if(i==="proplist")return'<properties><target path="'+X(o.args.at(-1).slice(0,o.args.at(-1).lastIndexOf("@")))+'">'+(w.special?'<property name="svn:special"/>':"")+"</target></properties>";if(w.special)throw Error("SVN special content forbidden");return c(l,{method:"GET"})}}function Nt(t,n){return pe(t,n,{transportFormat:"dav",transport:ve({request:fe()})})}export{Nt as createSvnHttpsHost};
|
package/dist/updates.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as u}from"node:fs";var a="@feng0531/dsh-vcs";function i(r){return typeof r=="string"&&/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(r)&&r.length<50}function l(r,s){if(!i(r)||!i(s))return!1;let t=r.split(".").map(BigInt),n=s.split(".").map(BigInt);for(let e=0;e<3;e++)if(t[e]!==n[e])return t[e]>n[e];return!1}var f=JSON.parse(u(new URL("../package.json",import.meta.url),"utf8")).version;function m(){return{packageName:a,runningVersion:f,protocol:1}}async function b({fetcher:r=fetch,signal:s}={}){let t=await r("https://registry.npmjs.org/@feng0531%2fdsh-vcs/latest",{redirect:"error",headers:{Accept:"application/json"},signal:AbortSignal.any([...s?[s]:[],AbortSignal.timeout(15e3)])});if(!t.ok)throw new Error("\u65E0\u6CD5\u67E5\u8BE2 npm \u6700\u65B0\u7248\u672C\uFF08HTTP "+t.status+"\uFF09");if(Number(t.headers.get("content-length"))>256*1024)throw new Error("\u7248\u672C\u4FE1\u606F\u8FC7\u5927");let n=t.body.getReader(),e=0,c=[];try{for(;;){let{value:p,done:g}=await n.read();if(g)break;if(e+=p.length,e>256*1024)throw new Error("\u7248\u672C\u4FE1\u606F\u8FC7\u5927");c.push(p)}}finally{await n.cancel(),n.releaseLock()}let o=JSON.parse(Buffer.concat(c).toString("utf8"));if(o.name!==a||!i(o.version)||!o.dsh?.bundle)throw new Error("npm \u8FD4\u56DE\u7684\u63D2\u4EF6\u4FE1\u606F\u65E0\u6548");return{...m(),latestVersion:o.version,available:l(o.version,f),registry:"https://registry.npmjs.org/",releaseUrl:"https://github.com/f-e-n-g-0531/dsh-vcs/releases/tag/v"+o.version}}export{f as RUNNING_VERSION,b as checkUpdate,m as runtimeInfo};
|
package/docs/RELEASING.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
## 发布内容
|
|
6
6
|
|
|
7
|
+
每次改动完成并验证通过后,主动完成 npm 与 GitHub 正式发布,不停留在本地修改或只推标签。版本使用 `vX.Y.Z`:X 为大版本,Y 为小版本/新增功能,Z 为 BUG 修复;递增前一位时后面的位归零。失败门禁必须先修复,不能为了发布跳过验证。发布不自动安装或重启宿主。
|
|
8
|
+
|
|
7
9
|
每个新版本必须有独立的中文发布说明,放在 `docs/releases/版本号.md`。按“本版变化、安装、限制、验证”组织,不把自动提交列表、开发轮次或内部恢复记录当作用户说明。没有新增功能时明确写出,不夸大改动。
|
|
8
10
|
|
|
9
11
|
更新包版本和锁文件,在变更记录中填写同一版本。核查许可证、第三方声明及公开文件,排除凭据和私人数据。
|
package/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import {readFileSync} from 'node:fs';
|
|
|
3
3
|
import {registerFrontendAssets} from './frontend-assets.mjs';
|
|
4
4
|
const runningFrontend=JSON.parse(readFileSync(new URL('./dist/frontend.json',import.meta.url),'utf8'));
|
|
5
5
|
import * as adapter from './vcs.mjs';
|
|
6
|
+
import {runtimeInfo,checkUpdate} from './dist/updates.mjs';
|
|
6
7
|
import {createSvnHttpsHost} from './dist/svn-host.mjs';
|
|
7
8
|
|
|
8
9
|
export const name = 'dsh-vcs';
|
|
@@ -135,7 +136,7 @@ async function readRepository(api, endpoint, repository, payload, {cwd, mode, si
|
|
|
135
136
|
case 'vcs/history':
|
|
136
137
|
return api.listHistory({...repository}, {snapshot: payload.snapshot, search: payload.search, offset: payload.offset ?? 0, limit: payload.limit ?? 50, signal});
|
|
137
138
|
case 'vcs/status':
|
|
138
|
-
return {cwd, repository: {...repository}, changes: await api.listChanges({...repository}, mode), mode};
|
|
139
|
+
return {cwd, repository: {...repository}, changes: await api.listChanges({...repository}, mode, {signal}), mode};
|
|
139
140
|
case 'vcs/conflict-stages':
|
|
140
141
|
return api.getConflictStages({...repository},{mode,id:payload.id,signal});
|
|
141
142
|
case 'vcs/compare':
|
|
@@ -160,6 +161,15 @@ export function createHandler(ctx, api = adapter, maxActive = 4, { now = Date.no
|
|
|
160
161
|
}
|
|
161
162
|
if(bindSvnIdentityResolver!==undefined){if(typeof bindSvnIdentityResolver!=='function')throw new Error('Invalid internal SVN resolver binding');bindSvnIdentityResolver(async(address,signal)=>{validatePayload('vcs/svn-identity',address);signal?.throwIfAborted();const cwd=await resolveSessionCwd(ctx,address.sessionId,signal),entry=current(address.sessionId,cwd),repository=entry?.repositories.get(address.repositoryId);if(!repository)throw rediscover();if(repository.type!=='svn')throw invalid('SVN working copy required.');const identity=await api.getSvnIdentity({...repository},{signal});await assertCurrent(address.sessionId,cwd,entry,signal);if(entry.repositories.get(address.repositoryId)!==repository)throw rediscover();return {...identity,cwd};});}
|
|
162
163
|
return async (endpoint, payload, signal) => {
|
|
164
|
+
if (['vcs/runtime-info','vcs/check-update'].includes(endpoint)) {
|
|
165
|
+
try {
|
|
166
|
+
signal?.throwIfAborted();
|
|
167
|
+
if (!payload || typeof payload!=='object' || Array.isArray(payload) || Object.keys(payload).length) throw invalid('Update requests accept no fields.');
|
|
168
|
+
if (endpoint==='vcs/runtime-info') return {ok:true,value:runtimeInfo()};
|
|
169
|
+
if (active>=maxActive) return failure('vcs/busy','Too many VCS requests. Please retry.');
|
|
170
|
+
active++;try { return {ok:true,value:await checkUpdate({signal})}; } finally { active--; }
|
|
171
|
+
} catch(error) { return failure(signal?.aborted?'vcs/cancelled':'vcs/update-failed',error.message); }
|
|
172
|
+
}
|
|
163
173
|
if (!['vcs/svn-identity', 'vcs/repositories', 'vcs/status', 'vcs/compare', 'vcs/conflict-stages', 'vcs/history', 'vcs/commit', 'vcs/commit-compare', 'vcs/revision-changes', 'vcs/revision-compare', 'vcs/file-history', 'vcs/blame', 'vcs/tree', 'vcs/tree-file', 'vcs/tree-segment', 'vcs/tree-history', 'vcs/commit-image', 'vcs/revision-image', 'vcs/workspace-image', 'vcs/references', 'vcs/stash-list', 'vcs/grep'].includes(endpoint)) return failure('vcs/not-found', 'Unknown VCS endpoint.');
|
|
164
174
|
if (active >= maxActive) return failure('vcs/busy', 'Too many VCS requests. Please retry.');
|
|
165
175
|
active++;
|
package/package.json
CHANGED
package/vcs.mjs
CHANGED
|
@@ -257,11 +257,11 @@ export async function searchRepositoryContent(repo,{commit,query,signal}={}) {
|
|
|
257
257
|
catch(error){if(error.code==='VCS_COMMAND'&&error.exitCode===1)return {matches:[]};throw error;}
|
|
258
258
|
signal?.throwIfAborted();
|
|
259
259
|
const matches=[];
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
if(
|
|
264
|
-
const
|
|
260
|
+
// -z frames path and line with NUL; legal paths can contain LF.
|
|
261
|
+
for(let position=0;position<text.length;){
|
|
262
|
+
const pathEnd=text.indexOf(String.fromCharCode(0),position),lineEnd=text.indexOf(String.fromCharCode(0),pathEnd+1),recordEnd=text.indexOf(String.fromCharCode(10),lineEnd+1);
|
|
263
|
+
if(pathEnd<position||lineEnd<0||recordEnd<0)throw new Error('Invalid content search result');
|
|
264
|
+
const treePath=text.slice(position,pathEnd),lineNo=text.slice(pathEnd+1,lineEnd),lineText=text.slice(lineEnd+1,recordEnd);position=recordEnd+1;
|
|
265
265
|
const cut=treePath.indexOf(':');
|
|
266
266
|
if(cut<0)throw new Error('Invalid content search result');
|
|
267
267
|
const path=treePath.slice(cut+1);
|
|
@@ -604,7 +604,7 @@ async function changes(repo, mode, options) {
|
|
|
604
604
|
}
|
|
605
605
|
await annotateSubmodules(repo, entries, options);
|
|
606
606
|
} else {
|
|
607
|
-
const document = parseXML(await svn(repo.root, ['status', '--xml', '--ignore-externals', '--', '.']));
|
|
607
|
+
const document = parseXML(await svn(repo.root, ['status', '--xml', '--ignore-externals', '--', '.'], undefined, options));
|
|
608
608
|
entries = [];
|
|
609
609
|
for (const target of document.status?.target || []) for (const item of target.entry || []) {
|
|
610
610
|
const state = item['wc-status'];
|
|
@@ -617,7 +617,7 @@ async function changes(repo, mode, options) {
|
|
|
617
617
|
return entries.map(entry => ({ ...entry, id: identity(entry, mode) }));
|
|
618
618
|
}
|
|
619
619
|
/** Changes have opaque IDs, status words, and repo-relative paths. */
|
|
620
|
-
export async function listChanges(repo, mode = 'all') { repo = await checkedRepo(repo); modeFor(repo, mode);
|
|
620
|
+
export async function listChanges(repo, mode = 'all', {signal} = {}) { repo = await checkedRepo(repo,signal); modeFor(repo, mode); const result=await changes(repo, mode,{signal});signal?.throwIfAborted();return result; }
|
|
621
621
|
// Keep the public adapter decoding export compatible with existing consumers.
|
|
622
622
|
export {decode} from './text-content.mjs';
|
|
623
623
|
async function content(fn) { try { return decode(await fn()); } catch (e) { if (e.code === 'TOO_LARGE') return { text: '', notice: 'File exceeds the 2 MiB preview limit.' }; throw e; } }
|