Files
op-packages/luci-app-honk/root/etc/honk/dashboard/assets/index-ChxA9-bE.js
T

83 lines
85 KiB
JavaScript

import{A as h,m as Nt,o as ct,p as Ot,q as me,t as Y,v as Dt,x as Te,y as ae,z as Ct,C as Rt,D as jt,F as Pt,G as Et,H as Lt,J as dt,K as Tt,M as Gt,N as we,O as zt,P as se,Q as Be,R as Ut,U as Ae,V as ue}from"./index-CXHBmSa5.js";import{d as re,m as Ve,v as Ft,g as Ht,a as Bt}from"./geodata-DprMPspn.js";import{mockSessionValid as Vt,refuseWithoutSession as Jt}from"./auth-DmBaDwOI.js";import"./vendor-react-Bf71BWbF.js";import"./auth-BGnwk8Hi.js";const Ie=Date.now(),B=e=>new Date(Ie-e*1e3).toISOString(),te=e=>new Date(Ie+e*1e3).toISOString(),V=B(0),K="mock-instance-1",De="40",ee={observed_at:V,profiles:["base","full_transparency"],limits:{max_request_target_bytes:4096,max_header_bytes:16384,max_json_body_bytes:65536},resources:{runtime:{available:!0},runtime_memory:{available:!0,metrics:["process.rss_bytes","cgroup.current_bytes","cgroup.limit_bytes","cgroup.events.high","cgroup.events.oom","cgroup.events.oom_kill","kernel.ebpf_bytes"]},datapath:{available:!0,kinds:["ebpf"],details:["attachments","maps"]},runtime_outbounds:{available:!0},traffic_history:{available:!0,max_window_seconds:604800,max_points:360},memory_history:{available:!0,max_window_seconds:604800,max_points:720},nodes:{available:!0,can_manage:!0},providers:{available:!0,can_refresh:!0,can_manage:!0,create_options:{update_interval:86400,user_agent:"honk/0.0.1-alpha",cache:!0},max_page_size:1e3},geodata:{available:!0,can_update:!0,assets:["geosite","geoip"],configurable_sources:!0},rules:{available:!0,max_rules:4096},config:{available:!0,content:!0,writable:!0,create:!0,max_bytes:1048576,max_sources:32},config_validate:{available:!0,modes:["syntax","full"],max_bytes:1048576,max_sources:32},logs:{available:!0,levels:["trace","debug","info","warn","error"],max_buffered_records:4096},dns_log:{available:!0,max_records:2048,max_page_size:500},runtime_settings:{available:!0,fields:["log.level","log.buffered_records","dns_log.max_records","flows.max_flows","flows.retention_seconds","record_flows","record_logs","record_dns_log","geodata"]},groups:{available:!0,config_patch:!0,selection:!0,max_patch_operations:32},probes:{available:!0,targets:["node","group"],kinds:["tcp_connect","http","dns"],purposes:["data","dns"],transports:["tcp","udp"],ip_versions:["ipv4","ipv6"],limits:{max_members_per_job:64,max_results_per_job:256,max_active_jobs:4,max_queued_jobs:16,max_concurrent_per_target:1,job_timeout_ms:3e4,per_principal_requests_per_minute:60,global_requests_per_minute:120}},connections:{available:!0,can_close:!0,max_bulk_close:200},flows:{available:!0,recording:"on",scopes:["userspace_tcp","userspace_udp","kernel_direct","kernel_block","dns_intercept","kernel_bypass"],max_flows:4096,max_steps_per_flow:64,retention_seconds:300,snapshot_ttl_seconds:60,max_page_size:1e3},routing_trace:{available:!0,resolve_modes:["none","live"],max_addresses:16,max_rule_steps:128,timeout_ms:5e3,per_principal_requests_per_minute:60,global_requests_per_minute:120},events:{available:!0,kinds:["stream.ready","runtime.updated","flow.updated","flow.gap","operation.updated","generation.changed"],retention_seconds:300,max_buffered_events:1024,max_clients:16,heartbeat_seconds:15},dns_query:{available:!0,record_types:["A","AAAA","HTTPS"],limits:{max_types_per_request:8,query_timeout_ms:5e3,max_response_bytes:65536,per_principal_requests_per_minute:60,global_requests_per_minute:120}},dns_cache:{available:!0,read:!0,delete_entry:!0,delete_name:!0,flush:!0,entry_kinds:["positive","negative"]},operations:{available:!0,retention_seconds:300},reload:{available:!0},suspend:{available:!0},resume:{available:!0}}},Wt={...ee,profiles:["base"],resources:{...ee.resources,runtime_outbounds:{available:!1},traffic_history:{available:!1},memory_history:{available:!1},logs:{available:!1},dns_log:{available:!1},runtime_settings:{available:!1},geodata:{available:!1},rules:{available:!1},nodes:{available:!0,can_manage:!1},providers:{available:!0,can_refresh:!1,can_manage:!1,max_page_size:1e3},config:{available:!1,content:!1,create:!1},config_validate:{available:!1},flows:{...ee.resources.flows,available:!1},routing_trace:{...ee.resources.routing_trace,available:!1},events:{...ee.resources.events,available:!1}}},Kt={...ee,profiles:["base"],resources:{...Object.fromEntries(Object.keys(ee.resources).map(e=>[e,{available:!1}])),runtime:{available:!0},connections:{available:!0,can_close:!1,max_bulk_close:1e3},config:{available:!1,content:!1,create:!1}}},Ge=[{id:"r1",cond:"pname(NetworkManager, systemd-resolved) && l4proto(udp) && dport(53)",target:"direct",must:!0},{id:"r2",cond:"dip(geoip: private)",target:"direct",must:!0},{id:"r3",cond:"domain(suffix: doubleclick.net)",target:"block",must:!1},{id:"r4",cond:"domain(geosite: cn)",target:"direct",must:!1},{id:"r5",cond:"domain(geosite: telegram)",target:"proxy",must:!1},{id:"r6",cond:"mac(aa:bb:cc:dd:ee:ff) && ipversion(4)",target:"direct",must:!1},{id:"r7",cond:"domain(geosite: openai)",target:"proxy",must:!1},{id:"r8",cond:"sip(10.0.0.0/24) && dport(25)",target:"block",must:!1}],ze=[{id:"r9",cond:"domain(geosite: category-ads-all)",target:"block",must:!1}],pe={proxy:{kind:"selector",native:"fixed(0)"},resilient:{kind:"urltest",native:"min_avg10"},gaming:{kind:"urltest",native:"min_last_delay"},skylink:{kind:"urltest",native:"min_moving_avg"}},Ce={observed_at:V,source:"config",log:{level:"info",buffered_records:1024},dns_log:{max_records:2048},flows:{max_flows:4096,retention_seconds:300},recording:{flows:{allowed:!0,mode:"auto",active:!0},logs:{allowed:!0,mode:"auto",active:!0},dns_log:{allowed:!0,mode:"auto",active:!0},events:{active:!0},grace_remaining_seconds:0}},Xt={...Ce,recording:{...Ce.recording,dns_log:{allowed:!1,mode:"auto",active:!1}}},ut={rules:Ge,fallback:{target:"resilient"}},Qt=`global {
tproxy_port: 12345
log_level: info
lan_interface: br-lan
wan_interface: auto
allow_insecure: false
auto_config_kernel_parameter: true
}
subscription {
sub-c: 'https://sub.example.net/api/v1/client/subscribe?token=demo'
}
node {
'hk-01': 'vless://demo@hk-01.example.net:443?security=tls#hk-01'
'hk-02': 'vless://demo@hk-02.example.net:443?security=tls#hk-02'
'sg-01': 'trojan://demo@sg-01.example.net:443#sg-01'
'jp-01': 'vless://demo@jp-01.example.net:443?security=tls#jp-01'
'us-01': 'anytls://demo@us-01.example.net:443#us-01'
}
group {
proxy { policy: ${pe.proxy.native} }
resilient { filter: name(hk-01, sg-01, us-01) policy: ${pe.resilient.native} }
gaming { filter: name(jp-01, hk-02) policy: ${pe.gaming.native} }
skylink { filter: subtag(sub-c) policy: ${pe.skylink.native} }
}
dns {
upstream {
cloudflare: 'tls://1.1.1.1:853'
alidns: 'udp://223.5.5.5:53'
}
routing {
request { qname(geosite: cn) -> alidns; fallback: cloudflare }
}
}
routing {
pname(NetworkManager, systemd-resolved) && l4proto(udp) && dport(53) -> direct(must)
dip(geoip: private) -> direct(must)
domain(suffix: doubleclick.net) -> block
domain(geosite: cn) -> direct
domain(geosite: telegram) -> proxy
include rules.dae
fallback: resilient
}
include {
config.d/*.dae
}
`,Yt=`# Household exceptions, kept apart from config.dae.
# The TV never leaves through a node.
mac(aa:bb:cc:dd:ee:ff) && ipversion(4) -> direct
# AI
domain(geosite: openai) -> proxy
sip(10.0.0.0/24) && dport(25) -> block
`,Je=`'香港 01 IPLC': 'vless://<redacted>'
'香港 02 BGP': 'vless://<redacted>'
'新加坡 01 2x': 'trojan://<redacted>'
'日本 01 2x': 'vless://<redacted>'
`,Zt=`# Written by honk from the subscription; edits are lost on refresh.
skylink { filter: subtag(sub-c) }
`,qt=[{level:"warning",source_id:"src-main",line:5,column:3,span:null,code:"interface_auto",message:"wan_interface: auto is resolved at start; a changed default route needs a reload"},{level:"warning",source_id:"src-rules",line:3,column:1,span:null,code:"mac_unseen",message:"mac(aa:bb:cc:dd:ee:ff) has not been seen on the LAN since start"},{level:"info",source_id:"src-sub-c",line:1,column:null,span:null,code:"subscription_cached",message:"Subscription fetched 30 minutes ago; nodes come from the cache"}],pt=[{id:"src-main",path:"/etc/honk/config.dae",kind:"main",writable:!0,loaded_at:B(3600),content:Qt},{id:"src-rules",path:"/etc/honk/rules.dae",kind:"include",writable:!0,loaded_at:B(3600),content:Yt},{id:"src-sub-c",path:"/var/lib/honk/subscriptions/sub-c.dae",kind:"subscription",writable:!1,loaded_at:B(1800),content:Je,onDisk:Je.replaceAll("<redacted>","demo@edge.example.net:443")},{id:"src-generated",path:"/var/lib/honk/generated/skylink.dae",kind:"generated",writable:!1,loaded_at:B(1800),content:Zt}],ft=pt.map(e=>e.id==="src-rules"?{...e,content:e.content+`
# Ads
`+ze.map(n=>`${n.cond} -> ${n.target}
`).join("")}:e),en=ft.map(e=>e.id==="src-rules"?{...e,content:e.content+`domain(suffix: example.org) -> direct
`}:e);function _e(e){const n=new Map;return(o,r={})=>{const{cursor:a,limit:c=1e3,...l}=r,i=Nt([e,l]);if(!Number.isSafeInteger(c)||c<1)throw new h(400,"invalid_request","Invalid page size");for(const[b,w]of n)w.expires<=Date.now()&&n.delete(b);const[d,s,p]=a?.split(":")??[ct(),"0"],v=Number(s);let m=a?n.get(d):void 0;if(a&&(p!==void 0||!m||m.key!==i||!Number.isSafeInteger(v)||v<1||v>=m.items.length))throw new h(e==="flows"?410:400,e==="flows"?"snapshot_expired":"invalid_request","Unknown or expired cursor");if(!m){if(o.length<=c)return{items:structuredClone(o),total:o.length,next_cursor:null};m={items:structuredClone(o),key:i,expires:Date.now()+3e4},n.size>=32&&n.delete(n.keys().next().value),n.set(d,m)}const _=v+c;return{items:structuredClone(m.items.slice(v,_)),total:m.items.length,next_cursor:_<m.items.length?`${d}:${_}`:null}}}function W(e,n){if(e===void 0)throw new h(404,"resource_not_found",`${n} not found`);return e}function Re(e){const n=URL.parse(e);return n?n.origin+n.pathname+(n.search?"?[redacted]":""):null}const tn=new Set(["global","subscription","node","group","dns","routing","upstream","request","response","include"]),We=new Set(Pt),nn=new Set(jt),on=e=>e===""?0:e.replace(/\n$/,"").split(`
`).length;async function ce({onDisk:e,...n}){const o=e??n.content;return{...n,content_sha256:await Ot(o),bytes:new TextEncoder().encode(o).length,line_count:on(o)}}function ge(e,n,o=!1){const{blocks:r}=ae(e),a=r.filter(c=>c.name===n).flatMap(c=>Ct(e,c).filter(l=>!l.block));return o&&!r.length&&a.push({from:0,to:e.length,block:void 0}),a.flatMap(({from:c,to:l})=>{const i=e.slice(0,c).split(`
`).length;return e.slice(c,l).split(`
`).map((d,s)=>({code:Rt(d).trim(),raw:d,line:i+s}))})}function ve(e){return[...ge(e,"include").filter(({code:n})=>n).map(({code:n,line:o})=>({path:me(n),line:o})),...ge(e,"routing",!0).flatMap(({code:n,line:o})=>{const r=/^include\s+(\S+)$/.exec(n);return r?[{path:me(r[1]),line:o}]:[]})]}const Ke=(e,n,o)=>e.filter(r=>Te(Y(n,o),Y(void 0,r.path)));function mt(e,n,o,r){const a=[],c=(s,p,v,m,_)=>a.push({level:v,source_id:e,line:s,column:p,span:null,code:m,message:_}),{blocks:l,tokens:i}=ae(n),d=s=>{s.depth===0&&!tn.has(s.name)&&c(s.line+1,1,"error","unknown_section",`Unknown section "${s.name}"`),s.close===n.length&&c(s.line+1,1,"error","section_not_closed",`Section "${s.name}" is never closed`),s.children.forEach(d)};l.forEach(d);for(const s of i)s.kind==="symbol"&&s.depth===0&&n[s.from]==="}"&&c(s.line+1,1,"error","brace_without_section","Closing brace without an open section");for(const{code:s,line:p}of ge(n,"global")){if(!s)continue;const v=/^([\w.-]+)\s*:\s*(.*)$/.exec(s);v?nn.has(v[1])||c(p,1,"error","unknown_key",`Unknown global setting "${v[1]}"`):c(p,1,"error","not_a_setting",'Expected "<key>: <value>"')}return ge(n,"routing",!0).forEach(({code:s,raw:p,line:v})=>{if(!s||/^include\s+\S+$/.test(s))return;const m=/^fallback:\s*(\S+)$/.exec(s),_=/^(.+?)\s*->\s*(\S+)$/.exec(s),b=m?.[1]??_?.[2];if(!b){c(v,1,"error","not_a_rule",'Expected "<condition> -> <outbound>", "include <file>" or "fallback: <outbound>"');return}const w=b.replace(/\(.*\)$/,"");r==="full"&&!We.has(b)&&!We.has(w)&&!o.has(w)&&c(v,p.lastIndexOf(b)+1,"error","unknown_outbound",`No group named "${w}"`),_&&!/\w\(/.test(_[1])&&c(v,1,"warning","bare_condition","Condition has no function call; it will never match")}),a}function sn(e){return new Set(ae(e).blocks.filter(n=>n.name==="group").flatMap(n=>n.children.map(o=>o.name)))}function $e(e,n,o=[]){const r=e.sources.map((l,i)=>({...l,id:l.id??`source-${i+1}`}));if(new Set(r.map(l=>l.id)).size!==r.length)throw new h(400,"invalid_request","Source IDs must be unique");const a=[];if(e.mode==="full"){const l=r[0]?.path,i=new Map([...o,...r].filter(s=>s.path).map(s=>[Y(void 0,s.path),s])),d=new Set(r.map(s=>s.path&&Y(void 0,s.path)));for(let s=0;s<r.length;s++){const p=r[s];for(const{path:v,line:m}of ve(p.content)){const _=Y(l,v);if(Dt(v)){for(const[w,N]of i)Te(_,w)&&!d.has(w)&&(d.add(w),r.push({...N,id:N.id??`source-${r.length+1}`}));continue}const b=i.get(_);b?d.has(_)||(d.add(_),r.push({...b,id:b.id??`source-${r.length+1}`})):a.push({source_id:p.id,line:m,column:1,span:null,level:"error",code:"include_not_found",message:`Include "${v}" cannot be resolved`})}}}const c=e.mode==="full"?new Set(r.flatMap(l=>[...sn(l.content)])):new Set;return a.push(...r.flatMap(l=>mt(l.id,l.content,c,e.mode))),{valid:!a.some(l=>l.level==="error"),diagnostics:a,generation_id:n,validated_at:new Date().toISOString()}}function rn(e,n,{enqueue:o,log:r,publish:a,eventData:c,trimLogs:l},i,d,s,p=!1){const v=p?Xt:Ce,m=structuredClone(v),_=()=>{const k=structuredClone(m);return e.resources.geodata.configurable_sources===!0&&(k.geodata=s.settings()),k};let b=null,w=[],N;const C=()=>N??=Promise.all([Promise.all((p?ft:pt).map(ce)),p?Promise.all(en.map(ce)):null]).then(([k,$])=>(b=k,w=$??[...k],k));let t=40,f=0;const u=k=>k.kind==="main"||k.kind==="include";function g(k,$=w){const j=[],E=$.find(U=>U.kind==="main"),H=U=>{if(j.some(M=>M.id===U.id))return;const y=U.id===k?.id?k.content:U.content;j.push({id:U.id,path:U.path,content:y});for(const{path:M}of ve(y))Ke($,E.path,M).forEach(H)};return E&&H(E),j}function A(k,$){const j=H=>new TextEncoder().encode(H).length;if(j(JSON.stringify($))>e.limits.max_json_body_bytes)throw new h(413,"request_too_large","Request body exceeds its limit");const E=e.resources.config.max_bytes;if(E!==void 0&&j(k)>E)throw new h(413,"request_too_large",`Source content exceeds ${E} bytes`)}function S(){const k=String(t+1),$=g();$.length&&d($.map(E=>E.content).join(`
`),k),b&&(b=[...w],s.follow(G(b).rules.flatMap(E=>E.kind==="rule"?[E.expression]:[]))),t+=1,Object.assign(m,structuredClone(v),{observed_at:new Date().toISOString()}),r("info","honk::routing","Routing generation published.",{generation_id:String(t)});const j=String(t);return n.generation={...n.generation,active_id:j,config_revision:j,activated_at:new Date().toISOString()},a({id:"",event:"generation.changed",data:{...c(),generation_id:j,previous_generation_id:String(t-1)}}),j}async function O(k){await C();const $=W(w.find(E=>E.kind==="main"),"Main source"),j=await ce({...$,content:k($.content),loaded_at:new Date().toISOString()});return()=>{if(w.find(E=>E.id===$.id)!==$)throw new h(412,"stale_revision","The main source changed before activation");return w=w.map(E=>E.id===$.id?j:E),S()}}function G(k){const $=k.find(I=>I.kind==="main"),j=new Map(k.map(I=>[Y(void 0,I.path),I])),E=new Map([...ut.rules,...ze].map(I=>[I.cond+" -> "+I.target+(I.must?"(must)":""),I.id])),H=[];let U=null;const y=new Set,M=(I,P)=>{if(!y.has(I.id)){y.add(I.id),ge(I.content,"routing",P).forEach(({code:R,raw:F,line:D})=>{const T=/^include\s+(\S+)$/.exec(R);if(T){const He=j.get(Y($.path,T[1]));He&&M(He,!0);return}const L=/^fallback:\s*(\S+)$/.exec(R),X=/^(.+?)\s*->\s*(\S+)$/.exec(R);if(!L&&!X)return;const Q=F.search(/\S/),At=new TextEncoder().encode(F.slice(0,Q)).length+1,le={file:I.path.split("/").pop(),source_id:I.id,line:D,column:At};if(L){U={outbound:L[1],source:le},H.push({rule_id:"fallback",index:H.length,expression:R,outbound:L[1],must:!1,source:le,kind:"fallback"});return}const $t=X[2].endsWith("(must)"),Mt=X[2].replace(/\(must\)$/,"");H.push({rule_id:E.get(R)??`${le.file}:${le.line}`,index:H.length,expression:X[1],outbound:Mt,must:$t,source:le,kind:"rule"})});for(const{path:R}of ve(I.content))Ke(k,$.path,R).forEach(F=>M(F,!0))}};return $&&M($,!1),{rules:H,fallback:U}}const z=async()=>{await C();const{rules:k,fallback:$}=G(b);if(!$)throw new h(503,"snapshot_unavailable","The routing section has no fallback",null,null,1);return{generation_id:String(t),rules:k,fallback:$}};return{api:{rules:async k=>{if(k?.throwIfAborted(),!e.resources.rules.available)throw new h(404,"capability_not_supported","The rule list is unavailable");return z()},config:async k=>{if(k?.throwIfAborted(),!e.resources.config.available)throw new h(404,"capability_not_supported","Configuration readback is unavailable");await C();const $=b,j=String(t),E=i();return{generation_id:j,revision:j,sources:$.map(H=>e.resources.config.content?{...H}:{...H,content:void 0}),diagnostics:[...$.filter(u).flatMap(H=>mt(H.id,H.content,E,"full").filter(U=>U.level!=="error")),...p?qt:[]],secrets_redacted:!0}},validateConfig:async(k,$)=>{if($?.throwIfAborted(),!e.resources.config_validate.available)throw new h(404,"capability_not_supported","Validation is unavailable");if(!e.resources.config_validate.modes?.includes(k.mode))throw new h(400,"invalid_request",`Mode ${k.mode} is not advertised`);return k.mode==="full"&&await C(),$e(k,String(t),w.filter(u))},replaceConfigSource:async(k,$,j,E)=>{E?.throwIfAborted(),await C();const U=W(w.find(I=>I.id===k),"Configuration source");if(!e.resources.config.writable||!U.writable)throw new h(403,"permission_denied","This source is read-only");if(A($,{content:$}),!j)throw new h(428,"precondition_required","If-Match is required");if(j.replace(/^"|"$/g,"")!==U.content_sha256)throw new h(412,"stale_revision","The source changed on disk; fetch it again before retrying");const y=$e({sources:g({id:k,content:$}),mode:"full"},String(t));if(!y.valid)throw new h(422,"unsupported_value","Validation found errors; nothing was written",null,{diagnostics:y.diagnostics});const M=await ce({...U,content:$,loaded_at:new Date().toISOString()});return w=w.map(I=>I.id===k?M:I),r("info","honk::config","Configuration source replaced; reloading.",{source_id:k}),o("reload",()=>{const I=S();return{active_generation_id:I,datapath_generation_id:I}})},createConfigSource:async(k,$,j)=>{j?.throwIfAborted();const E=e.resources.config;if(!E.available||!E.create)throw new h(404,"capability_not_supported","Creating configuration sources is unavailable");if(!E.writable)throw new h(403,"permission_denied","Configuration writes are disabled");if(A($,{path:k,content:$}),Et(k)||new TextEncoder().encode(k).length>1024)throw new h(400,"invalid_request","path must be a relative .dae path with normal segments");await C();const H=W(w.find(R=>R.kind==="main"),"Main source"),U=Y(H.path,k),y=()=>{if(w.some(R=>Y(void 0,R.path)===U))throw new h(409,"state_conflict",`${k} already exists`);if(E.max_sources!==void 0&&w.length>=E.max_sources)throw new h(413,"request_too_large","Source count exceeds the advertised max_sources")};if(y(),!g().some(R=>ve(R.content).some(F=>Te(Y(H.path,F.path),U)))){const R={level:"error",source_id:H.id,line:null,column:null,span:null,code:"source-not-included"};throw new h(422,"unsupported_value","No include pattern matches the path; nothing was written",null,{diagnostics:[{...R,message:`No include pattern matches ${k}`}]})}const I=await ce({id:`src-new-${++f}`,path:U,kind:"include",writable:!0,loaded_at:new Date().toISOString(),content:$});y();const P=$e({sources:g(void 0,[...w,I]),mode:"full"},String(t));if(!P.valid)throw new h(422,"unsupported_value","Validation found errors; nothing was written",null,{diagnostics:P.diagnostics});return w=[...w,I],r("info","honk::config","Configuration source created; reloading.",{source_id:I.id}),o("reload",()=>{const R=S();return{active_generation_id:R,datapath_generation_id:R}})},runtimeSettings:async k=>{if(k?.throwIfAborted(),!e.resources.runtime_settings.available)throw new h(404,"capability_not_supported","Runtime settings are unavailable");return _()},patchRuntimeSettings:async(k,$)=>{$?.throwIfAborted();const j=e.resources;if(!j.runtime_settings.available)throw new h(404,"capability_not_supported","Runtime settings are unavailable");const E=new Set(j.runtime_settings.fields??[]),H={"log.buffered_records":j.logs.max_buffered_records??0,"dns_log.max_records":j.dns_log.max_records??0,"flows.max_flows":j.flows.max_flows??0,"flows.retention_seconds":j.flows.retention_seconds??0},U=D=>new h(400,"invalid_request",D),{geodata:y,...M}=k;if(y!==void 0&&(!E.has("geodata")||j.geodata.configurable_sources!==!0))throw U("geodata cannot be changed on this backend");k=M;const I={record_flows:"flows",record_logs:"logs",record_dns_log:"dns_log"},P=Object.entries(I).flatMap(([D,T])=>{const L=k[D];return L===void 0?[]:[[D,T,L]]});for(const[D,T,L]of P){if(!E.has(D))throw U(`${D} cannot be changed on this backend`);if(L!=="auto"&&typeof L!="boolean")throw U(`${D} must be true, false or auto`);if(L===!0&&!m.recording?.[T].allowed)throw U(`${D} is forbidden by the configuration`)}const R=Object.entries(k).filter(([D])=>!(D in I)).flatMap(([D,T])=>Object.entries(T??{}).map(([L,X])=>[`${D}.${L}`,X]));for(const[D,T]of R){if(!E.has(D))throw U(`${D} cannot be changed on this backend`);if(D==="log.level"){if(!(j.logs.levels??[]).includes(T))throw U(`${T} is not an advertised log level`);continue}const L=H[D],X=D==="flows.retention_seconds"?1:64;if(!Number.isInteger(T)||T<X||T>L)throw U(`${D} must lie in [${X}, ${L}]`)}if(y!==void 0&&(s.patch(y),!Object.keys(M).length))return r("info","honk::geodata","Geodata settings stored.",{}),_();const F=D=>Object.assign(m[D],k[D]??{});F("log"),F("dns_log"),F("flows");for(const[,D,T]of P){const L=m.recording[D];L.mode=T==="auto"?"auto":T?"on":"off",L.active=L.allowed&&L.mode!=="off"}return m.recording&&(m.recording.events.active=!0),l(),m.source="runtime",m.observed_at=new Date().toISOString(),r("info","honk::settings","Runtime settings changed.",{fields:R.map(([D])=>D)}),_()},startReload:async k=>(k?.throwIfAborted(),await C(),o("reload",()=>{const $=S();return{active_generation_id:$,datapath_generation_id:$}})),startSuspend:async k=>(k?.throwIfAborted(),o("suspend",()=>(n.lifecycle.state="suspended",{runtime_state:"suspended"}))),startResume:async k=>(k?.throwIfAborted(),o("resume",()=>(n.lifecycle.state="running",{runtime_state:"running"})))},ruleSnapshot:z,advance:S,editMain:O,revision:()=>String(t),logSettings:()=>m.log}}function _t(e){return{default_member_id:e,final_outbound:null,check_url:null,check_interval:30,tolerance:10,idle_timeout:null,interrupt_connections:!1}}function gt(e){return{can_select:e==="selector",can_override:e!=="selector",supports_nested_groups:!0,mutable_config:["policy","default_member_id",...e==="selector"?[]:["check_url"],"tolerance","interrupt_connections"],probe_transports:["tcp","udp"]}}function Me(e,n,o="ipv4"){return{transport:e,purpose:e==="tcp"?"data":"dns",ip_version:o,warmth:"warm",measurement:e==="tcp"?"tcp_connect":"dns_round_trip",sample_source:"probe",state:n===null?"unavailable":"healthy",latency_ms:n,moving_avg_ms:null,avg10_ms:null,observed_at:V,error:n===null?"probe_failed":null}}function ne(e,n,o,r,a,c="shadowsocks"){return{id:e,name:e,protocol:c,subscription_tag:a,provider_id:a==="inline"?"inline":a,group_ids:[],health:[Me("udp",o),Me("tcp",n),...r?[Me("tcp",n,"ipv6")]:[]]}}function an(e){return e.runtime.health.filter(o=>o.state==="healthy"&&o.transport==="tcp"&&o.latency_ms!=null).sort((o,r)=>o.latency_ms-r.latency_ms)[0]?.member_id??e.members[0].id}function be(e,n,o,r){const a=pe[e],{kind:c}=a,l=new Set(n),i=new Set(r.map(s=>s.id));for(const s of r)l.has(s.id)&&s.group_ids.push(e);const d={member_id:o,resolved_leaf_node_id:o,source:c==="selector"?"runtime":"policy"};return{id:e,name:e,icon:null,config_revision:"40",policy:{...a},members:n.map(s=>({id:s,name:s,kind:i.has(s)?"node":"group"})),config:_t(c==="selector"?o:null),runtime:{selection:{tcp:d,udp:e==="proxy"&&l.has("hk-02")?{...d,member_id:"hk-02",resolved_leaf_node_id:"hk-02"}:{...d}},health:r.filter(s=>l.has(s.id)).flatMap(s=>s.health.map(p=>({...p,member_id:s.id,resolved_leaf_node_id:s.id,sorting_latency_ms:p.latency_ms,ranking:null})))},capabilities:gt(c)}}function ln(e,n=!1){const o=[ne("hk-01",84,91,!0,"inline","vless"),ne("hk-02",91,88,!0,"inline","vless"),ne("sg-01",63,70,!1,"inline","trojan"),ne("jp-01",n?null:132,n?null:139,!1,"inline","vless"),ne("us-01",188,201,!0,"inline","anytls")],r=[be("proxy",["hk-01","hk-02","sg-01","jp-01","us-01","resilient"],"hk-01",o),be("resilient",["hk-01","sg-01","us-01"],"sg-01",o),be("gaming",["jp-01","hk-02"],"hk-02",o)],a=[["香港",60],["台灣",40],["日本",30],["新加坡",40],["美國",160],["韓國",50],["英國",120],["德國",130],["澳洲",150],["土耳其",170]],c=["IPLC","BGP","家寬","解鎖","0.5x","2x","流媒體",""];let l=7;const i=()=>(l=l*48271%2147483647,l/2147483647),d=[];for(let s=0;s<e;s++){const[p,v]=a[s%a.length],m=String(Math.floor(s/a.length)+1).padStart(2,"0"),_=c[Math.floor(i()*c.length)],b=i()>.06||!n,w=Math.round(v+i()*v*.8),N=b?w+Math.round(i()*20):null;d.push(ne(p+" "+m+(_?" "+_:""),b?w:null,N,i()>.5,"sub-c"))}return d.length&&(o.push(...d),r.push(be("skylink",d.map(s=>s.id),d[0].id,o))),{nodes:o,groups:r}}const cn=[{id:"sub-c",name:"sub-c",kind:"subscription",url_redacted:"https://sub.example.net/api/v1/client/subscribe?token=<redacted>",node_count:120,updated_at:B(1800),expires_at:new Date(Ie+23*86400*1e3).toISOString(),traffic:{upload_bytes:"48318382080",download_bytes:"412316860416",total_bytes:"1099511627776"},status:"ok",last_error:null},{id:"inline",name:"config.dae",kind:"inline",url_redacted:null,node_count:5,updated_at:B(3600),expires_at:null,traffic:null,status:"ok",last_error:null}],de={code:"fetch_failed",message:"Subscription host answered HTTP 502",details:null},dn=["vless","vmess","trojan","trojan-go","ss","ssr","socks5","http","https","hysteria2","hy2","tuic","juicity","anytls"],un={observed_at:V,assets:[{kind:"geosite",sha256:"3f5a9c1e7b2d4c6a8e0f1b3d5a7c9e1f3b5d7a9c1e3f5a7b9d1c3e5f7a9b1d3c",size_bytes:"4404019",modified_at:B(3*86400),source_redacted:re.urls.geosite[0],fetched_url_redacted:re.urls.geosite[0],verified:!0,download_route:{route:"routing",group_id:null}},{kind:"geoip",sha256:"9b1d3c5e7f0a2c4e6b8d0f2a4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e",size_bytes:"17406771",modified_at:B(3*86400),source_redacted:re.urls.geoip[0],fetched_url_redacted:re.urls.geoip[1],verified:!0,download_route:{route:"routing",group_id:null}}],last_checked_at:B(3*86400),last_updated_at:B(3*86400),next_check_at:null,last_error:null,required_codes:{geosite:[],geoip:[]}},pn={last_checked_at:B(6*3600),last_error:{code:"download_failed",message:"Every geosite URL failed to download",details:null}};function ye(e,n,o,r,a=new Set){const c=o.find(i=>i.id===e);if(c)return c;if(a.has(e))return;a.add(e);const l=r.find(i=>i.id===e)?.runtime.selection[n];return l?ye(l.member_id,n,o,r,a):void 0}function ht(e,n,o){const r=e.target,a=r.type==="group"?o.find(l=>l.id===r.group_id):void 0;let c=r.type==="node"?[r.node_id]:a.members.map(l=>l.id);if(Array.isArray(e.members)&&(c=c.filter(l=>e.members.includes(l))),e.members==="leaves"){const l=new Set,i=new Set,d=s=>{i.has(s)||(i.add(s),n.some(p=>p.id===s)?l.add(s):o.find(p=>p.id===s)?.members.forEach(p=>d(p.id)))};c.forEach(d),c=[...l]}return c}function fn(e,n,o,r){const a=e.target,c=a.type==="group"?o.find(s=>s.id===a.group_id):void 0,l={tcp:c?.runtime.selection.tcp?.member_id??null,udp:c?.runtime.selection.udp?.member_id??null},i=ht(e,n,o),d=[];for(const s of i)for(const p of e.transport){const v=ye(s,p,n,o),m=e.ip_version==="any"?["ipv4","ipv6"]:[e.ip_version];for(const _ of m){const b=v?.health.find(f=>f.transport===p&&f.ip_version===_),w=b?.state==="healthy"?b.latency_ms:null,N=w===null?"unavailable":"healthy",C=N==="unavailable"?"probe_failed":null,t={transport:p,purpose:e.purpose,ip_version:_,warmth:e.warmth,measurement:e.kind==="http"?"http_round_trip":e.kind==="dns"?"dns_round_trip":"tcp_connect",sample_source:"probe",state:N,latency_ms:w,moving_avg_ms:null,avg10_ms:null,observed_at:r,error:C};if(v){const f=v.health.findIndex(u=>u.transport===p&&u.purpose===e.purpose&&u.ip_version===_&&u.warmth===e.warmth&&u.measurement===t.measurement);f<0?v.health.push(t):v.health[f]=t}if(c){const f={...t,member_id:s,resolved_leaf_node_id:v?.id??null,sorting_latency_ms:w,ranking:null},u=c.runtime.health.findIndex(g=>g.member_id===s&&g.transport===p&&g.purpose===e.purpose&&g.ip_version===_&&g.warmth===e.warmth&&g.measurement===t.measurement);u<0?c.runtime.health.push(f):c.runtime.health[u]=f}d.push({member_id:s,resolved_leaf_node_id:v?.id??null,kind:e.kind,purpose:e.purpose,transport:p,ip_version:_,warmth:e.warmth,state:N,latency_ms:w,health_updated:!!v,error:C,observed_at:r})}}return{target:a,results:d,selection_before:l,selection_after:{...l},selection_changed:{tcp:!1,udp:!1}}}function mn(e,n){const o={"/policy":structuredClone(e.policy)};for(const[i,d]of Object.entries(e.config))o["/config/"+i]=d;const r=i=>e.capabilities.mutable_config.some(d=>i===(d==="policy"?"/policy":"/config/"+d)),a=i=>{if(!(i in o))throw new h(422,"unsupported_value","Patch path does not exist")};for(const i of n){if(!r(i.path)||"from"in i&&!r(i.from))throw new h(422,"unsupported_value","Group field is not mutable");if(i.op!=="add"&&i.op!=="copy"&&i.op!=="move"&&a(i.path),i.op==="test"){if(JSON.stringify(o[i.path])!==JSON.stringify(i.value))throw new h(409,"state_conflict","Patch test failed")}else if(i.op==="remove")delete o[i.path];else if("from"in i){a(i.from);const d=o[i.from];i.op==="move"&&delete o[i.from],o[i.path]=structuredClone(d)}else o[i.path]=structuredClone(i.value)}const c=o["/policy"]??{kind:"selector",native:"selector"};if(typeof c!="object"||c===null||!("kind"in c)||!["selector","urltest","loadbalance","fallback","random","score"].includes(String(c.kind))||!("native"in c)||typeof c.native!="string")throw new h(422,"unsupported_value","Invalid group policy");const l=Object.fromEntries(Object.keys(e.config).map(i=>[i,o["/config/"+i]??(i==="interrupt_connections"?!1:null)]));for(const i of["check_interval","tolerance","idle_timeout"]){const d=l[i];if(d!==null&&(!Number.isSafeInteger(d)||d<(i==="check_interval"?1:0)))throw new h(422,"unsupported_value","Invalid group interval or tolerance")}if(typeof l.interrupt_connections!="boolean")throw new h(422,"unsupported_value","Invalid interruption setting");for(const i of["default_member_id","final_outbound","check_url"])if(l[i]!==null&&typeof l[i]!="string")throw new h(422,"unsupported_value","Invalid group configuration value");if(l.default_member_id!==null&&!e.members.some(i=>i.id===l.default_member_id))throw new h(422,"unsupported_value","Default member is not in this group");if(l.check_url!==null&&!Lt(l.check_url))throw new h(422,"unsupported_value","Invalid check URL");return{policy:c,config:l}}function _n(e,n,o,r,a){const{blocks:c,tokens:l}=ae(e),i=t=>c.filter(f=>f.name===t).flatMap(f=>we(e,f,l)),d=i("subscription"),s=new Set(d.map(t=>t.name)),p=i("node"),v=new Set(p.map(t=>t.name)),m=o.filter(t=>t.provider_id==="inline"?v.has(t.name):s.has(t.provider_id));for(const t of p){const f=me(t.value).split(":")[0],u=m.find(g=>g.provider_id==="inline"&&g.name===t.name);u?u.protocol=f:m.push({id:t.name,name:t.name,protocol:f,provider_id:"inline",subscription_tag:null,group_ids:[],health:[]})}const _=a.filter(t=>t.kind==="inline"||s.has(t.name)),b=t=>c.filter(f=>f.name==="subscription").flatMap(f=>f.children.filter(u=>u.name===t)).flatMap(f=>we(e,f,l)).find(f=>f.name==="url")?.value;for(const t of d){const f=Re(me(b(t.name)??t.value)),u=_.find(g=>g.name===t.name);if(u){u.url_redacted=f;continue}_.push({id:t.name,name:t.name,kind:"subscription",url_redacted:f,node_count:0,updated_at:null,expires_at:null,traffic:null,status:"stale",last_error:null})}const w=dt(e),N=new Map(m.map(t=>[t.id,[]])),C=w.map(t=>{const f=r.find(x=>x.name===t.name),u=m.filter(x=>Tt(t.filters,x)),g=u.map(x=>({id:x.id,name:x.name,kind:"node"}));for(const x of u)N.get(x.id).push(f?.id??t.name);const A=t.policy??"fixed(0)",S=Gt(A)??"selector",O=_t(null),G=c.filter(x=>x.name==="group").flatMap(x=>x.children).find(x=>x.name===t.name);for(const x of we(e,G,l)){if(!(x.name in O))continue;const k=me(x.value);Object.assign(O,{[x.name]:k==="null"?null:x.name==="interrupt_connections"?k==="true":["check_interval","tolerance","idle_timeout"].includes(x.name)?Number(k):k})}O.default_member_id===null&&S==="selector"&&(O.default_member_id=g[Number(/^fixed\((\d+)\)$/.exec(A)?.[1]??0)]?.id??null);const z={tcp:null,udp:null};for(const x of["tcp","udp"]){const k=f?.runtime.selection[x],$=k&&g.some(j=>j.id===k.member_id)?k.member_id:O.default_member_id??g[0]?.id;$&&(z[x]={member_id:$,resolved_leaf_node_id:$,source:S==="selector"?"runtime":"policy"})}return{id:f?.id??t.name,name:t.name,icon:f?.icon??null,config_revision:n,policy:{kind:S,native:A},config:O,members:g,runtime:{selection:z,health:u.flatMap(x=>x.health.map(k=>({...k,member_id:x.id,resolved_leaf_node_id:x.id,sorting_latency_ms:k.latency_ms,ranking:null})))},capabilities:gt(S)}});for(const t of m)t.group_ids=N.get(t.id);for(const t of _)t.node_count=m.filter(f=>f.provider_id===t.id).length;o.splice(0,o.length,...m),r.splice(0,r.length,...C),a.splice(0,a.length,..._)}function gn(e,n,o){const r=dt(e).find(s=>s.name===n);e=zt(e,n,{filters:r.filters,policy:o.policy.native});const{blocks:a,tokens:c}=ae(e),l=a.filter(s=>s.name==="group").flatMap(s=>s.children).find(s=>s.name===n),i=we(e,l,c).filter(s=>s.name in o.config),d=Object.entries(o.config).map(([s,p])=>`${s}: ${typeof p=="string"?se(p):String(p)}`).join(`
`);e=e.slice(0,l.close)+`
`+d+`
`+e.slice(l.close);for(const s of i.reverse())e=e.slice(0,s.from)+e.slice(s.to);return e}function Xe(e){try{return e()}catch{throw new h(422,"unsupported_value","The value cannot be written to the configuration")}}const Qe=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function hn(e,n,{enqueue:o,log:r,pending:a},c,l,i,d,s=!1){const p=_e("nodes"),v=_e("providers"),m=structuredClone(cn),{nodes:_,groups:b}=ln(Number.isFinite(n)?n:120,s);for(const t of m)t.node_count=_.filter(f=>f.provider_id===t.id).length;s&&Object.assign(m.find(t=>t.id==="sub-c"),{status:"stale",last_error:de});const w=new Map,N=new Set;return{api:{nodes:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.nodes.available)throw new h(404,"capability_not_supported","Nodes are unavailable");const u=p(t?.group_id?_.filter(g=>g.group_ids.includes(t.group_id)):_,t);return{observed_at:V,nodes:u.items,next_cursor:u.next_cursor}},groups:async t=>{if(t?.throwIfAborted(),!e.resources.groups.available)throw new h(404,"capability_not_supported","Groups are unavailable");return b.map(f=>({id:f.id,name:f.name,icon:f.icon,config_revision:f.config_revision,policy:{...f.policy},member_count:f.members.length,selection:{tcp_member_id:f.runtime.selection.tcp?.member_id??null,udp_member_id:f.runtime.selection.udp?.member_id??null}}))},group:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.groups.available)throw new h(404,"capability_not_supported","Groups are unavailable");return structuredClone(W(b.find(u=>u.id===t),"Group"))},selectGroup:async(t,f,u)=>{u?.throwIfAborted();const g=W(b.find(x=>x.id===t),"Group"),A=!g.capabilities.can_select&&g.capabilities.can_override;if(!g.capabilities.can_select&&!A)throw new h(404,"capability_not_supported","Group does not support manual selection");W(g.members.find(x=>x.id===f.member_id),"Group member");const S=f.network==="both"?["tcp","udp"]:[f.network];let O=!1;for(const x of S){const k=g.runtime.selection[x]?.member_id;g.runtime.selection[x]={member_id:f.member_id,resolved_leaf_node_id:ye(f.member_id,x,_,b)?.id??null,source:A?"override":"runtime"},!(k===f.member_id||!g.config.interrupt_connections)&&(O=i(t,x)||O)}const G=(w.get(t)??0n)+1n;w.set(t,G);const z={group_id:t,member_id:f.member_id,network:f.network,source:A?"override":"runtime",selection_revision:String(G),connections_interrupted:O};return f.network!=="both"&&(z.resolved_leaf_node_id=g.runtime.selection[f.network]?.resolved_leaf_node_id),z},clearGroupOverride:async(t,f,u)=>{u?.throwIfAborted();const g=W(b.find(G=>G.id===t),"Group");if(!g.capabilities.can_override)throw new h(409,"state_conflict","Group has no override to clear");const A=f==="both"?["tcp","udp"]:[f],S=an(g);for(const G of A)g.runtime.selection[G]={member_id:S,resolved_leaf_node_id:ye(S,G,_,b)?.id??null,source:"policy"};const O=(w.get(t)??0n)+1n;return w.set(t,O),{group_id:t,network:f,selection_revision:String(O),connections_interrupted:!1,selection:structuredClone(g.runtime.selection)}},patchGroup:async(t,f,u,g)=>{g?.throwIfAborted();const A=W(b.find(z=>z.id===t),"Group");if(u!=='"'+A.config_revision+'"')throw new h(412,"stale_revision","Group configuration revision changed");if(N.has(t))throw new h(409,"state_conflict","Group update is pending");const S=e.resources.groups.max_patch_operations;if(S!==void 0&&f.length>S)throw new h(413,"request_too_large","Too many patch operations");const O=mn(A,f),G=await l(z=>gn(z,A.name,O));return N.add(t),o("group_update",()=>{N.delete(t);const z=G();return{group_id:t,config_revision:z}})},startProbe:async(t,f)=>{f?.throwIfAborted();const u=e.resources.probes;if(!u.available)throw new h(404,"capability_not_supported","Probes are unavailable");const g=t.ip_version==="any"?["ipv4","ipv6"]:[t.ip_version];if(!u.targets?.includes(t.target.type)||!u.kinds?.includes(t.kind)||!u.purposes?.includes(t.purpose)||t.transport.some(x=>!u.transports?.includes(x))||g.some(x=>!u.ip_versions?.includes(x)))throw new h(422,"unsupported_value","Probe dimensions are not advertised");const A=t.target,S=A.type==="group"?W(b.find(x=>x.id===A.group_id),"Group"):void 0;if(A.type==="node"&&W(_.find(x=>x.id===A.node_id),"Node"),!t.transport.length||t.transport.some(x=>S&&!S.capabilities.probe_transports.includes(x))||t.kind!=="dns"&&(t.purpose!=="data"||t.transport.some(x=>x!=="tcp"))||t.kind==="dns"&&t.purpose!=="dns")throw new h(422,"unsupported_value","Unsupported probe dimensions");if(S&&Array.isArray(t.members)&&t.members.some(x=>!S.members.some(k=>k.id===x)))throw new h(422,"unsupported_value","Probe member is not in this group");const O=ht(t,_,b).length,G=u.limits;if(O>G.max_members_per_job||O*t.transport.length*g.length>G.max_results_per_job)throw new h(413,"request_too_large","Probe exceeds the advertised member or result limit");const z=structuredClone(t);return o("probe",()=>fn(z,_,b,new Date().toISOString()))},providers:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.providers.available)throw new h(404,"capability_not_supported","Providers are unavailable");const u=e.resources.providers.max_page_size??1e3;if(t?.limit!==void 0&&t.limit>u)throw new h(400,"invalid_request",`limit exceeds max_page_size ${u}`);const g=v(m,{...t,limit:t?.limit??Math.min(100,u)});return{providers:g.items,next_cursor:g.next_cursor}},refreshProvider:async(t,f)=>{f?.throwIfAborted();const u=W(m.find(g=>g.id===t),"Provider");if(!e.resources.providers.can_refresh||u.kind!=="subscription")throw new h(404,"capability_not_supported","This provider cannot be refreshed");if(N.has("refresh:"+t))throw new h(409,"state_conflict","A refresh for this provider is already queued or running");return N.add("refresh:"+t),o("provider_refresh",()=>{if(N.delete("refresh:"+t),s&&u.id==="sub-c")throw u.last_error=structuredClone(de),r("warn","honk::subscription","Subscription fetch failed.",{provider:u.name,error:de.code}),new h(502,de.code,de.message);return u.updated_at=new Date().toISOString(),u.status="ok",u.last_error=null,structuredClone(u)})},createProvider:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.providers.can_manage)throw new h(404,"capability_not_supported","Provider management is unavailable");if(!/^https?:\/\//.test(t.url))throw new h(422,"unsupported_value","The subscription URL must start with http:// or https://");if(m.some($=>$.name===t.name))throw new h(409,"state_conflict",`A provider named ${t.name} already exists`);if(!URL.parse(t.url))throw new h(422,"unsupported_value","The subscription URL cannot be parsed");const{update_interval:g,user_agent:A,cache:S}=t,O=e.resources.providers.create_options??{};if(["update_interval","user_agent","cache"].some($=>t[$]!==void 0&&O[$]===void 0)||g!==void 0&&!(Number.isInteger(g)&&g>=0&&g<=31536e3)||A!==void 0&&!/^[\x20-\x7E]{1,256}$/.test(A))throw new h(422,"unsupported_value","The subscription options are not supported");const z=[A!==void 0&&`ua: ${se(A)}`,g!==void 0&&`interval: '${g}s'`,S!==void 0&&`cache: ${S}`].filter(Boolean).map($=>` ${$}
`),x=Xe(()=>z.length?` ${Be(t.name)}: {
url: ${se(t.url)}
${z.join("")} }
`:` ${Be(t.name)}: ${se(t.url)}
`),k=await l($=>$.replace(/^(subscription \{\n)/m,`$1${x}`));return r("info","honk::subscription","Subscription added.",{provider:t.name}),k(),structuredClone(W(m.find($=>$.name===t.name),"Provider"))},deleteProvider:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.providers.can_manage)throw new h(404,"capability_not_supported","Provider management is unavailable");const u=m.findIndex(O=>O.id===t);if(u<0)return{deleted:0};if(m[u].kind==="inline")throw new h(404,"capability_not_supported","The inline provider is the node section itself");const g=m[u],A=Qe(g.name),S=await l(O=>O.replace(new RegExp(`^\\s*${A}:\\s*\\{\\n[\\s\\S]*?\\n\\s*\\}\\n|^\\s*${A}:.*\\n`,"m"),""));return r("info","honk::subscription","Subscription removed.",{provider:g.name}),S(),{deleted:1}},createNode:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.nodes.can_manage)throw new h(404,"capability_not_supported","Node management is unavailable");const u=/^([a-z][a-z0-9+.-]*):\/\/\S+$/i.exec(t.link.trim())?.[1]?.toLowerCase();if(!u||!dn.includes(u))throw new h(422,"unsupported_value",`Unsupported share link scheme "${u??""}"`);if(_.some(S=>S.name===t.name))throw new h(409,"state_conflict",`An inline node named ${t.name} already exists`);const g=Xe(()=>` ${se(t.name)}: ${se(t.link.trim())}
`),A=await l(S=>S.replace(/^(node \{\n)/m,`$1${g}`));return r("info","honk::config","Node added.",{node:t.name,protocol:u}),A(),structuredClone(W(_.find(S=>S.name===t.name),"Node"))},deleteNode:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.nodes.can_manage)throw new h(404,"capability_not_supported","Node management is unavailable");const u=_.find(A=>A.id===t);if(!u)return{deleted:0};if(u.provider_id!=="inline")throw new h(404,"capability_not_supported","Only inline nodes can be deleted; refresh or delete the provider instead");const g=await l(A=>A.replace(new RegExp(`^\\s*'${Qe(u.name)}':.*\\n`,"m"),""));return r("info","honk::config","Node removed.",{node:u.name}),g(),{deleted:1}},geodata:async t=>{if(t?.throwIfAborted(),!e.resources.geodata.available)throw new h(404,"capability_not_supported","Geodata is unavailable");return d.status()},updateGeodata:async t=>{if(t?.throwIfAborted(),!e.resources.geodata.can_update)throw new h(404,"capability_not_supported","Geodata update is unavailable");if(a("geodata_update"))throw new h(409,"state_conflict","A geodata update is already queued or running");return o("geodata_update",()=>{const f=d.update();return r("info","honk::geodata","Geodata updated; reloading.",{assets:f.assets.map(u=>u.kind)}),c(),f})}},groupNames:()=>new Set(b.map(t=>t.name)),groupIds:()=>new Set(b.map(t=>t.id)),activate:(t,f)=>_n(t,f,_,b,m)}}const bn=[{level:"info",target:"honk::main",message:"Runtime snapshot captured.",fields:{pid:1842}},{level:"info",target:"honk::config",message:"Configuration state checked.",fields:{sources:4,generation_id:"40"}},{level:"info",target:"honk::subscription",message:"Subscription status checked.",fields:{provider:"sub-c"}},{level:"warn",target:"honk::subscription",message:"Subscription served from cache.",fields:{provider:"sub-c",age_seconds:1800}},{level:"warn",target:"honk::datapath",message:"Routing map sample delayed.",fields:{interface:"lan0"}},{level:"info",target:"honk::dns",message:"DNS listener healthy.",fields:{bind:"127.0.0.1:5353"}},{level:"info",target:"honk::routing",message:"Routing generation checked.",fields:{generation_id:"40"}},{level:"error",target:"honk::group",message:"Health check failed.",fields:{node:"jp-01",error:"connect timeout"}},{level:"info",target:"honk::api",message:"Native API request served.",fields:{listen:"127.0.0.1:9090"}}];function wn(e,n,o,r,a,c=2500,l=!1){const i=new Map;let d=0;const s=new Set,p=[],v=new Map,m=new Map,_=()=>{for(;p.length&&Date.now()-Date.parse(p[0].data.observed_at)>n.retention_seconds*1e3;)p.shift();const y=p.length?Number(p[0].id.split(":")[1])-1:d,M=S.length?Number(S[0].id.split(":")[2])-1:G;for(const[I,P]of v)(P.position<(P.stream==="events"?y:M)||P.stream==="events"&&Date.now()-P.issued>n.retention_seconds*1e3)&&(v.delete(I),m.delete(JSON.stringify([P.stream,P.filter,P.position])))},b=(y,M,I)=>{const P=JSON.stringify([y,M,I]),R=m.get(P);if(R)return R;const F=ct();return v.set(F,{stream:y,filter:M,position:I,issued:Date.now()}),m.set(P,F),F},w=(y,M,I,P)=>{if(y===void 0)return P;_();const R=v.get(y);if(!R||R.stream!==M||R.filter!==I)throw new h(409,"event_cursor_expired","The stream cursor cannot be replayed");return R.position};let N;const C=()=>({instance_id:K,observed_at:new Date().toISOString()});function t(y){y.id=`${K}:${++d}`,p.push(y),p.length>n.max_buffered_events&&p.shift(),_(),s.forEach(M=>M(y))}function f(){o.observed_at=new Date().toISOString(),t({id:"",event:"runtime.updated",data:{...C(),href:"/api/v1/runtime"}})}const u=(y,M)=>{const I="op-"+(i.size+1),P=new Date().toISOString(),R={operation_id:I,kind:y,created_at:P,started_at:P};i.set(I,{...R,finished_at:null,status:"running",result:null,error:null,retryAfter:1}),t({id:"",event:"operation.updated",data:{...C(),resource_id:I,status:"running",href:"/api/v1/operations/"+I}}),setTimeout(()=>{try{i.set(I,{...R,status:"succeeded",result:M(),error:null,finished_at:new Date().toISOString()})}catch(T){i.set(I,{...R,status:"failed",finished_at:new Date().toISOString(),result:null,error:T instanceof h?{code:T.code,message:T.message,details:T.details}:{code:"operation_failed",message:T instanceof Error?T.message:String(T)}})}const D=i.get(I);y==="reload"&&(D.status==="succeeded"||D.status==="failed")&&(o.last_reload={operation_id:I,status:D.status,finished_at:D.finished_at,error:D.error}),t({id:"",event:"operation.updated",data:{...C(),resource_id:I,status:D.status,href:"/api/v1/operations/"+I}}),f()},1e3);const F="/api/v1/operations/"+I;return{operation_id:I,kind:y,status:"queued",href:F,retryAfter:1}},g=async(y,M)=>(M?.throwIfAborted(),structuredClone(W(i.get(y),"Operation"))),A=["trace","debug","info","warn","error"],S=[],O=new Set;let G=0;function z(){const y=r().buffered_records;S.length>y&&S.splice(0,S.length-y),_()}const x=(y,M,I,P=null,R=Date.now())=>{const F={id:`${K}:logs:${++G}`,ts:new Date(R).toISOString(),level:y,target:M,message:I,fields:P};S.push(F),z(),O.forEach(D=>D(F))},k=[()=>x("info","honk::routing","Routing generation published.",{generation_id:a()}),()=>x("debug","honk::dns","Upstream answered.",{upstream:"tls://1.1.1.1:853",elapsed_ms:12}),()=>x("info","honk::group","Health check finished.",{group:"resilient",healthy:3,unavailable:0}),...l?[()=>x("warn","honk::subscription","Subscription served from cache.",{provider:"sub-c",age_seconds:1800})]:[],()=>x("trace","honk::datapath","Kernel map synced.",{entries:4096})],$=Date.now();for(let y=240;y>0;y-=2){const M=$-y*6e4,I=l&&y>=34&&y<=48,P=y>=72&&y<=116||y>=164&&y<=188;x("info","honk::group","Health check finished.",{group:"resilient",healthy:I?2:3,unavailable:I?1:0},M),x("debug","honk::dns","Upstream answered.",{upstream:"tls://1.1.1.1:853",elapsed_ms:12+y%9},M+2e4),y%6===0&&x("trace","honk::datapath","Kernel map synced.",{entries:4096+y},M+1e4),l&&y%14===0&&x("warn","honk::subscription","Subscription served from cache.",{provider:"sub-c",age_seconds:1800},M+4e4),P&&x("info","honk::dns","Query answered.",{queries:12+y%15},M+3e4),I&&(x("warn","honk::group","Health check slow.",{node:"us-01",elapsed_ms:2400},M+3e4),x("error","honk::group","Health check failed.",{node:"us-01",error:"connect timeout"},M+5e4))}for(const y of bn)(l||y.level!=="warn"&&y.level!=="error")&&x(y.level,y.target,y.message,y.fields??null);for(let y=28;y>0;y--){const M={instance_id:K,observed_at:new Date($-y*1e4).toISOString()};if(!l&&y>=4&&y<=7){t({id:"",event:"runtime.updated",data:{...M,href:"/api/v1/runtime"}});continue}y===21?t({id:"",event:"generation.changed",data:{...M,previous_generation_id:"39",generation_id:"40"}}):y===17?t({id:"",event:"operation.updated",data:{...M,resource_id:"op-1182",status:"succeeded",href:"/api/v1/operations/op-1182"}}):y===7?t({id:"",event:"flow.gap",data:{...M,resource_id:null,reason:"sampled",dropped_records:"3"}}):y===6?t({id:"",event:"flow.gap",data:{...M,resource_id:null,reason:"buffer_overflow",dropped_records:"12"}}):y===5?t({id:"",event:"operation.updated",data:{...M,resource_id:"op-1183",status:"failed",href:"/api/v1/operations/op-1183"}}):y===4?t({id:"",event:"flow.gap",data:{...M,resource_id:null,reason:"recording_changed",dropped_records:"0"}}):y%3===0?t({id:"",event:"flow.updated",data:{...M,resource_id:`flow-r${String(y).padStart(2,"0")}`,revision:1,href:`/api/v1/flows/flow-r${String(y).padStart(2,"0")}`}}):t({id:"",event:"runtime.updated",data:{...M,href:"/api/v1/runtime"}})}let j;async function E({level:y,target:M,lastEventId:I,signal:P,onRecord:R,onConnectionChange:F}){if(P?.aborted)return;if(!e.available)throw new h(404,"capability_not_supported","Logs are unavailable");if(y&&!e.levels?.includes(y))throw new h(400,"invalid_request",`Level ${y} is not advertised`);const D=y?A.indexOf(y):0,T=JSON.stringify([y??null,M??null]),L=Q=>{A.indexOf(Q.level)>=D&&(!M||Q.target.startsWith(M))&&R({...structuredClone(Q),id:b("logs",T,Number(Q.id.split(":")[2]))})},X=w(I,"logs",T,0);F?.(!0);for(const Q of S)Number(Q.id.split(":")[2])>X&&L(Q);if(P?.aborted){F?.(!1);return}O.add(L),j||(j=setInterval(()=>k[Math.floor(Math.random()*k.length)](),c)),await new Promise(Q=>{P?.addEventListener("abort",()=>{O.delete(L),O.size||(clearInterval(j),j=void 0),F?.(!1),Q()},{once:!0})})}async function H({kinds:y,lastEventId:M,signal:I,onEvent:P,onConnectionChange:R}){if(I?.aborted)return;if(!n.available)throw new h(404,"capability_not_supported","Events are unavailable");const F=JSON.stringify([...new Set(y??[])].sort()),D=L=>{(L.event==="stream.ready"||!y?.length||y.includes(L.event))&&P({...structuredClone(L),id:b("events",F,Number(L.id.split(":")[1]))})},T=w(M,"events",F,0);R?.(!0),D({id:`${K}:${Number.isFinite(T)?T:d}`,event:"stream.ready",data:C()});for(const L of p)Number(L.id.split(":")[1])>T&&D(L);if(I?.aborted){R?.(!1);return}s.add(D),N||(N=setInterval(f,5e3)),await new Promise(L=>{I?.addEventListener("abort",()=>{s.delete(D),s.size||(clearInterval(N),N=void 0),R?.(!1),L()},{once:!0})})}return{api:{operation:g,pollOperation:async(y,M)=>{let I=y.retryAfter;for(;;){await Ut(I,M);const P=await g(y.href.split("/").pop(),M);if(P.status==="succeeded"||P.status==="failed")return P;I=P.retryAfter??1}},subscribeEvents:H,subscribeLogs:E},enqueue:u,log:x,publish:t,eventData:C,trimLogs:z,pending:y=>[...i.values()].some(M=>M.kind===y&&(M.status==="queued"||M.status==="running"))}}function bt(e,n){const o=n.find(a=>a.stage==="outbound")?.data,r=n.find(a=>a.stage==="route")?.data;return{chain:o?[...o.selection_path.map(a=>a.group_id),...o.leaf_node_id?[o.leaf_node_id]:[]]:[],chain_source:o?"evaluation":"unknown",rule_id:r?.rule_id??null,rule_expression:r?.rules.find(a=>a.rule_id===r.rule_id)?.expression??null,rule_source:r?r.plane==="kernel"?"kernel":"recomputed":"unknown",ingress:e.ingress,domain_source:e.domain_source}}const vn=[[["ad.doubleclick.net","doubleclick.net","googlesyndication.com","adservice.google.com"],"r3"],[["taobao.com","tmall.com","alipay.com","alicdn.com","weixin.qq.com","wechat.com","zhihu.com","zhimg.com","douyin.com","iqiyi.com","youku.com","weibo.com","jd.com","xiaohongshu.com","baidu.com","bilibili.com"],"r4"],[["telegram.org","t.me"],"r5"],[["openai.com","chatgpt.com","oaistatic.com"],"r7"]],je=e=>Ge.find(n=>n.id===e);function yn(e){const n=e.toLowerCase();for(const[o,r]of vn)if(o.some(a=>n===a||n.endsWith("."+a)))return{id:r,expression:je(r).cond};return null}function Ue(e,n,o,r){const a={src:e.src??null,dst:e.dst??null,domain:e.domain??null,domain_source:e.domain?"dns_mapping":null,pid:null,process_path:null,src_mac:null,ingress:"lan",domain_rule_ids:null,dscp:0,mark:0},c={observed_at:o,elapsed_us:0,generation_id:De,evidence:"observed"},l=e.outbound==="direct",i=e.state==="blocked",s=(e.domain?yn(e.domain):null)??(i?{id:"r3",expression:je("r3").cond}:l?{id:"r2",expression:je("r2").cond}:{id:"fallback",expression:"fallback: "+ut.fallback.target}),p=s.expression,v=e.outbound==="resilient"?"sg-01":e.outbound==="gaming"?"hk-02":"hk-01",m=e.outbound==="resilient"?"score":e.outbound==="gaming"?"urltest":"selector",_=[{...c,seq:1,stage:"input",data:{values:{...a,pname:e.pname},source:l||i?"kernel":"socket"}},{...c,seq:2,stage:"route",data:{evaluation_id:"eval-1",chain:"traffic",plane:l||i?"kernel":"userspace",rule_id:s.id,rules:[{rule_id:s.id,expression:p,result:"matched",conditions:[],missing_inputs:[]}],outbound:e.outbound,must:i,mark:0,input:null,dns_action:null}},{...c,seq:3,stage:"datapath",data:{plane:l||i?"kernel":"userspace",action:i?"drop":l?"activate_direct":"redirect",reason:"route_selected",error:null}}];!l&&!i&&(_.push({...c,seq:_.length+1,stage:"dial_mode",data:{configured:a.domain?"domain":"ip",effective_target:a.domain?"domain":"ip",domain:a.domain,domain_source:a.domain_source,verification:a.domain?"matched":"not_required",reason:a.domain?"dns_mapping_matches":"ip_target"}}),a.domain&&_.push({...c,seq:_.length+1,stage:"dns",data:{lookup_id:"lookup-1",parent_lookup_id:null,attempt_id:null,purpose:"dial_target",name:a.domain,qtype:"A",source:"cache",upstream_transport:null,carrier_transport:null,cache:"hit",cache_entry_id:null,upstream:null,route_evaluation_ids:[],status:"NOERROR",addresses:[e.dst.split(":")[0]],selected_ip:e.dst.split(":")[0],error:null}}),_.push({...c,seq:_.length+1,stage:"reroute",data:{performed:!1,reason:"no_new_routing_input",from_evaluation_id:"eval-1",to_evaluation_id:null}},{...c,seq:_.length+2,stage:"outbound",data:{attempt_id:"attempt-1",parent_attempt_id:null,kind:"leaf",evaluation_id:"eval-1",routing_source:"evaluation",routed_outbound:e.outbound,effective_outbound:e.outbound,mode_override:"none",selection_path:[{group_id:e.outbound,member_id:v,member_name:v,policy:m,reason:m==="selector"?"runtime_selection":"policy_selection",selection:{previous_member_id:null,metric:null,tolerance_ms:null,candidates:[{member_id:v,member_name:v,leaf_node_id:v,leaf_node_name:v,eligible:!0,sorting_latency_ms:null,score:null,selected:!0,reason:"selected"}]}}],leaf_node_id:v,leaf_node_name:v,target:a.domain?a.domain+":"+e.dst.split(":").at(-1):a.dst,target_kind:a.domain?"domain":"ip",dial_ip:e.dst.split(":")[0],server_addr:"198.51.100.1:443",resolution_location:a.domain?"local_dns":"original_ip",status:"succeeded",error:null}})),i||_.push({...c,seq:_.length+1,stage:"connection",data:{state:"active",reason:"reply_received",milestone:"first_reply",attempt_id:l?null:"attempt-1",reply_received:!0,error:null}}),e.state==="closed"&&_.push({...c,seq:_.length+1,stage:"connection",data:{state:"closed",milestone:"terminal",reason:"peer_closed",attempt_id:null,reply_received:null,error:null}});const b=_.map((N,C)=>({...N,elapsed_us:C*1200,observed_at:new Date(Date.parse(e.started_at??o)+C*1.2).toISOString()})),w={id:i&&!e.flow_id?"flow-blocked":"flow-"+e.id,instance_id:r,revision:1,network:n,state:e.state,pname:e.pname,connection_id:i?null:e.id,outbound:e.outbound,...bt(a,b),observed_by:e.observed_by,started_at:e.started_at,ended_at:i||e.state==="closed"?b[b.length-1].observed_at:null,input:a};return l?{...w,trace_status:"partial",trace:{status:"partial",missing:["not_instrumented"],steps:b}}:{...w,trace_status:"complete",trace:{status:"complete",missing:[],steps:b}}}const kn={1:252,2:723,3:18,4:5,5:1,6:580,7:61,8:3},he=[];function Z(e,n,o,r,a,c,l=null,i="tcp",d=null){const s={id:e,flow_id:["1","2","5"].includes(e)?"flow-"+e:null,pname:d,state:r==="block"?"blocked":"active",src:o,dst:n,domain:l,outbound:r,started_at:B(kn[e]??60),observed_by:r==="direct"||r==="block"?"ebpf":"userspace",upload_bytes:c,download_bytes:a,upload_bytes_per_second:r==="block"?"0":String(800+Number(e)*173),download_bytes_per_second:r==="block"?"0":String(8e3+Number(e)*2279)},p=Ue(s,i,V,K);return he.push(p),{...s,...bt(p.input,p.trace.steps)}}const ke=["www.netflix.com","rr1---sn-ab5l6n7z.googlevideo.com","www.google.com","raw.githubusercontent.com","gateway.icloud.com","login.live.com","store.steampowered.com","api.spotify.com","pbs.twimg.com","scontent.cdninstagram.com","chatgpt.com","claude.ai","www.reddit.com","en.wikipedia.org","api.cloudflare.com","www.taobao.com","weixin.qq.com","www.zhihu.com","www.douyin.com","www.iqiyi.com","www.jd.com","www.xiaohongshu.com","www.baidu.com","steamcdn-a.akamaihd.net","www.speedtest.net","controlplane.tailscale.com","www.youtube.com","graph.facebook.com","api.github.com","registry.npmjs.org","updates.mozilla.org","www.twitch.tv","music.apple.com","api.weather.com","maps.googleapis.com","cdn.jsdelivr.net","fonts.gstatic.com","www.wikipedia.org","www.amazon.com","www.microsoft.com","www.nytimes.com","www.bbc.com","api.openai.com","www.cloudflare.com","media.discordapp.net","www.bing.com"],Ye=["10.0.0.7","10.0.0.20","10.0.0.31","10.0.0.42","10.0.0.53","10.0.0.64"],Ze=["firefox","chromium","steam","curl","spotify","discord"],J={observed_at:V,instance_id:K,visibility:"full",truncated:!1,total_tcp:6,total_udp:2,tcp:[Z("1","149.154.167.220:443","10.0.0.12","proxy","1200000","84000","api.telegram.org"),Z("2","120.92.78.14:443","10.0.0.7","direct","1200000000","9400000","cdn.bilibili.com"),Z("3","52.84.19.3:443","10.0.0.7","proxy","58000000","12000"),Z("4","142.250.66.46:443","10.0.0.31","block","0","0","doubleclick.net"),Z("6","104.16.132.229:443","10.0.0.31","proxy","3400000","210000","chatgpt.com"),Z("7","203.0.113.9:8443","10.0.0.20","resilient","96000","40000")],udp:[Z("5","1.1.1.1:53","10.0.0.12","direct","2000","2000",null,"udp"),Z("8","10.0.0.1:53","10.0.0.20","gaming","12000","1000",null,"udp")]},xn=/netflix|googlevideo|youtube|twitch|iqiyi|douyin|steamcdn|speedtest|music\.apple|spotify/,Sn=/icloud|github\.com|npmjs|speedtest/,In=/weixin|discordapp|tailscale/;function An(e,n){const o=c=>((n+1)*2654435761+c*40503>>>0)/4294967296,r=(c,l,i)=>Math.round(c*(l/c)**o(i));if(xn.test(e)){const c=r(4e7,9e8,1);return[String(c),String(Math.round(c*(.002+.02*o(2))))]}if(Sn.test(e)){const c=r(8e6,16e7,3);return[String(Math.round(c*(.05+.3*o(4)))),String(c)]}if(In.test(e)){const c=r(2e6,4e7,5);return[String(c),String(Math.round(c*(.4+.8*o(6))))]}const a=r(6e4,12e6,7);return[String(a),String(Math.round(a*(.03+.4*o(8))))]}const wt=ke.map((e,n)=>{const o=String(n+9),r=n>=15&&n<=22?"direct":[6,23,31].includes(n)?"gaming":n%9===0?"resilient":n%4===0?"direct":"proxy",a=n%6===0?"udp":"tcp",c=Ye[n%Ye.length],l=`203.0.113.${10+n}:443`;return{network:a,row:Z(o,l,c,r,...An(e,n),e,a,Ze[n%Ze.length])}});J.tcp.push(...wt.filter(e=>e.network==="tcp").map(e=>e.row));J.udp.push(...wt.filter(e=>e.network==="udp").map(e=>e.row));J.total_tcp=J.tcp.length;J.total_udp=J.udp.length;const $n="3",Mn={"flow-unobserved":!0},Ne={...he[0],id:"flow-unobserved",connection_id:null,state:"observed",outbound:null,chain:[],chain_source:"unknown",rule_id:null,rule_expression:null,rule_source:"unknown",trace_status:"partial",trace:{status:"partial",missing:["not_instrumented"],steps:[]}};he.push(Ne,{...Ne,id:"flow-ipv6",input:{...Ne.input,src:"[2001:db8::12]:5000"}});const Nn={...J.tcp.find(e=>e.id==="3"),id:"failed-1",dst:"198.51.100.23:443",state:"failed",started_at:B(12),upload_bytes:"0",download_bytes:"0",upload_bytes_per_second:"0",download_bytes_per_second:"0"};function vt(e,n,o,r="tcp"){const a={id:e,flow_id:"flow-"+e,pname:null,state:o==="block"?"blocked":"closed",src:"10.0.0."+(10+Number(e.replace(/\D/g,""))%40)+":"+(4e4+Number(e.replace(/\D/g,""))),dst:"203.0.113."+Number(e.replace(/\D/g,""))%250+":443",domain:n,outbound:o,started_at:B(15+Number(e.replace(/\D/g,""))*13%270),observed_by:o==="direct"||o==="block"?"ebpf":"userspace"};he.push(Ue(a,r,V,K))}for(const[e,n,o]of[["r01","www.netflix.com","proxy"],["r02","rr1---sn-ab5l6n7z.googlevideo.com","proxy"],["r03","www.google.com","proxy"],["r04","raw.githubusercontent.com","proxy"],["r05","gateway.icloud.com","direct"],["r06","login.live.com","proxy"],["r07","store.steampowered.com","proxy"],["r08","api.spotify.com","proxy"],["r09","pbs.twimg.com","proxy"],["r10","scontent.cdninstagram.com","proxy"],["r11","chatgpt.com","proxy"],["r12","claude.ai","proxy"],["r13","www.reddit.com","proxy"],["r14","en.wikipedia.org","proxy"],["r15","api.cloudflare.com","proxy"],["r16","www.taobao.com","direct"],["r17","weixin.qq.com","direct"],["r18","www.zhihu.com","direct"],["r19","www.douyin.com","direct"],["r20","www.iqiyi.com","direct"],["r21","www.jd.com","direct"],["r22","www.xiaohongshu.com","direct"],["r23","www.baidu.com","direct"],["r24","steamcdn-a.akamaihd.net","proxy"],["r25","www.speedtest.net","direct"],["r26","ad.doubleclick.net","block"],["r27","pagead2.googlesyndication.com","block"],["r28","controlplane.tailscale.com","direct"],["r29","www.youtube.com","proxy"],["r30","graph.facebook.com","proxy"]])vt(e,n,o);for(let e=31;e<=150;e++){const n=e%19===0?"ad.doubleclick.net":ke[e*7%ke.length],o=n.includes("doubleclick.net")?"block":e%5===0?"direct":"proxy";vt("r"+String(e).padStart(2,"0"),n,o,e%7===0?"udp":"tcp")}function On(){const e=[...J.tcp.map(r=>({connection:r,network:"tcp"})),...J.udp.map(r=>({connection:r,network:"udp"}))].filter(({connection:r})=>Number(r.id)<=8).sort((r,a)=>Number(r.connection.id)-Number(a.connection.id)),n={...J,tcp:[],udp:[],total_tcp:0,total_udp:0},o=[];for(let r=0;r<1200;r++){const{connection:a,network:c}=e[r%e.length],l=String(r+1).padStart(4,"0"),i={...a,id:"c-"+l,started_at:B(r*3600/1200),flow_id:(r+1)%3===0?"flow-c-"+l:null};if(n[c].push(i),i.flow_id){const d=Ue(i,c,V,K);o.push({...d,id:i.flow_id,connection_id:i.id})}}return n.total_tcp=n.tcp.length,n.total_udp=n.udp.length,{connections:n,flows:o}}const ie={observed_at:V,coverage:{positive:!0,negative:!0,persistent:!1},total:5,next_cursor:null,entries:[{entry_id:"c1",domain:"api.telegram.org.",type:"A",class:"IN",status:"NOERROR",answers:[{name:"api.telegram.org.",type:"A",class:"IN",ttl:240,data:"149.154.167.220"}],expires_at:te(240),stale_until:null},{entry_id:"c2",domain:"cdn.bilibili.com.",type:"A",class:"IN",status:"NOERROR",answers:[{name:"cdn.bilibili.com.",type:"A",class:"IN",ttl:60,data:"120.92.78.14"}],expires_at:te(60),stale_until:te(120)},{entry_id:"c3",domain:"cdn.bilibili.com.",type:"AAAA",class:"IN",status:"NXDOMAIN",expires_at:te(60),stale_until:null},{entry_id:"c4",domain:"doubleclick.net.",type:"A",class:"IN",status:"NOERROR",answers:[{name:"doubleclick.net.",type:"A",class:"IN",ttl:3600,data:"0.0.0.0"}],expires_at:te(3600),stale_until:null},{entry_id:"c5",domain:"discord.com.",type:"HTTPS",class:"IN",status:"NXDOMAIN",expires_at:te(540),stale_until:null}]};for(const[e,n]of ke.entries())for(const o of["A","AAAA"]){const r=n+".";if(ie.entries.some(c=>c.domain===r&&c.type===o))continue;const a=o==="AAAA"&&e%9===0;ie.entries.push({entry_id:`demo-${e}-${o}`,domain:r,type:o,class:"IN",status:a?"NXDOMAIN":"NOERROR",...a?{}:{answers:[{name:r,type:o,class:"IN",ttl:600,data:o==="A"?`192.0.2.${e+20}`:`2001:db8::${(e+20).toString(16)}`}]},expires_at:te(600+e%6*300),stale_until:null})}ie.total=ie.entries.length;const Dn={cn:["cn","bilibili.com","baidu.com","qq.com"],telegram:["telegram.org","telegram.me","t.me","telegra.ph"],openai:["openai.com","chatgpt.com","oaistatic.com"]},Cn={domain:"domain",pname:"pname",l4proto:"network",dport:"dst_port",dip:"dst_ip",sip:"src_ip",ipversion:"dst_ip",mac:"mac"},fe=e=>e.toLowerCase().replace(/\.$/,""),qe=(e,n)=>e===n||e.endsWith("."+n);function et(e,n){const[o,r="32"]=n.split("/");if(e.includes(":")||o.includes(":"))return e.toLowerCase()===o.toLowerCase();const a=l=>l.split(".").reduce((i,d)=>i<<8|Number(d),0)>>>0,c=Number(r)===0?0:4294967295<<32-Number(r)>>>0;return(a(e)&c)===(a(o)&c)}function Rn(e,n){const o=/^(\w+)\((.*)\)$/.exec(e),[,r,a]=o,c=Cn[r],l=c==="mac"?void 0:n[c];if(l==null||l==="")return{result:"indeterminate",missing_inputs:[c??r]};let i=!1;if(r==="domain"){const[d,s]=a.split(":").map(v=>v.trim()),p=fe(String(l));i=d==="geosite"?(Dn[s]??[]).some(v=>qe(p,v)):d==="suffix"?qe(p,fe(s)):d==="full"?p===fe(s):d==="keyword"&&p.includes(fe(s))}else if(r==="dip"||r==="sip"){const d=String(l).toLowerCase();i=a==="geoip: private"?d.includes(":")?/^(f[cd]|fe[89ab])/.test(d)||d==="::1":["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8","169.254.0.0/16"].some(s=>et(d,s)):a.split(",").some(s=>et(d,s.trim()))}else r==="ipversion"?i=a===(String(l).includes(":")?"6":"4"):i=a.split(",").some(d=>d.trim()===String(l));return{result:i?"matched":"not_matched",missing_inputs:[]}}function tt(e,n){let o=!1,r=null;const a=new Set,c=n.rules.filter(l=>l.kind==="rule").map(l=>{const i=l.expression.split(/\s*&&\s*/).map((p,v)=>({id:l.rule_id+"/"+v,expression:p,...o?{result:"skipped",missing_inputs:[]}:Rn(p,e)})),d=o?"skipped":i.some(p=>p.result==="not_matched")?"not_matched":i.some(p=>p.result==="indeterminate")?"indeterminate":"matched",s=d==="indeterminate"?[...new Set(i.flatMap(p=>p.missing_inputs))]:[];for(const p of s)a.add(p);return d==="matched"&&(o=!0,r=l.outbound),{rule_id:l.rule_id,expression:l.expression+" -> "+l.outbound+(l.must?"(must)":""),result:d,missing_inputs:s,conditions:i}});return c.push({rule_id:"fallback",expression:"fallback: "+n.fallback.outbound,result:o?"skipped":"matched",missing_inputs:[],conditions:[]}),o||(r=n.fallback.outbound),{dst_ip:e.dst_ip??null,decision:a.size?"indeterminate":"determinate",outbound:a.size?null:r,missing_inputs:[...a],rules:c}}function jn({input:e,resolve:n},o,r){if(!e.domain&&!e.dst_ip)throw new h(400,"invalid_request","A domain or destination IP is required");if(n==="live"&&(!e.domain||e.dst_ip))throw new h(400,"invalid_request","Live resolution requires a domain and no destination IP");const a={mode:"simulation",instance_id:K,generation_id:o.generation_id,observed_at:new Date().toISOString(),evaluations:[],dns:[]};if(n==="none")return a.evaluations.push(tt(e,o)),a;const c=fe(e.domain)+".",l=r.entries.filter(d=>d.domain===c&&(d.type==="A"||d.type==="AAAA")),i=[...new Set(l.flatMap(d=>(d.answers??[]).filter(s=>s.type==="A"||s.type==="AAAA").map(s=>s.data)))];return a.dns.push({lookup_id:"simulation-dns-1",parent_lookup_id:null,attempt_id:null,purpose:"dial_target",name:c,qtype:l.length===1?l[0].type:"ANY",source:"cache",upstream_transport:null,carrier_transport:null,cache:l.length?"hit":"miss",cache_entry_id:l.length===1?l[0].entry_id:null,upstream:null,route_evaluation_ids:[],status:i.length?"NOERROR":l[0]?.status??"SERVFAIL",addresses:i,selected_ip:null,error:l.length?null:"No address in mock DNS cache"}),a.evaluations=i.map(d=>tt({...e,dst_ip:d},o)),a}const nt=new WeakMap;function Pn(e,n){let o=nt.get(e);return o||nt.set(e,o=En(e,n)),o}function En(e,n){let o=941;const r=()=>(o=o*48271%2147483647)/2147483647,a=[...new Set(e.map(d=>d.input.domain).filter(d=>!!d))],c=[...new Set([...J.tcp,...J.udp].map(d=>ue(d.src)))],l=["ad.doubleclick.net","pagead2.googlesyndication.com","ads.tracker.example"];let i=0;return Array.from({length:480},(d,s)=>{i+=12+Math.floor(r()*24)+(s%96<28?4:32);const p=n&&s%73===0,m=(p?l[s%l.length]:a[Math.floor(r()*(r()<.72?Math.min(a.length,16):a.length))]).replace(/\.$/,"")+".",_=s%4===0?"AAAA":"A",b=ie.entries.find(u=>u.domain===m&&u.type===_),w=!p&&!!b&&s%3===0,N=n&&!w&&!p&&s%23===7,C=!w&&!N&&!p&&s%11===5,t=p?"REFUSED":w?b.status:N?s%2?"TIMEOUT":"SERVFAIL":C?"NXDOMAIN":"NOERROR",f=w||p?null:s%7===0?"udp://223.5.5.5":s%4===0?"https://dns.google/dns-query":"tls://1.1.1.1";return{id:"dl-"+String(s+1).padStart(6,"0"),observed_at:new Date(Date.parse(V)-i*1e3).toISOString(),src:`${c[Math.floor(r()*c.length)]}:${4e4+s%2e4}`,question:{name:m,type:_},status:t,cached:w,upstream:f,route:{source:"default",rule:null},elapsed_ms:w?.3:p?1.8:N?3e3+s%3*400:s%9===2?150+s%7*32:8+s%31,answers:w?b?.answers??[]:t==="NOERROR"?[{name:m,type:_,class:"IN",ttl:300,data:_==="AAAA"?"2001:db8::20":"192.0.2.20"}]:[]}})}function Ln(e,n,o,r,a,c,l=!1,i=!1){const d=_e("flows"),s=_e("dnsCache"),p=_e("dnsLog"),v=n?On():void 0,m=v?.flows??structuredClone(he),_=v?.connections??structuredClone(J);i&&!v&&_.tcp.push(structuredClone(Nn)),o==="m1"&&(_.visibility="partial");const b=structuredClone(ie);function w(t,f){if(t.state==="active"){const S=r.outbounds.find(O=>O.name===t.outbound);S&&S.active_connections--}t.state="closed",t.upload_bytes_per_second=t.download_bytes_per_second="0";const u=m.find(S=>S.connection_id===t.id);if(!u)return;const g=new Date().toISOString(),A=u.trace.steps[u.trace.steps.length-1];u.state="closed",u.ended_at=g,u.revision++,u.trace.steps.push({seq:(A?.seq??0)+1,observed_at:g,elapsed_us:Math.max(A?.elapsed_us??0,(Date.now()-Date.parse(u.started_at))*1e3),generation_id:a(),evidence:"observed",stage:"connection",data:{state:"closed",milestone:"terminal",reason:f,attempt_id:null,reply_received:null,error:null}})}const N={connections:async(t,f)=>{if(f?.throwIfAborted(),l)for(const O of[..._.tcp,..._.udp])O.state==="active"&&(O.download_bytes=String(BigInt(O.download_bytes??"0")+1460n));const u=t?.src===void 0?void 0:Ae(t.src);if(t?.src!==void 0&&!u)throw new h(400,"invalid_request","Expected a source IP literal");const g=t?.type==="udp"?[]:_.tcp.filter(O=>!u||ue(O.src)===u),A=t?.type==="tcp"?[]:_.udp.filter(O=>!u||ue(O.src)===u),S=t?.limit??1e3;return{..._,total_tcp:g.length,total_udp:A.length,tcp:structuredClone(g.slice(0,S)),udp:structuredClone(A.slice(0,Math.max(0,S-g.length))),truncated:g.length+A.length>S}},flows:async(t,f)=>{f?.throwIfAborted();const u=d(m.filter(g=>(!t?.network||t.network==="all"||g.network===t.network)&&(!t?.state||t.state==="all"||g.state===t.state)&&(t?.connection_id===void 0||g.connection_id===t.connection_id)),t);return{instance_id:K,observed_at:V,coverage:{userspace_tcp:"full",userspace_udp:"full",kernel_direct:"partial",kernel_block:"partial",dns_intercept:"partial",kernel_bypass:"none"},dropped_records:n||!i?"0":$n,flows:u.items.map(({trace:g,input:A,...S})=>Mn[S.id]?S:{...S,input:A}),next_cursor:u.next_cursor}},flow:async(t,f)=>(f?.throwIfAborted(),structuredClone(W(m.find(u=>u.id===t),"Flow"))),routingTrace:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.routing_trace.available)throw new h(404,"capability_not_supported","Routing trace is unavailable");return jn(t,await c(),b)},dnsCache:async(t,f)=>{f?.throwIfAborted();const u=t?.name??t?.domain,g=b.entries.filter(O=>(!u||O.domain===u||O.domain===u+".")&&(!t?.type||t.type.includes(O.type))),A=s(g,t),S={entries:String(b.entries.length),entry_capacity:"256"};return{...b,coverage:{...b.coverage},entries:A.items,total:A.total,next_cursor:A.next_cursor,usage:S}},dnsLog:async(t,f)=>{f?.throwIfAborted();const u=e.resources.dns_log;if(!u.available)throw new h(404,"capability_not_supported","DNS log unavailable");if(t?.limit!==void 0&&t.limit>u.max_page_size)throw new h(400,"invalid_request","limit exceeds the advertised page size");const g=t?.name?.toLowerCase(),A=t?.src===void 0?void 0:Ae(t.src),S=Pn(m,i),O=S.filter(z=>(!g||z.question.name.toLowerCase().includes(g))&&(!t?.type||z.question.type===t.type)&&(!A||z.src!==null&&ue(z.src)===A)),G=p(O,{...t,limit:t?.limit??200});return{observed_at:new Date().toISOString(),total:S.length,next_cursor:G.next_cursor,records:G.items}},dnsQuery:async(t,f,u)=>{u?.throwIfAborted();const g=t.trim().toLowerCase().replace(/\.$/,"")+".";return{domain:g,cache_mode:"normal",query_time:new Date().toISOString(),results:f.map(A=>{const S=b.entries.find(G=>G.domain===g&&G.type===A&&Date.parse(G.expires_at)>Date.now()),O=A==="AAAA"?"2001:db8::14":A==="HTTPS"?'1 . alpn="h2"':"192.0.2.14";return{type:A,cached:!!S,cache_entry_id:S?.entry_id??null,upstream:S?null:"udp://192.0.2.53",route:{source:"default",rule:null},status:S?.status??"NOERROR",elapsed_ms:S?.1:8.4,question:{name:g,type:A},answers:S?structuredClone(S.answers??[]):[{name:g,type:A,class:"IN",ttl:60,data:O}]}})}},closeConnection:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.connections.can_close)throw new h(404,"capability_not_supported","Closing connections is unavailable");for(const u of[_.tcp,_.udp]){const g=u.findIndex(A=>A.id===t);if(!(g<0)){if(u[g].observed_by==="ebpf")throw new h(409,"state_conflict","Connection is not owned by the userspace datapath");w(u[g],"closed_by_request"),u.splice(g,1);return}}throw new h(404,"resource_not_found","Connection not found")},closeConnections:async(t,f)=>{if(f?.throwIfAborted(),!e.resources.connections.can_close)throw new h(404,"capability_not_supported","Closing connections is unavailable");const u=t?.src===void 0?void 0:Ae(t.src);if(t?.src!==void 0&&!u)throw new h(400,"invalid_request","Expected a source IP literal");const g=t?.type??"all";if(g==="all"&&!u&&!t?.all)throw new h(400,"invalid_request","An unfiltered close needs all=true");const S=(g==="tcp"?["tcp"]:g==="udp"?["udp"]:["tcp","udp"]).flatMap(x=>_[x].filter(k=>!u||ue(k.src)===u).map(k=>({network:x,c:k}))),O=e.resources.connections.max_bulk_close??1e3;if(S.length>O)throw new h(413,"request_too_large",`More than ${O} connections match`);let G=0,z=0;for(const{network:x,c:k}of S){if(k.observed_by==="ebpf"){z+=1;continue}w(k,"closed_by_request"),_[x].splice(_[x].indexOf(k),1),G+=1}return{closed:G,skipped:z}},deleteDnsEntry:async(t,f)=>{f?.throwIfAborted();const u=b.entries.findIndex(g=>g.entry_id===t);return u<0?{deleted:0}:(b.entries.splice(u,1),{deleted:1})},flushDnsCache:async t=>{t?.throwIfAborted();const f=b.entries.length;return b.entries.length=0,{matched:f,deleted:f}}};function C(t,f){let u=!1;for(const g of _[f])g.outbound!==t||!["active","dialing","routing"].includes(g.state)||(u=!0,w(g,"group_selection_changed"));return u}return{api:N,interrupt:C}}const q=[...J.tcp,...J.udp].filter(e=>e.state==="active"),xe={up:q.reduce((e,n)=>e+Number(n.upload_bytes_per_second),0),down:q.reduce((e,n)=>e+Number(n.download_bytes_per_second),0)};function Fe(e){const n=1+.24*Math.sin(e/7200)+.19*(Math.cos(e/3100)-1),o=1+.3*Math.sin(e/86400*Math.PI*2),r=e>23e3&&e<31e3?.55*Math.sin((e-23e3)/8e3*Math.PI):0;return{sampled_at:B(e),upload_bytes_per_second:String(Math.max(0,Math.round(xe.up*(n*o+r*.6)))),download_bytes_per_second:String(Math.max(0,Math.round(xe.down*(n*o+r)))),connections:Math.max(2,Math.round(q.length+8*Math.sin(e/9e3)+5*(Math.cos(e/3600)-1)+r*12))}}function yt(e){return{observed_at:B(-e),window_seconds:3600,sampled_every_seconds:10,samples:Array.from({length:361},(n,o)=>Fe(3600-o*10-e))}}yt(0);const kt=Array.from({length:1008},(e,n)=>Fe(604800-n*600)),xt=e=>kt.reduce((n,o)=>n+BigInt(o[e])*600n,0n),Pe=xt("download_bytes_per_second"),Ee=xt("upload_bytes_per_second"),Tn=420,ot=Math.round(kt.reduce((e,n)=>e+n.connections*600,0)/Tn),Le=["direct","proxy","resilient","gaming","block"].map(e=>({name:e,kind:e==="direct"||e==="block"?"builtin":"group",active:q.filter(n=>n.outbound===e).length,weight:q.filter(n=>n.outbound===e).reduce((n,o)=>n+BigInt(o.download_bytes??"0")+BigInt(o.upload_bytes??"0"),0n)})),Oe=Le.reduce((e,n)=>e+n.weight,0n),oe={up:0n,down:0n,connections:0},St={observed_at:V,counter_since:B(604800),outbounds:Le.map((e,n)=>{const o=n===Le.length-2,r=o?Ee-oe.up:Ee*e.weight/Oe,a=o?Pe-oe.down:Pe*e.weight/Oe,c=o?ot-oe.connections:Math.floor(ot*Number(e.weight)/Number(Oe));return oe.up+=r,oe.down+=a,oe.connections+=c,{name:e.name,kind:e.kind,active_connections:e.active,total_connections:String(c),upload_bytes:String(r),download_bytes:String(a),errors:"0"}})},Gn={api:{name:"dae/honk-native",major:1,status:"draft"},engine:{name:"honk",version:"v0.0.1.beta.81"},build:{revision:"697ac9ea5e82bc120ed942943fe0cafbdd7bf423",target:null,built_at:null}},It={observed_at:V,instance_id:K,lifecycle:{state:"running",started_at:B(777600),uptime_seconds:"777600"},generation:{active_id:De,config_revision:De,state:"active",activated_at:B(210)},datapath:{kind:"ebpf",state:"active",visibility:"full",ebpf:{backend:"real",programs:"loaded",hooks:"attached",routing:{state:"published",generation_id:"40"},health:"healthy",last_error:null,checked_at:V}},traffic:{scope:"visible",observed_by:"mixed",counter_since:St.counter_since,sampled_at:V,connections:{tcp:q.filter(e=>J.tcp.includes(e)).length,udp:q.filter(e=>J.udp.includes(e)).length,total:q.length},bytes:{upload:String(Ee),download:String(Pe)},rates:{window_seconds:10,upload_bytes_per_second:String(xe.up),download_bytes_per_second:String(xe.down)}},process:{pid:1842,cpu_percent:2.1},last_reload:{operation_id:"op-1182",status:"succeeded",finished_at:B(170),error:null}},zn={observed_at:V,kind:"ebpf",state:"active",visibility:"full",ebpf:{...It.datapath.ebpf,attachments:["lan0","wan0"].flatMap(e=>["ingress","egress"].map(n=>({name:"honk_"+n,interface:e,direction:n,state:"attached"}))),maps:{state:"ready",conn_state:{occupancy:q.length,capacity:65536,occupancy_known:!0}}},errors:[]},st={code:"sample_delayed",message:"Routing map sample delayed"},Un={operation_id:"op-1183",status:"failed",finished_at:B(50),error:{code:"routing_publish_timeout",message:"Routing map update timed out; the previous generation stays active"}},Se={observed_at:V,process:{rss_bytes:"48234496"},cgroup:{scope:"service",current_bytes:"67108864",limit_bytes:"268435456",events:{high:"2",oom:"0",oom_kill:"0"}},kernel:{ebpf_bytes:"18874368",sampled_at:V}},Fn=String(78*1048576);function rt(e){const n=1+.04*Math.sin(e/6e4)+.03*Math.sin(e/36e5)+.01*Math.sin(e/216e5);return{rss_bytes:String(Math.round(Number(Se.process.rss_bytes)*n)),cgroup_current_bytes:String(Math.round(Number(Se.cgroup.current_bytes)*n))}}function Hn(e,n,o=!1){const r=structuredClone(It),a=structuredClone(zn);if(o){for(const d of[r.datapath,a])d.state="degraded",Object.assign(d.ebpf,{health:"degraded",last_error:st.message});a.errors=[structuredClone(st)],r.last_reload=structuredClone(Un)}const c=structuredClone(Se);o&&(c.cgroup.limit_bytes=Fn);const l=structuredClone(St);return n&&l.outbounds.push(...Array.from({length:24},(d,s)=>({name:`kad.${["hk","jp","us"][s%3]}.${String(s+1).padStart(2,"0")}`,kind:"group",active_connections:0,total_connections:"1",upload_bytes:"0",download_bytes:String(Math.max(0,9e5-s*6e4)),errors:"0"}))),{api:{discovery:async d=>(d?.throwIfAborted(),{name:"dae/honk-native",status:"draft",api_major:1,base_path:"/api/v1",links:{version:"/api/v1/version",capabilities:"/api/v1/capabilities",runtime:"/api/v1/runtime",runtime_outbounds:"/api/v1/runtime/outbounds",config:"/api/v1/config",config_validate:"/api/v1/config/validate",traffic_history:"/api/v1/runtime/traffic/history",memory_history:"/api/v1/runtime/memory/history",logs:"/api/v1/logs",providers:"/api/v1/providers",geodata:"/api/v1/geodata",rules:"/api/v1/rules",operations:"/api/v1/operations/{id}"}}),version:async d=>(d?.throwIfAborted(),structuredClone(Gn)),capabilities:async d=>(d?.throwIfAborted(),structuredClone(e)),runtime:async d=>{d?.throwIfAborted();const s=Date.now();r.lifecycle.started_at&&(r.lifecycle.uptime_seconds=String(Math.max(0,Math.floor((s-Date.parse(r.lifecycle.started_at))/1e3))));const p=structuredClone(r),v=1+.15*Math.sin(s/2e4);return p.traffic.sampled_at=new Date(s).toISOString(),p.traffic.rates&&(p.traffic.rates.upload_bytes_per_second=String(Math.round(Number(r.traffic.rates.upload_bytes_per_second)*v)),p.traffic.rates.download_bytes_per_second=String(Math.round(Number(r.traffic.rates.download_bytes_per_second)*v))),p},runtimeOutbounds:async d=>{if(d?.throwIfAborted(),!e.resources.runtime_outbounds.available)throw new h(404,"capability_not_supported","Outbound counters unavailable");return structuredClone(l)},trafficHistory:async(d,s)=>{s?.throwIfAborted();const p=e.resources.traffic_history;if(!p.available)throw new h(404,"capability_not_supported","Traffic history unavailable");const v=d?.window_seconds??p.max_window_seconds,m=d?.max_points??p.max_points;if(!Number.isSafeInteger(v)||v<1||v>p.max_window_seconds||!Number.isSafeInteger(m)||m<1||m>p.max_points)throw new h(400,"invalid_request","History query exceeds the advertised limits");const _=Math.floor((Date.now()-Ie)/1e4)*10,b=yt(_);if(v>3600){const C=Math.ceil(v/Math.max(1,m-1)/10)*10,t=Math.min(m,Math.floor(v/C)+1);return{...b,window_seconds:v,sampled_every_seconds:C,samples:Array.from({length:t},(f,u)=>Fe((t-1-u)*C-_))}}const w=b.samples.filter(C=>Date.parse(C.sampled_at)>Date.parse(b.observed_at)-v*1e3),N=Math.max(1,Math.ceil(w.length/m));return{...b,window_seconds:v,sampled_every_seconds:b.sampled_every_seconds*N,samples:structuredClone(w.filter((C,t)=>(w.length-1-t)%N===0))}},memoryHistory:async(d,s)=>{s?.throwIfAborted();const p=e.resources.memory_history;if(!p.available)throw new h(404,"capability_not_supported","Memory history unavailable");const v=d?.window_seconds??p.max_window_seconds,m=d?.max_points??p.max_points;if(!Number.isSafeInteger(v)||v<1||v>p.max_window_seconds||!Number.isSafeInteger(m)||m<1||m>p.max_points)throw new h(400,"invalid_request","History query exceeds the advertised limits");const _=Date.now(),b=Math.ceil(v/Math.max(1,m-1)/5)*5,w=Math.min(m,Math.floor(v/b)+1),N=Array.from({length:w},(C,t)=>{const f=(w-1-t)*b,u=_-f*1e3;return{sampled_at:new Date(u).toISOString(),...rt(u),kernel_ebpf_bytes:Se.kernel?.ebpf_bytes??null}});return{observed_at:new Date(_).toISOString(),window_seconds:v,sampled_every_seconds:b,samples:N}},datapath:async(d,s)=>{s?.throwIfAborted();const p=structuredClone(a);return d!=="full"&&p.ebpf&&(delete p.ebpf.attachments,delete p.ebpf.maps),p},runtimeMemory:async d=>{d?.throwIfAborted();const s=structuredClone(c),p=Date.now(),v=rt(p);return s.observed_at=new Date(p).toISOString(),s.process={rss_bytes:v.rss_bytes},s.cgroup={...s.cgroup,current_bytes:v.cgroup_current_bytes},s}},runtime:r,outbounds:l}}const it=["geosite","geoip"];function at(e){const n={geosite:new Set,geoip:new Set};for(const o of e){const r=ae(o).tokens.map(a=>o.slice(a.from,a.to));r.forEach((a,c)=>{(a==="geosite"||a==="geoip")&&r[c+1]===":"&&n[a].add(r[c+2].split("@")[0].toLowerCase())})}return{geosite:[...n.geosite].sort(),geoip:[...n.geoip].sort()}}const Bn=e=>/^https:\/\/(raw\.githubusercontent\.com|[a-z]+\.jsdelivr\.net)\//.test(e),lt=(e,n)=>Ht.find(o=>o.urls[e].includes(n)||o.mirrors[e].includes(n));function Vn(e,n,o=!1){const r=e.resources.geodata.configurable_sources===!0;let a=null;const c={enabled:!0,interval_hours:24};let l={route:"routing",group_id:null};const i=structuredClone(un);i.required_codes=at([...Ge,...o?ze:[]].map(m=>m.cond)),o&&Object.assign(i,pn);const d=()=>a??{geosite:[...re.urls.geosite],geoip:[...re.urls.geoip]},s=m=>c.enabled?new Date(m+c.interval_hours*36e5+17*6e4).toISOString():null;i.next_check_at=s(Date.parse(i.last_checked_at??"")||Date.now());const p=m=>new h(400,"invalid_request",m),v=()=>{const m=structuredClone(i);if(!r){for(const _ of m.assets)delete _.fetched_url_redacted,delete _.verified,delete _.download_route;delete m.last_checked_at,delete m.last_updated_at,delete m.next_check_at,delete m.last_error,delete m.required_codes}return{...m,observed_at:new Date().toISOString()}};return{settings:()=>({source:a?"db":"default",geosite:{urls:d().geosite},geoip:{urls:d().geoip},auto_update:{...c},download:{...l,group_id:l.group_id&&n().has(l.group_id)?l.group_id:null}}),patch(m){if(m===null)a=null,Object.assign(c,{enabled:!0,interval_hours:24}),l={route:"routing",group_id:null};else{for(const C of it){const t=m[C]?.urls;if(t){if(!t.length||t.length>Ve)throw p(`geodata.${C}.urls must hold 1 to ${Ve} URLs`);if(new Set(t).size!==t.length)throw p(`geodata.${C}.urls must not repeat a URL`);if(!t.every(Ft))throw p(`geodata.${C}.urls holds an invalid URL`)}}const _=m.auto_update?.interval_hours,{min:b,max:w}=Bt;if(_!==void 0&&(!Number.isInteger(_)||_<b||_>w))throw p(`geodata.auto_update.interval_hours must lie in [${b}, ${w}]`);const N=m.download;if(N&&N.route==="group"!=(N.group_id!==void 0))throw p("geodata.download.group_id is required for route group only");if(N?.group_id!==void 0&&!n().has(N.group_id))throw new h(422,"unsupported_value",`geodata.download.group_id ${N.group_id} is not a current group`);(m.geosite||m.geoip)&&(a={geosite:m.geosite?.urls??d().geosite,geoip:m.geoip?.urls??d().geoip}),Object.assign(c,m.auto_update??{}),N&&(l={route:N.route,group_id:N.group_id??null})}i.next_check_at=s(Date.now())},status:v,follow(m){i.required_codes=at(m)},update(){const m=Date.now(),_=new Date(m).toISOString();i.last_checked_at=_,i.next_check_at=s(m);const b=d();for(const w of it){const N=lt(w,b[w][0])?.categories?.[w];if((N?(i.required_codes?.[w]??[]).filter(t=>!N.includes(t)):[]).length){const t="Geodata update did not complete successfully";i.last_error={code:"asset_validation_failed",message:t,details:null};const f=i.assets.map(u=>({kind:u.kind,written:!1,durability_confirmed:!1}));throw new h(500,"geodata_update_failed",t,null,{stage:"asset_validation_failed",assets:f,committed:!1})}}for(const w of i.assets){const N=b[w.kind][0];w.modified_at=_,w.sha256=Array.from({length:64},()=>Math.floor(Math.random()*16).toString(16)).join(""),w.size_bytes=String((lt(w.kind,N)?.sizes[w.kind]??Number(w.size_bytes))+Math.floor(Math.random()*65536)),w.source_redacted=Re(N),w.fetched_url_redacted=Re(N),w.verified=Bn(N),w.download_route={route:l.route,group_id:l.route==="group"?l.group_id:null}}return i.last_updated_at=_,i.last_error=null,v()}}}function Yn(e={}){let n=120,o=!1,r=!1;try{const b=localStorage.getItem("doona-mock-big");o=b!==null,b!==null&&(n=Math.max(0,Math.floor(Number(b)||0))),r=localStorage.getItem("doona-mock-busy")!==null}catch{}let a=ee,c=null,l=e.faults??!1;try{const b=new URLSearchParams(globalThis.location?.search).get("scenario");b==="faults"?localStorage.setItem("doona-mock-scenario","faults"):b===""&&localStorage.removeItem("doona-mock-scenario"),l||=localStorage.getItem("doona-mock-scenario")==="faults",c=localStorage.getItem("doona-mock-profile"),c==="base"&&(a=Wt),c==="m1"&&(a=Kt)}catch{}const i=Hn(a,o,l),d=Vn(a,()=>m.groupIds(),l),s=rn(a,i.runtime,{enqueue:(b,w)=>p.enqueue(b,w),log:(b,w,N,C)=>p.log(b,w,N,C),publish:b=>p.publish(b),eventData:()=>p.eventData(),trimLogs:()=>p.trimLogs()},()=>m.groupNames(),(b,w)=>m.activate(b,w),d,l),p=wn(a.resources.logs,a.resources.events,i.runtime,s.logSettings,s.revision,r?20:2500,l),v=Ln(a,o,c,i.outbounds,s.revision,s.ruleSnapshot,r,l),m=hn(a,n,p,s.advance,s.editMain,v.interrupt,d,l),_={...i.api,...p.api,...v.api,...m.api,...s.api};return e.signIn&&!Vt(e.session)?Jt(_):_}export{Yn as createMockApi};