snoutdata 0.10.5 → 0.11.0

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.
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- var bc=Object.defineProperty;var D=(e,t)=>()=>(e&&(t=e(e=0)),t);var Vr=(e,t)=>{for(var n in t)bc(e,n,{get:t[n],enumerable:!0})};function Yr(e){let t=[],n={};for(let r=0;r<e.length;r++){let o=e[r];if(!o.startsWith("-")){t.push(o);continue}if(o==="--"){t.push(...e.slice(r+1));break}let[s,a]=xc(o);if(Sc.has(s)){let l=a??e[++r];if(l===void 0)throw new h(`--${s} needs a value`);n[s]=l;continue}if(a!==void 0)throw new h(`--${s} does not take a value`);if(!_c.has(s))throw new h(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function xc(e){let t=e.replace(/^--?/,""),n=t.indexOf("=");return n===-1?[t,void 0]:[t.slice(0,n),t.slice(n+1)]}function A(e,t){let n=e.flags[t];return typeof n=="string"?n:void 0}function G(e,t){return e.flags[t]===!0}function Pe(e,t){let n=A(e,t);if(n===void 0)return;let r=Number(n);if(!Number.isInteger(r))throw new h(`--${t} must be a whole number, not "${n}"`);return r}var h,Sc,_c,ne=D(()=>{"use strict";h=class extends Error{},Sc=new Set(["name","at","region","ref","file","provider","domain","expires","project","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint","memory","concurrency","client-id","site-url","allow","subject","p8","key-id","team-id","topic","environment","email","channel","since","level"]);_c=new Set(["json","window","help","version","force","no-verify-jwt","reset","stdin","quiet","status","no-wait","no-browser","device","sso","env","dry-run","out-of-order","allow-delete","local","no-migrations","history","show-url","check","watch"])});import{chmodSync as vc,existsSync as Zr,mkdirSync as Qr,readFileSync as eo,rmSync as kc,writeFileSync as to}from"node:fs";import{homedir as Et}from"node:os";import{dirname as vn,join as no,parse as Ec,resolve as $c}from"node:path";function kn(e=Et()){return no(e,".snoutdata","auth.json")}function $t(e=Et()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=kn(e);if(!Zr(n))return null;try{let r=JSON.parse(eo(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function ie(e,t=Et()){let n=kn(t);Qr(vn(n),{recursive:!0}),to(n,`${JSON.stringify(e,null,2)}
3
- `,{mode:384}),vc(n,384)}function ro(e=Et()){kc(kn(e),{force:!0})}function oo(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function so(e){return no(e,".snoutdata","project.json")}function Xe(e){let t=$c(e),n=Ec(t).root;for(;;){let r=so(t);if(Zr(r))try{let o=JSON.parse(eo(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return typeof o.local=="string"&&o.local?{ref:o.ref,directory:t,local:o.local}:{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=vn(t)}}function Ze(e,t){let n=so(e);return Qr(vn(n),{recursive:!0}),to(n,`${JSON.stringify(t,null,2)}
4
- `),n}function Qe(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:Xe(e.cwd??process.cwd())?.ref??null}var be=D(()=>{"use strict"});import{existsSync as En,readFileSync as ao}from"node:fs";import{homedir as Ac}from"node:os";import{isAbsolute as Pc,join as Se,resolve as Ce}from"node:path";function Rc(e=process.env,t=process.platform,n=Ac()){let r;return t==="win32"?r=e.APPDATA??Se(n,"AppData","Roaming"):t==="darwin"?r=Se(n,"Library","Application Support"):r=e.XDG_CONFIG_HOME??Se(n,".config"),["snoutdata","snoutdata-dev"].map(o=>Se(r,o,"localProjects.json"))}function At(e=Rc()){let t=[];for(let n of e)if(En(n))try{let r=JSON.parse(ao(n,"utf8"));for(let o of r.projects??[])typeof o.folder=="string"&&o.folder&&!t.some(s=>s.folder===o.folder)&&t.push({name:o.name??null,folder:o.folder,ref:o.ref??null})}catch{}return t}function Pt(e){let t=new Map;for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim(),a=r.slice(o+1).trim();a.length>=2&&(a[0]==='"'||a[0]==="'")&&a.endsWith(a[0])&&(a=a.slice(1,-1)),t.set(s,a)}return t}function et(e){return En(Se(e,"compose.yaml"))&&En(Se(e,".env"))}function _e(e,t=null){let n=Ce(e);if(!et(n))throw new Error(`${n} is not a SnoutData stack folder: it needs compose.yaml and .env (see github.com/snoutdata/snout-stack)`);let r=Pt(ao(Se(n,".env"),"utf8")),o=r.get("SNOUT_REF"),s=r.get("POSTGRES_OWNER_PASSWORD");if(!o||!s)throw new Error(`${Se(n,".env")} has no ${o?"POSTGRES_OWNER_PASSWORD":"SNOUT_REF"}, so this stack was not set up with \`snout-stack init\``);let a=Number(r.get("API_PORT")??8e3)||8e3,l=Number(r.get("DB_PORT")??5432)||5432;return{folder:n,ref:o,name:t,apiUrl:(r.get("API_EXTERNAL_URL")||`http://127.0.0.1:${a}`).replace(/\/$/,""),apiPort:a,dbPort:l,anonKey:r.get("ANON_KEY")||null,serviceRoleKey:r.get("SERVICE_ROLE_KEY")||null,ownerPassword:s}}function io(e,t=process.cwd(),n=At()){if(!e){if(et(t))return _e(t);let[i]=n;if(n.length===1&&i)return _e(i.folder,i.name);throw n.length===0?new Error("there is no local project to link: set one up in Studio (Projects, Local), or pass the folder of a stack you made with snout-stack"):new Error(`there are ${n.length} local projects; name one: ${n.map(u=>u.name??u.folder).join(", ")}`)}let r=Pc(e)?e:Ce(t,e);if(et(r)){let i=n.find(u=>Ce(u.folder)===Ce(r));return _e(r,i?.name??null)}let o=e.toLowerCase(),s=n.filter(i=>i.name?.toLowerCase()===o||i.ref===e),[a]=s;if(s.length===1&&a)return _e(a.folder,a.name);let l=n.map(i=>i.name??i.folder);throw new Error(s.length>1?`more than one local project is called ${e}; link it by its folder`:`no local project called ${e}, and it is not a stack folder${l.length?`. The local projects are ${l.join(", ")}`:""}`)}function co(e){let t=`${e.ref}_owner`;return{ref:e.ref,host:"127.0.0.1",port:e.dbPort,database:e.ref,user:t,password:e.ownerPassword,ssl:"disable",uri:`postgresql://${t}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.dbPort}/${e.ref}`,anonKey:e.anonKey,serviceRoleKey:e.serviceRoleKey,keysIssuedAt:null,keysExpireAt:null,wakesInstantly:!0,state:"local"}}function lo(e,t,n=At()){if(t&&et(t))try{let r=n.find(s=>Ce(s.folder)===Ce(t)),o=_e(t,r?.name??null);if(o.ref===e)return o}catch{}for(let r of n)if(r.ref===e&&et(r.folder))return _e(r.folder,r.name);return null}var tt=D(()=>{"use strict"});function O(e,t,n){throw new N(e,t,n)}function Rt(e){return e===403?"forbidden":e===404?"not-found":e===409?"conflict":e===402||e===429?"quota":e===503?"not-ready":e===504?"timeout":e===410?"outdated":e>=500?"server":"failed"}function uo(e){let t=e;return!t||typeof t!="object"||t.code!=="ENOENT"||typeof t.path!="string"||t.syscall==="spawn"?null:`no file at ${t.path}`}function mo(e){let t=[e],n=e;for(let r=0;r<4&&n instanceof Error&&n.cause;r+=1)n=n.cause,t.push(n);for(let r of t){let o=typeof r=="object"&&r&&"code"in r?String(r.code):"";if(Tc.has(o))return"network";if(o==="ABORT_ERR"||o==="ETIMEDOUT")return"timeout"}return e instanceof Error&&/fetch failed|network|socket hang up/i.test(e.message)?"network":"failed"}var ue,N,Tc,ee=D(()=>{"use strict";ue={ok:0,failed:1,usage:2,"not-signed-in":3,"not-ready":4,forbidden:5,"not-found":6,conflict:7,quota:8,network:9,timeout:10,outdated:11,"tool-missing":127,server:1},N=class extends Error{code;details;constructor(t,n,r){super(n),this.name="CliFailure",this.code=t,this.details=r}get exitCode(){return ue[this.code]}};Tc=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH","EPIPE","CERT_HAS_EXPIRED","UNABLE_TO_VERIFY_LEAF_SIGNATURE","DEPTH_ZERO_SELF_SIGNED_CERT"])});function Tt(e){let t=e.env.SNOUTDATA_NO_INTERACTIVE;if(t!==void 0&&t!==""&&t!=="0"&&t!=="false")return{canAsk:!1,reason:"opted-out"};if(e.json)return{canAsk:!1,reason:"json"};for(let n of Oc){let r=e.env[n];if(r!==void 0&&r!==""&&r!=="0"&&r!=="false")return{canAsk:!1,reason:"ci"}}return e.isTty?{canAsk:!0}:{canAsk:!1,reason:"not-a-tty"}}function Me(e){switch(e){case"json":return"running with --json, so nothing was asked";case"opted-out":return"SNOUTDATA_NO_INTERACTIVE is set, so nothing was asked";case"ci":return"this looks like CI, so nothing was asked";case"not-a-tty":return"there is no terminal to ask at"}}function fo(e){$n=e}function xe(){return($n??Tt({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function je(){return($n??Tt({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var Oc,$n,Le=D(()=>{"use strict";Oc=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];$n=null});import{execFile as Nc}from"node:child_process";import{existsSync as Dc,readFileSync as ho}from"node:fs";import{homedir as An,hostname as Ic}from"node:os";import{join as Ue}from"node:path";function Cc(e,t,n){if(t==="win32"){let r=n.APPDATA??Ue(An(),"AppData","Roaming");return Ue(r,e)}return t==="darwin"?Ue(An(),"Library","Application Support",e):Ue(n.XDG_CONFIG_HOME??Ue(An(),".config"),e)}function yo(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>Ue(Cc(n,e,t),"mcp.json"))}function Mc(e=process.platform,t=process.env){return yo(e,t)[0]}function jc(e=Mc()){if(!Dc(e))return null;try{let t=JSON.parse(ho(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function Lc(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}function Fc(e,t){let n=`0100007F:${t.toString(16).toUpperCase().padStart(4,"0")}`,r=[];for(let o of e.split(`
5
- `).slice(1)){let s=o.trim().split(/\s+/);s.length>7&&s[1]?.toUpperCase()===n&&s[3]==="0A"&&r.push(Number(s[7]))}return r}function Bc(e,t){return e.split(`
6
- `).some(n=>n.trim()===`n127.0.0.1:${t}`)}function po(e,t){return new Promise(n=>{Nc(e,t,{timeout:5e3,windowsHide:!0},(r,o)=>{n(r&&!o?"":String(o))})})}async function wo(e,t=process.platform){if(!Number.isInteger(e)||e<=0||e>65535)return!1;try{if(t==="linux"){let n=process.getuid?.();if(n===void 0)return!1;let r=Fc(ho("/proc/net/tcp","utf8"),e);return r.length>0&&r.every(o=>o===n)}if(t==="darwin"){let n=process.getuid?.();if(n===void 0)return!1;let r=await po("/usr/sbin/lsof",["-nP","-a",`-iTCP@127.0.0.1:${e}`,"-sTCP:LISTEN","-u",String(n),"-Fn"]);return Bc(r,e)}if(t==="win32"){let n=[`$c = Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort ${e} -State Listen -ErrorAction Stop | Select-Object -First 1`,'$p = Get-CimInstance Win32_Process -Filter "ProcessId=$($c.OwningProcess)" -ErrorAction Stop',"$o = Invoke-CimMethod -InputObject $p -MethodName GetOwner -ErrorAction Stop","$me = [Security.Principal.WindowsIdentity]::GetCurrent().Name",'if ($o.ReturnValue -eq 0 -and ("$($o.Domain)\\$($o.User)" -ieq $me)) { "mine" } else { "other" }'].join("; ");return(await po("powershell.exe",["-NoProfile","-NonInteractive","-Command",n])).trim()==="mine"}}catch{return!1}return!1}async function bo(e,t){let n=Pn.get(e);if(n!==void 0&&Date.now()-n<Hc)return!0;let r=await t(e).catch(()=>!1);return r?Pn.set(e,Date.now()):Pn.delete(e),r}async function On(e,t,n,r=wo){if(!await bo(e.port,r))throw new Rn(e.port);return fetch(`http://127.0.0.1:${e.port}${t}`,{...n,headers:{...n.headers,Authorization:`Bearer ${e.token}`}})}async function Ot(e,t=wo){if(e!==void 0)return go(e,t);let n="no-config";for(let r of yo()){let o=jc(r);if(!o)continue;let s=await go(o,t);if(s.available)return s;(s.skip!=="not-running"&&s.skip!=="not-verified"||n==="no-config")&&(n=s.skip)}return{available:!1,skip:n}}async function go(e,t){let n=Lc(e);if(n)return{available:!1,skip:n};let r=e;if(!await bo(r.port,t))return{available:!1,skip:"not-verified"};let o;try{let s=await On(r,"/cli/whoami",{signal:AbortSignal.timeout(Uc)},t);if(!s.ok)return{available:!1,skip:"not-running"};o=await s.json()}catch{return{available:!1,skip:"not-running"}}return o.signedIn?o.canMintToken?{available:!0,who:o,config:r}:{available:!1,skip:"cannot-mint"}:{available:!1,skip:"signed-out"}}async function So(e,t){let n=await On(e,"/cli/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client:"snoutdata-cli",command:t}),signal:AbortSignal.timeout(6e4)}),r=await n.json().catch(()=>({}));return n.ok&&r.ok&&r.token?{token:r.token,name:r.name??"SnoutData CLI"}:{declined:!0,reason:r.error??`SnoutData Studio refused (${n.status})`}}function Nt(){return Ic()}async function Tn(e,t,n,r){let o=await On(e,"/mcp",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:t,params:n}),signal:AbortSignal.timeout(r)});if(!o.ok)throw new Error(`the app answered ${o.status}`);let s=await o.json();if(s.error)throw new Error(s.error.message??"SnoutData Studio refused");return s.result}async function _o(e){return await Tn(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await Tn(e,"tools/list",{},3e3)).tools??[]}async function xo(e,t,n){return Tn(e,"tools/call",{name:t,arguments:n},12e4)}var Uc,Hc,Pn,Rn,Dt=D(()=>{"use strict";Uc=1500;Hc=1e4,Pn=new Map;Rn=class extends Error{constructor(t){super(`the process on 127.0.0.1:${t} is not one of this user's, so SnoutData Studio's token was not sent to it`),this.name="PeerNotVerified"}}});import{basename as Gc}from"node:path";function Ct(e={bun:typeof process.versions.bun=="string",execPath:process.execPath,script:process.argv[1]??""}){let t=Gc(e.execPath.replace(/\\/g,"/")).toLowerCase();if(e.bun&&t.startsWith("snoutdata"))return"binary";let n=e.script.replace(/\\/g,"/");return/\/_npx\//.test(n)?"npx":/\/node_modules\/snoutdata\//.test(n)?"npm":"source"}function qc(e=Ct(),t=process.platform){switch(e){case"binary":case"npm":return"snoutdata upgrade";case"npx":return"npx snoutdata@latest";case"source":return t==="win32"?me:nt}}function Nn(e={}){let t=e.kind??Ct(),n=e.platform??process.platform,r=e.minimum?`${e.minimum} or later is required`:"a newer version is required",o=n==="win32"?me:`${nt} (or ${me})`;return`snoutdata ${V} is installed and ${r}. Run \`${qc(t,n)}\`, or install again: ${o}`}function ko(e,t){if(e==="dev"||t==="dev")return!1;let n=e.split("-")[0].split(".").map(Number),r=t.split("-")[0].split(".").map(Number);for(let o=0;o<Math.max(n.length,r.length);o+=1){let s=n[o]??0,a=r[o]??0;if(Number.isNaN(s)||Number.isNaN(a))return!1;if(s!==a)return s<a}return!1}var V,It,nt,me,rt=D(()=>{"use strict";V="0.10.5",It=`snoutdata-cli/${V}`,nt="curl -fsSL https://snoutdata.com/install.sh | sh",me="npm install -g snoutdata@latest"});async function Eo(e,t){let n=await fetch(`${ce}/functions/v1/${e}`,{method:"POST",headers:{apikey:fe,"Content-Type":"application/json","x-client-info":It},body:JSON.stringify(t)});Lt(n);let r=await n.json().catch(()=>({}));if(n.status===410){let o=r.error;throw Ut(n,typeof o=="string"?o:"This version of the snoutdata CLI is no longer supported.")}return{status:n.status,body:r}}async function Mt(e={}){let{status:t,body:n}=await Eo("cloud-device-start",{client:e});return t===429&&O("quota",n.error??"too many pairing attempts from here: wait a few minutes"),t===404&&O("not-found","this SnoutData does not have the pairing flow (cloud-device-start is not deployed). Use `snoutdata login` to sign in with a browser, or set SNOUTDATA_ACCESS_TOKEN."),(t!==200||!n.code||!n.secret)&&O("server",n.error??`could not start a pairing (${t})`),n}async function jt(e,t){let n=t.intervalMs,r=Date.parse(t.expiresAt);for(;;){Date.now()>r&&O("timeout","that pairing code expired before it was approved. Run the command again for a new one."),await new Promise(a=>setTimeout(a,n));let{status:o,body:s}=await Eo("cloud-device-poll",{secret:e});switch(o===404&&O("not-found","that pairing is not known to the server. Run the command again for a new code."),o===400&&O("usage",s.error??"that is not a pairing secret"),o!==200&&O("server",s.error??`the pairing could not be checked (${o})`),s.status){case"approved":return{token:s.token,name:s.name};case"denied":O("forbidden",s.error??"that pairing was refused in the browser");case"used":O("conflict",s.error??"that pairing has already been completed");case"expired":O("timeout",s.error??"that pairing code expired before it was approved");case"slow-down":n=Math.max(n*2,(s.interval??2)*1e3);break;default:s.interval&&(n=Math.max(n,s.interval*1e3));break}t.onWait?.(Math.max(0,Math.round((r-Date.now())/1e3)))}}var Dn=D(()=>{"use strict";J();ee();rt()});function st(e){ot=e}function Ao(e){$o=e}function Po(){return ot}function b(e){return Ro?`\x1B[2m${e}\x1B[0m`:e}function k(e){return Ro?`\x1B[1m${e}\x1B[0m`:e}function g(e,t){if(ot){process.stdout.write(`${JSON.stringify(e,null,2)}
7
- `);return}t()}function c(e){!ot&&!$o&&process.stderr.write(`${e}
8
- `)}function ve(e,t,n){process.stderr.write(`${t}
2
+ var kc=Object.defineProperty;var I=(e,t)=>()=>(e&&(t=e(e=0)),t);var Xr=(e,t)=>{for(var n in t)kc(e,n,{get:t[n],enumerable:!0})};function Zr(e){let t=[],n={};for(let r=0;r<e.length;r++){let o=e[r];if(!o.startsWith("-")){t.push(o);continue}if(o==="--"){t.push(...e.slice(r+1));break}let[s,a]=$c(o);if(vc.has(s)){let d=a??e[++r];if(d===void 0)throw new y(`--${s} needs a value`);n[s]=d;continue}if(a!==void 0)throw new y(`--${s} does not take a value`);if(!Ec.has(s))throw new y(`unknown option --${s}`);n[s]=!0}return{command:t,flags:n,json:n.json===!0}}function $c(e){let t=e.replace(/^--?/,""),n=t.indexOf("=");return n===-1?[t,void 0]:[t.slice(0,n),t.slice(n+1)]}function P(e,t){let n=e.flags[t];return typeof n=="string"?n:void 0}function q(e,t){return e.flags[t]===!0}function Oe(e,t){let n=P(e,t);if(n===void 0)return;let r=Number(n);if(!Number.isInteger(r))throw new y(`--${t} must be a whole number, not "${n}"`);return r}var y,vc,Ec,re=I(()=>{"use strict";y=class extends Error{},vc=new Set(["name","at","region","ref","file","provider","domain","expires","project","timeout","out","team","dir","days","schema","db-url","data-api-version","postgrest-version","port","entrypoint","memory","concurrency","client-id","site-url","allow","subject","p8","key-id","team-id","topic","environment","email","channel","since","level"]);Ec=new Set(["json","window","help","version","force","no-verify-jwt","reset","stdin","quiet","status","no-wait","no-browser","device","sso","env","dry-run","out-of-order","allow-delete","local","no-migrations","history","show-url","check","watch"])});import{chmodSync as Pc,existsSync as eo,mkdirSync as to,readFileSync as no,rmSync as Ac,writeFileSync as ro}from"node:fs";import{homedir as Et}from"node:os";import{dirname as vn,join as oo,parse as Tc,resolve as Rc}from"node:path";function En(e=Et()){return oo(e,".snoutdata","auth.json")}function $t(e=Et()){let t=process.env.SNOUTDATA_ACCESS_TOKEN;if(t)return{accessToken:t};let n=En(e);if(!eo(n))return null;try{let r=JSON.parse(no(n,"utf8"));return typeof r.accessToken=="string"?r:null}catch{return null}}function le(e,t=Et()){let n=En(t);to(vn(n),{recursive:!0}),ro(n,`${JSON.stringify(e,null,2)}
3
+ `,{mode:384}),Pc(n,384)}function so(e=Et()){Ac(En(e),{force:!0})}function ao(e,t=Date.now()){return e.expiresAt?e.expiresAt*1e3-6e4<=t:!1}function io(e){return oo(e,".snoutdata","project.json")}function Le(e){let t=Rc(e),n=Tc(t).root;for(;;){let r=io(t);if(eo(r))try{let o=JSON.parse(no(r,"utf8"));if(typeof o.ref=="string"&&o.ref)return typeof o.local=="string"&&o.local?{ref:o.ref,directory:t,local:o.local}:{ref:o.ref,directory:t}}catch{}if(t===n)return null;t=vn(t)}}function Ue(e,t){let n=io(e);return to(vn(n),{recursive:!0}),ro(n,`${JSON.stringify(t,null,2)}
4
+ `),n}function Pt(e){let t=e.environment??process.env;return e.flag?e.flag:t.SNOUTDATA_PROJECT?t.SNOUTDATA_PROJECT:Le(e.cwd??process.cwd())?.ref??null}function co(e){let t=e.environment??process.env,n=Le(e.cwd??process.cwd())?.ref??null;if(e.flag)return n===e.flag?{kind:"linked",ref:n}:{kind:"link",ref:e.flag,replaces:n};let r=t.SNOUTDATA_PROJECT;return r?n===r?{kind:"linked",ref:n}:{kind:"environment",ref:r}:n?{kind:"linked",ref:n}:{kind:"create"}}var ke=I(()=>{"use strict"});import{existsSync as $n,readFileSync as lo}from"node:fs";import{homedir as Oc}from"node:os";import{isAbsolute as Nc,join as ve,resolve as Fe}from"node:path";function Dc(e=process.env,t=process.platform,n=Oc()){let r;return t==="win32"?r=e.APPDATA??ve(n,"AppData","Roaming"):t==="darwin"?r=ve(n,"Library","Application Support"):r=e.XDG_CONFIG_HOME??ve(n,".config"),["snoutdata","snoutdata-dev"].map(o=>ve(r,o,"localProjects.json"))}function At(e=Dc()){let t=[];for(let n of e)if($n(n))try{let r=JSON.parse(lo(n,"utf8"));for(let o of r.projects??[])typeof o.folder=="string"&&o.folder&&!t.some(s=>s.folder===o.folder)&&t.push({name:o.name??null,folder:o.folder,ref:o.ref??null})}catch{}return t}function Tt(e){let t=new Map;for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim(),a=r.slice(o+1).trim();a.length>=2&&(a[0]==='"'||a[0]==="'")&&a.endsWith(a[0])&&(a=a.slice(1,-1)),t.set(s,a)}return t}function et(e){return $n(ve(e,"compose.yaml"))&&$n(ve(e,".env"))}function Ee(e,t=null){let n=Fe(e);if(!et(n))throw new Error(`${n} is not a SnoutData stack folder: it needs compose.yaml and .env (see github.com/snoutdata/snout-stack)`);let r=Tt(lo(ve(n,".env"),"utf8")),o=r.get("SNOUT_REF"),s=r.get("POSTGRES_OWNER_PASSWORD");if(!o||!s)throw new Error(`${ve(n,".env")} has no ${o?"POSTGRES_OWNER_PASSWORD":"SNOUT_REF"}, so this stack was not set up with \`snout-stack init\``);let a=Number(r.get("API_PORT")??8e3)||8e3,d=Number(r.get("DB_PORT")??5432)||5432;return{folder:n,ref:o,name:t,apiUrl:(r.get("API_EXTERNAL_URL")||`http://127.0.0.1:${a}`).replace(/\/$/,""),apiPort:a,dbPort:d,anonKey:r.get("ANON_KEY")||null,serviceRoleKey:r.get("SERVICE_ROLE_KEY")||null,ownerPassword:s}}function uo(e,t=process.cwd(),n=At()){if(!e){if(et(t))return Ee(t);let[i]=n;if(n.length===1&&i)return Ee(i.folder,i.name);throw n.length===0?new Error("there is no local project to link: set one up in Studio (Projects, Local), or pass the folder of a stack you made with snout-stack"):new Error(`there are ${n.length} local projects; name one: ${n.map(u=>u.name??u.folder).join(", ")}`)}let r=Nc(e)?e:Fe(t,e);if(et(r)){let i=n.find(u=>Fe(u.folder)===Fe(r));return Ee(r,i?.name??null)}let o=e.toLowerCase(),s=n.filter(i=>i.name?.toLowerCase()===o||i.ref===e),[a]=s;if(s.length===1&&a)return Ee(a.folder,a.name);let d=n.map(i=>i.name??i.folder);throw new Error(s.length>1?`more than one local project is called ${e}; link it by its folder`:`no local project called ${e}, and it is not a stack folder${d.length?`. The local projects are ${d.join(", ")}`:""}`)}function mo(e){let t=`${e.ref}_owner`;return{ref:e.ref,host:"127.0.0.1",port:e.dbPort,database:e.ref,user:t,password:e.ownerPassword,ssl:"disable",uri:`postgresql://${t}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.dbPort}/${e.ref}`,anonKey:e.anonKey,serviceRoleKey:e.serviceRoleKey,keysIssuedAt:null,keysExpireAt:null,wakesInstantly:!0,state:"local"}}function fo(e,t,n=At()){if(t&&et(t))try{let r=n.find(s=>Fe(s.folder)===Fe(t)),o=Ee(t,r?.name??null);if(o.ref===e)return o}catch{}for(let r of n)if(r.ref===e&&et(r.folder))return Ee(r.folder,r.name);return null}var tt=I(()=>{"use strict"});function N(e,t,n){throw new C(e,t,n)}function Rt(e,t){return t==="no-capacity"?"no-capacity":e===403?"forbidden":e===404?"not-found":e===409?"conflict":e===402||e===429?"quota":e===503?"not-ready":e===504?"timeout":e===410?"outdated":e>=500?"server":"failed"}function po(e){let t=e;return!t||typeof t!="object"||t.code!=="ENOENT"||typeof t.path!="string"||t.syscall==="spawn"?null:`no file at ${t.path}`}function go(e){let t=[e],n=e;for(let r=0;r<4&&n instanceof Error&&n.cause;r+=1)n=n.cause,t.push(n);for(let r of t){let o=typeof r=="object"&&r&&"code"in r?String(r.code):"";if(Ic.has(o))return"network";if(o==="ABORT_ERR"||o==="ETIMEDOUT")return"timeout"}return e instanceof Error&&/fetch failed|network|socket hang up/i.test(e.message)?"network":"failed"}var fe,C,Ic,te=I(()=>{"use strict";fe={ok:0,failed:1,usage:2,"not-signed-in":3,"not-ready":4,forbidden:5,"not-found":6,conflict:7,quota:8,network:9,timeout:10,outdated:11,"no-capacity":12,"tool-missing":127,server:1},C=class extends Error{code;details;constructor(t,n,r){super(n),this.name="CliFailure",this.code=t,this.details=r}get exitCode(){return fe[this.code]}};Ic=new Set(["ECONNREFUSED","ECONNRESET","ENOTFOUND","EAI_AGAIN","EHOSTUNREACH","ENETUNREACH","EPIPE","CERT_HAS_EXPIRED","UNABLE_TO_VERIFY_LEAF_SIGNATURE","DEPTH_ZERO_SELF_SIGNED_CERT"])});function Ot(e){let t=e.env.SNOUTDATA_NO_INTERACTIVE;if(t!==void 0&&t!==""&&t!=="0"&&t!=="false")return{canAsk:!1,reason:"opted-out"};if(e.json)return{canAsk:!1,reason:"json"};for(let n of Cc){let r=e.env[n];if(r!==void 0&&r!==""&&r!=="0"&&r!=="false")return{canAsk:!1,reason:"ci"}}return e.isTty?{canAsk:!0}:{canAsk:!1,reason:"not-a-tty"}}function Be(e){switch(e){case"json":return"running with --json, so nothing was asked";case"opted-out":return"SNOUTDATA_NO_INTERACTIVE is set, so nothing was asked";case"ci":return"this looks like CI, so nothing was asked";case"not-a-tty":return"there is no terminal to ask at"}}function ho(e){Pn=e}function $e(){return(Pn??Ot({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).canAsk}function He(){return(Pn??Ot({isTty:!!process.stdin.isTTY,json:!1,env:process.env})).reason}var Cc,Pn,Ge=I(()=>{"use strict";Cc=["CI","GITHUB_ACTIONS","GITLAB_CI","BUILDKITE","CIRCLECI","TF_BUILD"];Pn=null});import{execFile as Mc}from"node:child_process";import{existsSync as jc,readFileSync as bo}from"node:fs";import{homedir as An,hostname as Lc}from"node:os";import{join as qe}from"node:path";function Uc(e,t,n){if(t==="win32"){let r=n.APPDATA??qe(An(),"AppData","Roaming");return qe(r,e)}return t==="darwin"?qe(An(),"Library","Application Support",e):qe(n.XDG_CONFIG_HOME??qe(An(),".config"),e)}function So(e=process.platform,t=process.env){return t.SNOUTDATA_DESKTOP_CONFIG?[t.SNOUTDATA_DESKTOP_CONFIG]:["snoutdata","snoutdata-dev"].map(n=>qe(Uc(n,e,t),"mcp.json"))}function Fc(e=process.platform,t=process.env){return So(e,t)[0]}function Bc(e=Fc()){if(!jc(e))return null;try{let t=JSON.parse(bo(e,"utf8"));return typeof t.port!="number"||typeof t.token!="string"||!t.token?null:t}catch{return null}}function Hc(e){return e?e.enabled===!1?"server-disabled":e.signInHandoff===!1?"handoff-disabled":null:"no-config"}function qc(e,t){let n=`0100007F:${t.toString(16).toUpperCase().padStart(4,"0")}`,r=[];for(let o of e.split(`
5
+ `).slice(1)){let s=o.trim().split(/\s+/);s.length>7&&s[1]?.toUpperCase()===n&&s[3]==="0A"&&r.push(Number(s[7]))}return r}function Kc(e,t){return e.split(`
6
+ `).some(n=>n.trim()===`n127.0.0.1:${t}`)}function yo(e,t){return new Promise(n=>{Mc(e,t,{timeout:5e3,windowsHide:!0},(r,o)=>{n(r&&!o?"":String(o))})})}async function _o(e,t=process.platform){if(!Number.isInteger(e)||e<=0||e>65535)return!1;try{if(t==="linux"){let n=process.getuid?.();if(n===void 0)return!1;let r=qc(bo("/proc/net/tcp","utf8"),e);return r.length>0&&r.every(o=>o===n)}if(t==="darwin"){let n=process.getuid?.();if(n===void 0)return!1;let r=await yo("/usr/sbin/lsof",["-nP","-a",`-iTCP@127.0.0.1:${e}`,"-sTCP:LISTEN","-u",String(n),"-Fn"]);return Kc(r,e)}if(t==="win32"){let n=[`$c = Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort ${e} -State Listen -ErrorAction Stop | Select-Object -First 1`,'$p = Get-CimInstance Win32_Process -Filter "ProcessId=$($c.OwningProcess)" -ErrorAction Stop',"$o = Invoke-CimMethod -InputObject $p -MethodName GetOwner -ErrorAction Stop","$me = [Security.Principal.WindowsIdentity]::GetCurrent().Name",'if ($o.ReturnValue -eq 0 -and ("$($o.Domain)\\$($o.User)" -ieq $me)) { "mine" } else { "other" }'].join("; ");return(await yo("powershell.exe",["-NoProfile","-NonInteractive","-Command",n])).trim()==="mine"}}catch{return!1}return!1}async function xo(e,t){let n=Tn.get(e);if(n!==void 0&&Date.now()-n<Jc)return!0;let r=await t(e).catch(()=>!1);return r?Tn.set(e,Date.now()):Tn.delete(e),r}async function Nn(e,t,n,r=_o){if(!await xo(e.port,r))throw new Rn(e.port);return fetch(`http://127.0.0.1:${e.port}${t}`,{...n,headers:{...n.headers,Authorization:`Bearer ${e.token}`}})}async function Nt(e,t=_o){if(e!==void 0)return wo(e,t);let n="no-config";for(let r of So()){let o=Bc(r);if(!o)continue;let s=await wo(o,t);if(s.available)return s;(s.skip!=="not-running"&&s.skip!=="not-verified"||n==="no-config")&&(n=s.skip)}return{available:!1,skip:n}}async function wo(e,t){let n=Hc(e);if(n)return{available:!1,skip:n};let r=e;if(!await xo(r.port,t))return{available:!1,skip:"not-verified"};let o;try{let s=await Nn(r,"/cli/whoami",{signal:AbortSignal.timeout(Gc)},t);if(!s.ok)return{available:!1,skip:"not-running"};o=await s.json()}catch{return{available:!1,skip:"not-running"}}return o.signedIn?o.canMintToken?{available:!0,who:o,config:r}:{available:!1,skip:"cannot-mint"}:{available:!1,skip:"signed-out"}}async function ko(e,t){let n=await Nn(e,"/cli/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client:"snoutdata-cli",command:t}),signal:AbortSignal.timeout(6e4)}),r=await n.json().catch(()=>({}));return n.ok&&r.ok&&r.token?{token:r.token,name:r.name??"SnoutData CLI"}:{declined:!0,reason:r.error??`SnoutData Studio refused (${n.status})`}}function Dt(){return Lc()}async function On(e,t,n,r){let o=await Nn(e,"/mcp",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:t,params:n}),signal:AbortSignal.timeout(r)});if(!o.ok)throw new Error(`the app answered ${o.status}`);let s=await o.json();if(s.error)throw new Error(s.error.message??"SnoutData Studio refused");return s.result}async function vo(e){return await On(e,"initialize",{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"snoutdata-cli",version:"0"}},3e3),(await On(e,"tools/list",{},3e3)).tools??[]}async function Eo(e,t,n){return On(e,"tools/call",{name:t,arguments:n},12e4)}var Gc,Jc,Tn,Rn,It=I(()=>{"use strict";Gc=1500;Jc=1e4,Tn=new Map;Rn=class extends Error{constructor(t){super(`the process on 127.0.0.1:${t} is not one of this user's, so SnoutData Studio's token was not sent to it`),this.name="PeerNotVerified"}}});import{basename as Wc}from"node:path";function Mt(e={bun:typeof process.versions.bun=="string",execPath:process.execPath,script:process.argv[1]??""}){let t=Wc(e.execPath.replace(/\\/g,"/")).toLowerCase();if(e.bun&&t.startsWith("snoutdata"))return"binary";let n=e.script.replace(/\\/g,"/");return/\/_npx\//.test(n)?"npx":/\/node_modules\/snoutdata\//.test(n)?"npm":"source"}function zc(e=Mt(),t=process.platform){switch(e){case"binary":case"npm":return"snoutdata upgrade";case"npx":return"npx snoutdata@latest";case"source":return t==="win32"?pe:nt}}function Dn(e={}){let t=e.kind??Mt(),n=e.platform??process.platform,r=e.minimum?`${e.minimum} or later is required`:"a newer version is required",o=n==="win32"?pe:`${nt} (or ${pe})`;return`snoutdata ${X} is installed and ${r}. Run \`${zc(t,n)}\`, or install again: ${o}`}function Po(e,t){if(e==="dev"||t==="dev")return!1;let n=e.split("-")[0].split(".").map(Number),r=t.split("-")[0].split(".").map(Number);for(let o=0;o<Math.max(n.length,r.length);o+=1){let s=n[o]??0,a=r[o]??0;if(Number.isNaN(s)||Number.isNaN(a))return!1;if(s!==a)return s<a}return!1}var X,Ct,nt,pe,rt=I(()=>{"use strict";X="0.11.0",Ct=`snoutdata-cli/${X}`,nt="curl -fsSL https://snoutdata.com/install.sh | sh",pe="npm install -g snoutdata@latest"});async function Ao(e,t){let n=await fetch(`${de}/functions/v1/${e}`,{method:"POST",headers:{apikey:ge,"Content-Type":"application/json","x-client-info":Ct},body:JSON.stringify(t)});Ut(n);let r=await n.json().catch(()=>({}));if(n.status===410){let o=r.error;throw Ft(n,typeof o=="string"?o:"This version of the snoutdata CLI is no longer supported.")}return{status:n.status,body:r}}async function jt(e={}){let{status:t,body:n}=await Ao("cloud-device-start",{client:e});return t===429&&N("quota",n.error??"too many pairing attempts from here: wait a few minutes"),t===404&&N("not-found","this SnoutData does not have the pairing flow (cloud-device-start is not deployed). Use `snoutdata login` to sign in with a browser, or set SNOUTDATA_ACCESS_TOKEN."),(t!==200||!n.code||!n.secret)&&N("server",n.error??`could not start a pairing (${t})`),n}async function Lt(e,t){let n=t.intervalMs,r=Date.parse(t.expiresAt);for(;;){Date.now()>r&&N("timeout","that pairing code expired before it was approved. Run the command again for a new one."),await new Promise(a=>setTimeout(a,n));let{status:o,body:s}=await Ao("cloud-device-poll",{secret:e});switch(o===404&&N("not-found","that pairing is not known to the server. Run the command again for a new code."),o===400&&N("usage",s.error??"that is not a pairing secret"),o!==200&&N("server",s.error??`the pairing could not be checked (${o})`),s.status){case"approved":return{token:s.token,name:s.name};case"denied":N("forbidden",s.error??"that pairing was refused in the browser");case"used":N("conflict",s.error??"that pairing has already been completed");case"expired":N("timeout",s.error??"that pairing code expired before it was approved");case"slow-down":n=Math.max(n*2,(s.interval??2)*1e3);break;default:s.interval&&(n=Math.max(n,s.interval*1e3));break}t.onWait?.(Math.max(0,Math.round((r-Date.now())/1e3)))}}var In=I(()=>{"use strict";z();te();rt()});function st(e){ot=e}function Ro(e){To=e}function Oo(){return ot}function b(e){return No?`\x1B[2m${e}\x1B[0m`:e}function x(e){return No?`\x1B[1m${e}\x1B[0m`:e}function g(e,t){if(ot){process.stdout.write(`${JSON.stringify(e,null,2)}
7
+ `);return}t()}function c(e){!ot&&!To&&process.stderr.write(`${e}
8
+ `)}function Pe(e,t,n){process.stderr.write(`${t}
9
9
  `),ot&&process.stdout.write(`${JSON.stringify({ok:!1,code:e,error:t,...n},null,2)}
10
- `)}function K(e){process.stderr.write(`${e}
10
+ `)}function J(e){process.stderr.write(`${e}
11
11
  `)}function F(e){if(e.length===0)return"";let t=e[0].map((n,r)=>Math.max(...e.map(o=>(o[r]??"").length)));return e.map(n=>n.map((r,o)=>r.padEnd(t[o]??0)).join(" ").trimEnd()).join(`
12
- `)}function pe(e){if(!e)return"never";let t=Math.round((Date.now()-Date.parse(e))/1e3),n=t<0,r=Math.abs(t);if(r<60)return n?"in a moment":"just now";let o=[[60,"minute"],[60,"hour"],[24,"day"],[30,"month"]],s=r,a="second";for(let[i,u]of o){if(s<i)break;s=Math.round(s/i),a=u}let l=`${s} ${a}${s===1?"":"s"}`;return n?`in ${l}`:`${l} ago`}var ot,$o,Ro,q=D(()=>{"use strict";ot=!1,$o=!1;Ro=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as No,randomBytes as In}from"node:crypto";import{createServer as Wc}from"node:http";import{createInterface as Jc}from"node:readline/promises";import{spawn as zc}from"node:child_process";function Ft(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function To(e,t){let n=e==="ok"?"#e0b256":"#e0765a",r=e==="ok"?"Signed in":"Sign-in failed",o=e==="ok"?"You can close this tab and go back to your terminal.":ul(t??"Something went wrong.");return`<!doctype html><html><head><meta charset="utf-8"><title>SnoutData</title>
12
+ `)}function he(e){if(!e)return"never";let t=Math.round((Date.now()-Date.parse(e))/1e3),n=t<0,r=Math.abs(t);if(r<60)return n?"in a moment":"just now";let o=[[60,"minute"],[60,"hour"],[24,"day"],[30,"month"]],s=r,a="second";for(let[i,u]of o){if(s<i)break;s=Math.round(s/i),a=u}let d=`${s} ${a}${s===1?"":"s"}`;return n?`in ${d}`:`${d} ago`}var ot,To,No,K=I(()=>{"use strict";ot=!1,To=!1;No=process.stdout.isTTY&&!process.env.NO_COLOR});import{createHash as Co,randomBytes as Cn}from"node:crypto";import{createServer as Yc}from"node:http";import{createInterface as Xc}from"node:readline/promises";import{spawn as Zc}from"node:child_process";function Bt(e){return e.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Do(e,t){let n=e==="ok"?"#e0b256":"#e0765a",r=e==="ok"?"Signed in":"Sign-in failed",o=e==="ok"?"You can close this tab and go back to your terminal.":gl(t??"Something went wrong.");return`<!doctype html><html><head><meta charset="utf-8"><title>SnoutData</title>
13
13
  <meta name="viewport" content="width=device-width,initial-scale=1"></head>
14
14
  <body style="font-family:ui-sans-serif,system-ui,-apple-system,sans-serif;background:#050403;color:#e9e4dc;display:grid;place-items:center;min-height:100vh;margin:0">
15
15
  <div style="text-align:center;max-width:460px;padding:40px 24px">
@@ -21,32 +21,32 @@ var bc=Object.defineProperty;var D=(e,t)=>()=>(e&&(t=e(e=0)),t);var Vr=(e,t)=>{f
21
21
  <p style="color:#8f887e;margin:0;font-size:15px;line-height:1.5">${o}</p>
22
22
  ${e==="ok"?"":'<p style="color:#8f887e;margin:14px 0 0;font-size:14px">Run <code style="background:#1b1917;border:1px solid #262320;border-radius:6px;padding:2px 6px;font-size:13px">snoutdata login</code> to try again.</p>'}
23
23
  </div>
24
- </body></html>`}function Cn(){let e=Ft(In(32)),t=Ft(No("sha256").update(e).digest());return{verifier:e,challenge:t}}function Vc(e,t){return e==="darwin"?{command:"open",args:[t],verbatim:!1}:e==="win32"?{command:"cmd.exe",args:["/c","start",'""',`"${t}"`],verbatim:!0}:{command:"xdg-open",args:[t],verbatim:!1}}function Yc(e){let{command:t,args:n,verbatim:r}=Vc(process.platform,e),o=zc(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function Do(){return"1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com"}function Qc(){return"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"}function el(e){let t=e??Ft(In(32));return{raw:t,hashed:No("sha256").update(t).digest("hex")}}function tl(e){return`${Xc}?${new URLSearchParams({...e.loginHint?{login_hint:e.loginHint}:{},client_id:Do(),redirect_uri:e.redirectUri,response_type:"code",scope:"openid email profile",code_challenge:e.challenge,code_challenge_method:"S256",nonce:e.hashedNonce,state:e.state,prompt:"select_account"}).toString()}`}function nl(e,t){let n=new URL(e,"http://127.0.0.1");if(!n.searchParams.has("code")&&!n.searchParams.has("error"))return{kind:"ignore"};let r=n.searchParams.get("error_description")??n.searchParams.get("error");if(r)return{kind:"error",message:r};let o=n.searchParams.get("code");return o?t!==void 0&&n.searchParams.get("state")!==t?{kind:"error",message:"the browser came back with a state that did not match"}:{kind:"ok",code:o}:{kind:"error",message:"the browser came back without a code"}}async function Mn(e){return new Promise((t,n)=>{let r="",o=Wc((a,l)=>{let i=nl(a.url??"/",e.state);if(i.kind==="ignore"){l.writeHead(404).end();return}l.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),l.end(i.kind==="ok"?To("ok"):To("failed",i.message)),o.close(),i.kind==="ok"?t({code:i.code,redirectUri:r}):n(new Error(i.message))});o.listen(0,"127.0.0.1",()=>{let a=o.address();r=`http://127.0.0.1:${typeof a=="object"&&a?a.port:0}${e.callbackPath??""}`,Promise.resolve().then(()=>e.authorizeUrl(r)).then(i=>{process.stdout.write(`${i}
25
- `),e.noBrowser?c("Open that in a browser, on any machine, and sign in."):(c("Opening that in your browser. If nothing opens, paste it into one."),Yc(i))},i=>{o.close(),n(i)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function Io(e,t){let n=await fetch(`${ce}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:fe,"Content-Type":"application/json"},body:JSON.stringify({auth_code:e,code_verifier:t})});if(!n.ok)throw new Error(`could not exchange the code: ${(await n.text()).slice(0,200)}`);return Co(await n.json())}async function rl(e,t,n){let r=await fetch(Zc,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:Do(),client_secret:Qc(),code:e,code_verifier:t,grant_type:"authorization_code",redirect_uri:n})}),o=await r.json().catch(()=>({}));if(!r.ok||!o.id_token)throw new Error(`Google would not exchange the code: ${o.error_description??o.error??`HTTP ${r.status}`}`);return o.id_token}async function ol(e){let{verifier:t,challenge:n}=Cn(),{raw:r,hashed:o}=el(),s=Ft(In(16)),{code:a,redirectUri:l}=await Mn({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:d=>tl({redirectUri:d,challenge:n,hashedNonce:o,state:s,loginHint:e.email})}),i=await rl(a,t,l),u=await fetch(`${ce}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:fe,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:i,nonce:r})});if(!u.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await u.text()).slice(0,200)}`);return Co(await u.json())}function Co(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return ie(n),n}function Oo(e){let t=e.trim().toLowerCase();if(!t||/\s/.test(t))return null;let n=t.indexOf("@");if(n===0||t.indexOf("@",n+1)!==-1)return null;let r=n===-1?t:t.slice(n+1);if(!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/.test(r))return null;let o=r.slice(r.lastIndexOf(".")+1);return/^[a-z]{2,}$/.test(o)?r:null}function sl(e){try{let t=JSON.parse(e);for(let n of["error_description","msg","message","error"]){let r=t[n];if(typeof r=="string"&&r)return r}}catch{return""}return""}function al(e,t,n){let r=sl(t);return e===404||/no sso provider/i.test(r)?new N("not-found",`No single sign-on is set up for ${n}. Check the address, or ask whoever administers your team to connect your identity provider.`,{domain:n}):/saml.*(disabled|not enabled)/i.test(r)?new N("failed",`Single sign-on is not available for ${n} yet. Ask whoever administers your team to connect your identity provider.`,{domain:n}):new N(Rt(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function il(e){let t=await fetch(`${ce}/auth/v1/sso`,{method:"POST",headers:{apikey:fe,"Content-Type":"application/json"},body:JSON.stringify({domain:e.domain,redirect_to:e.redirectTo,skip_http_redirect:!0,code_challenge:e.challenge,code_challenge_method:"s256"})}),n=await t.text();if(!t.ok)throw al(t.status,n,e.domain);let r;try{r=JSON.parse(n).url}catch{r=void 0}if(typeof r!="string"||!r)throw new N("server",`Single sign-on for ${e.domain} was accepted but came back without somewhere to send the browser.`,{domain:e.domain});return r}async function cl(e){if(e!==void 0){let r=Oo(e);if(!r)throw new h(`--domain wants a work email or a company domain, like you@yourcompany.com, not "${e}"`);return r}if(!xe()){let r=je();throw new h(`login --sso needs --domain <your work email or company domain>${r?`, because ${Me(r)}`:""}`)}let t=Jc({input:process.stdin,output:process.stderr}),n=new Promise((r,o)=>{t.once("close",()=>o(new h("nothing left to read: pass --domain <your work email or company domain>")))});n.catch(()=>{});try{for(let r=0;r<3;r+=1){let o=await Promise.race([t.question("Your work email address: "),n]),s=Oo(o);if(s)return s;c("That is not a work email or a company domain. Try something like you@yourcompany.com.")}}finally{t.close()}throw new h("no domain to sign in with: pass --domain <your work email or company domain>")}async function ll(e){let t=await cl(e.domain),{verifier:n,challenge:r}=Cn();c(`Signing in with the identity provider for ${k(t)}.`);let{code:o}=await Mn({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>il({domain:t,redirectTo:s,challenge:r})});return Io(o,n)}async function Bt(e={}){return jn(e.email,await dl(e))}function jn(e,t){if(!e)return t;let n={...t,expectedEmail:e};return ie(n),t.email&&!Ln(t.email,e)&&K(`Signed in as ${t.email}, not ${e}. Run \`snoutdata login --email ${e}\` and pick that account.`),n}function Ln(e,t){return e.trim().toLowerCase()===t.trim().toLowerCase()}async function dl(e){if(e.sso)return ll({...e,domain:e.domain??e.email});let t=e.provider??"google";if(t==="google")return ol(e);let{verifier:n,challenge:r}=Cn(),{code:o}=await Mn({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>{let a=new URL(`${ce}/auth/v1/authorize`);return a.searchParams.set("provider",t),a.searchParams.set("redirect_to",s),a.searchParams.set("code_challenge",r),a.searchParams.set("code_challenge_method","s256"),a.searchParams.set("prompt","select_account"),a.toString()}});return Io(o,n)}function ul(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var Xc,Zc,Ht=D(()=>{"use strict";J();ne();be();ee();Le();q();Xc="https://accounts.google.com/o/oauth2/v2/auth",Zc="https://oauth2.googleapis.com/token"});import{createInterface as ml}from"node:readline/promises";async function Un(e){let t=ml({input:process.stdin,output:process.stderr});try{let n=(await t.question(`${e} [Y/n] `)).trim().toLowerCase();return n===""||n==="y"||n==="yes"}finally{t.close()}}async function fl(){let e=await Ot();if(!e.available)return(e.skip==="handoff-disabled"||e.skip==="server-disabled")&&c(b("SnoutData Studio is running but is not set to hand out sign-ins.")),null;let t=e.who.email??"the signed-in account";if(c(""),c(`SnoutData Studio is open here, signed in as ${k(t)}.`),c(b("It can create an access token for this terminal. Your sign-in itself is not handed over,")),c(b("and you can revoke the token later with `snoutdata tokens revoke`.")),!await Un("Use it?"))return null;c("Asking SnoutData Studio. Approve it there.");let n=await So(e.config,process.argv.slice(2).join(" ").slice(0,60)||"snoutdata");if("declined"in n)return c(`The app did not create a token: ${n.reason}`),null;let r={accessToken:n.token};return ie(r),c(`Signed in with "${n.name}".`),r}async function pl(){if(!await Un("Sign in with a browser?"))return null;try{let e=await Bt({});return c(`Signed in${e.email?` as ${e.email}`:""}.`),{accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt}}catch(e){return c(`That did not finish: ${e instanceof Error?e.message:String(e)}`),null}}async function gl(){if(!await Un("Sign in by typing a code into a browser on another machine?"))return null;let e=await Mt({name:Nt(),platform:process.platform});c(""),c(` Open ${k(e.verifyUrl)}`),c(` Type ${k(e.code)}`),c(""),c(b("The code is typed rather than in the link on purpose: it is what proves the person")),c(b("at the browser is the person at this terminal. Waiting\u2026"));let t=await jt(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return ie(n),c(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function Mo(){if(!xe())return null;let e=await fl();if(e)return e;let t=await pl();return t||gl()}var jo=D(()=>{"use strict";be();Dt();Dn();Le();Ht();q()});var ge={};Vr(ge,{ACCOUNTS_URL:()=>ce,ANON_KEY:()=>fe,ApiError:()=>le,FUNCTIONS:()=>Uo,NotSignedIn:()=>ke,call:()=>_,connection:()=>te,exportStatus:()=>Fe,isSettled:()=>Fo,listProjects:()=>re,listTeams:()=>Gt,localStack:()=>Ee,noticeFrom:()=>Lt,outdated:()=>Ut,requestExport:()=>at,waitForReady:()=>ct,waitForSettled:()=>it,whoami:()=>Re});function hl(){let e=je();return e?`not signed in, and ${Me(e)}. Set SNOUTDATA_ACCESS_TOKEN to a token made at https://dashboard.snoutdata.com/#/tokens, or by \`snoutdata tokens create\` on a machine that is signed in.`:"not signed in: run `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN"}async function yl(){let e=$t();if(e||(e=await Mo()),!e)throw new ke;if(!oo(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${ce}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:fe,"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refreshToken})});if(!t.ok)throw new ke;let n=await t.json(),r={...e,accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at};return ie(r),r.accessToken}async function _(e,t={},n="POST"){let r=typeof t=="object"&&t?t.ref:void 0;if(typeof r=="string"){let u=Ee(r);if(u)throw new N("usage",`${r} is a local project (${u.folder}), and this command is about SnoutData Cloud. Locally: db url, db psql, db push, gen types, keys, status, start, stop, functions, secrets.`,{ref:r,local:u.folder})}let o=await yl(),s=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",a=await fetch(`${Uo}/${e}${s}`,{method:n,headers:{Authorization:`Bearer ${o}`,apikey:fe,"Content-Type":"application/json","x-client-info":It},body:n==="GET"?void 0:JSON.stringify(t)});Lt(a);let l=await a.text(),i;try{i=l?JSON.parse(l):{}}catch{throw new le(a.status,l.slice(0,300)||a.statusText)}if(!a.ok){let u=typeof i=="object"&&i&&"error"in i?String(i.error):`${a.status} ${a.statusText}`;throw a.status===401?new ke(u):a.status===410?Ut(a,u):new le(a.status,u)}return i}function Lt(e){let t=e.headers.get("x-snoutdata-cli-notice");!t||Lo||(Lo=!0,K(`${t} ${Nn({minimum:e.headers.get("x-snoutdata-cli-minimum")})}`))}function Ut(e,t){let n=e.headers.get("x-snoutdata-cli-minimum");return new N("outdated",`${t.replace(/\s+$/,"")} ${Nn({minimum:n})}`,{status:410,installed:V,minimum:n??null})}async function Re(){return _("cloud-whoami")}async function re(){return _("cloud-project-list")}async function te(e){let t=Ee(e);return t?co(t):_("cloud-project-connection",{ref:e})}function Ee(e){return lo(e,Xe(process.cwd())?.local??null)}async function Gt(){return _("cloud-team-list")}async function at(e){return _("cloud-project-export",{ref:e})}async function Fe(e){return _("cloud-project-export",{ref:e},"GET")}async function it(e,t,n={}){let r=Date.now()+(n.timeoutMs??3e5);for(;;){let{projects:o}=await re(),s=o.find(l=>l.ref===e)??null,a=s?.state??"deleted";if(n.onTick?.(a),Fo(t,a))return s;if(Date.now()>=r)throw new le(504,`${e} is still ${a} after waiting`);await new Promise(l=>setTimeout(l,2e3))}}function Fo(e,t){let n={pause:"paused",resume:"ready",delete:"deleted"}[e];return t===n||t==="error"}async function ct(e,t={}){let n=Date.now()+(t.timeoutMs??3e5);for(;;){let{projects:r}=await re(),o=r.find(s=>s.ref===e);if(!o)throw new le(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new le(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var ce,fe,Uo,le,ke,Lo,J=D(()=>{"use strict";be();tt();ee();Le();jo();q();rt();ce=process.env.SNOUTDATA_ACCOUNTS_URL??"https://accounts.snoutdata.com",fe=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzbm91dGRhdGEiLCJyZWYiOiJ6ZjdnNjVtYjhmYXJhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3OTA4MDcwNDcsImV4cCI6MjEwNDg3NTUyNH0.M-Cb0wFEhJXU21cXtQxZAn-WnRcq4rK2uNSkeHdLucY",Uo=`${ce}/functions/v1`,le=class extends Error{status;constructor(t,n){super(n),this.status=t}},ke=class extends Error{constructor(t){super(t??hl())}};Lo=!1});function br(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function xa(e,t,n){let r="";for(;r.length<n;)r+=br(e,t);return r}var wr,_a,va=D(()=>{"use strict";wr="0123456789abcdefghjkmnpqrstvwxyz",_a="abcdefghjkmnpqrstvwxyz"});function $a(e){return br(e,ka)+xa(e,wr,Ea-1)}function Nu(e){return Ou.test(e)}function Z(e){if(!Nu(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function j(e,t=Du){return Z(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var ka,Ea,Ou,Du,ye=D(()=>{"use strict";va();ka=_a,Ea=13,Ou=new RegExp(`^[${ka}][${wr}]{${Ea-1}}$`);Du="db.snoutdata.com"});var Aa=D(()=>{"use strict";ye()});function Ra(e,t){let n=Pa(e,7),r=Pa(t,24);return Math.max(2,Math.ceil(n*24/r))}function Pa(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var Sr=D(()=>{"use strict"});function _r(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var xr=D(()=>{"use strict"});var Ta=D(()=>{"use strict"});var $h,Oa=D(()=>{"use strict";ye();$h=7*24});var Ph,Na=D(()=>{"use strict";Ph=7*24*60*60*1e3});var Iu,Da=D(()=>{"use strict";Na();Iu=60*60*1e3});var Ke,Ih,Ia=D(()=>{"use strict";Ke="snout_storage_admin",Ih=`do $$ begin
24
+ </body></html>`}function Mn(){let e=Bt(Cn(32)),t=Bt(Co("sha256").update(e).digest());return{verifier:e,challenge:t}}function Qc(e,t){return e==="darwin"?{command:"open",args:[t],verbatim:!1}:e==="win32"?{command:"cmd.exe",args:["/c","start",'""',`"${t}"`],verbatim:!0}:{command:"xdg-open",args:[t],verbatim:!1}}function el(e){let{command:t,args:n,verbatim:r}=Qc(process.platform,e),o=Zc(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}function jn(){return"1064562458607-9fqmghjoqjr7d56b98agkvjt9vt26pdc.apps.googleusercontent.com"}function rl(){return"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"}function ol(e){let t=e??Bt(Cn(32));return{raw:t,hashed:Co("sha256").update(t).digest("hex")}}function sl(e){return`${tl}?${new URLSearchParams({...e.loginHint?{login_hint:e.loginHint}:{},client_id:jn(),redirect_uri:e.redirectUri,response_type:"code",scope:"openid email profile",code_challenge:e.challenge,code_challenge_method:"S256",nonce:e.hashedNonce,state:e.state,prompt:"select_account"}).toString()}`}function al(e,t){let n=new URL(e,"http://127.0.0.1");if(!n.searchParams.has("code")&&!n.searchParams.has("error"))return{kind:"ignore"};let r=n.searchParams.get("error_description")??n.searchParams.get("error");if(r)return{kind:"error",message:r};let o=n.searchParams.get("code");return o?t!==void 0&&n.searchParams.get("state")!==t?{kind:"error",message:"the browser came back with a state that did not match"}:{kind:"ok",code:o}:{kind:"error",message:"the browser came back without a code"}}async function Ln(e){return new Promise((t,n)=>{let r="",o=Yc((a,d)=>{let i=al(a.url??"/",e.state);if(i.kind==="ignore"){d.writeHead(404).end();return}d.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),d.end(i.kind==="ok"?Do("ok"):Do("failed",i.message)),o.close(),i.kind==="ok"?t({code:i.code,redirectUri:r}):n(new Error(i.message))});o.listen(0,"127.0.0.1",()=>{let a=o.address();r=`http://127.0.0.1:${typeof a=="object"&&a?a.port:0}${e.callbackPath??""}`,Promise.resolve().then(()=>e.authorizeUrl(r)).then(i=>{process.stdout.write(`${i}
25
+ `),e.noBrowser?c("Open that in a browser, on any machine, and sign in."):(c("Opening that in your browser. If nothing opens, paste it into one."),el(i))},i=>{o.close(),n(i)})}),o.on("error",n),setTimeout(()=>{o.close(),n(new Error("timed out waiting for the browser"))},e.timeoutMs??3e5).unref()})}async function Mo(e,t){let n=await fetch(`${de}/auth/v1/token?grant_type=pkce`,{method:"POST",headers:{apikey:ge,"Content-Type":"application/json"},body:JSON.stringify({auth_code:e,code_verifier:t})});if(!n.ok)throw new Error(`could not exchange the code: ${(await n.text()).slice(0,200)}`);return jo(await n.json())}async function il(e,t,n){let r=await fetch(nl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:jn(),client_secret:rl(),code:e,code_verifier:t,grant_type:"authorization_code",redirect_uri:n})}),o=await r.json().catch(()=>({}));if(!r.ok||!o.id_token)throw new Error(`Google would not exchange the code: ${o.error_description??o.error??`HTTP ${r.status}`}`);return o.id_token}async function cl(e){if(!jn())throw new Error("this build of snoutdata has no Google sign-in (it was built for testing). Sign in with a released snoutdata, then run this one.");let{verifier:t,challenge:n}=Mn(),{raw:r,hashed:o}=ol(),s=Bt(Cn(16)),{code:a,redirectUri:d}=await Ln({state:s,timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:l=>sl({redirectUri:l,challenge:n,hashedNonce:o,state:s,loginHint:e.email})}),i=await il(a,t,d),u=await fetch(`${de}/auth/v1/token?grant_type=id_token`,{method:"POST",headers:{apikey:ge,"Content-Type":"application/json"},body:JSON.stringify({provider:"google",id_token:i,nonce:r})});if(!u.ok)throw new Error(`could not turn the Google sign-in into a session: ${(await u.text()).slice(0,200)}`);return jo(await u.json())}function jo(e){let t=e,n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:t.expires_at,email:t.user?.email};return le(n),n}function Io(e){let t=e.trim().toLowerCase();if(!t||/\s/.test(t))return null;let n=t.indexOf("@");if(n===0||t.indexOf("@",n+1)!==-1)return null;let r=n===-1?t:t.slice(n+1);if(!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/.test(r))return null;let o=r.slice(r.lastIndexOf(".")+1);return/^[a-z]{2,}$/.test(o)?r:null}function ll(e){try{let t=JSON.parse(e);for(let n of["error_description","msg","message","error"]){let r=t[n];if(typeof r=="string"&&r)return r}}catch{return""}return""}function dl(e,t,n){let r=ll(t);return e===404||/no sso provider/i.test(r)?new C("not-found",`No single sign-on is set up for ${n}. Check the address, or ask whoever administers your team to connect your identity provider.`,{domain:n}):/saml.*(disabled|not enabled)/i.test(r)?new C("failed",`Single sign-on is not available for ${n} yet. Ask whoever administers your team to connect your identity provider.`,{domain:n}):new C(Rt(e),r||`Could not start single sign-on for ${n}.`,{domain:n})}async function ul(e){let t=await fetch(`${de}/auth/v1/sso`,{method:"POST",headers:{apikey:ge,"Content-Type":"application/json"},body:JSON.stringify({domain:e.domain,redirect_to:e.redirectTo,skip_http_redirect:!0,code_challenge:e.challenge,code_challenge_method:"s256"})}),n=await t.text();if(!t.ok)throw dl(t.status,n,e.domain);let r;try{r=JSON.parse(n).url}catch{r=void 0}if(typeof r!="string"||!r)throw new C("server",`Single sign-on for ${e.domain} was accepted but came back without somewhere to send the browser.`,{domain:e.domain});return r}async function ml(e){if(e!==void 0){let r=Io(e);if(!r)throw new y(`--domain wants a work email or a company domain, like you@yourcompany.com, not "${e}"`);return r}if(!$e()){let r=He();throw new y(`login --sso needs --domain <your work email or company domain>${r?`, because ${Be(r)}`:""}`)}let t=Xc({input:process.stdin,output:process.stderr}),n=new Promise((r,o)=>{t.once("close",()=>o(new y("nothing left to read: pass --domain <your work email or company domain>")))});n.catch(()=>{});try{for(let r=0;r<3;r+=1){let o=await Promise.race([t.question("Your work email address: "),n]),s=Io(o);if(s)return s;c("That is not a work email or a company domain. Try something like you@yourcompany.com.")}}finally{t.close()}throw new y("no domain to sign in with: pass --domain <your work email or company domain>")}async function fl(e){let t=await ml(e.domain),{verifier:n,challenge:r}=Mn();c(`Signing in with the identity provider for ${x(t)}.`);let{code:o}=await Ln({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>ul({domain:t,redirectTo:s,challenge:r})});return Mo(o,n)}async function Ht(e={}){return Un(e.email,await pl(e))}function Un(e,t){if(!e)return t;let n={...t,expectedEmail:e};return le(n),t.email&&!Fn(t.email,e)&&J(`Signed in as ${t.email}, not ${e}. Run \`snoutdata login --email ${e}\` and pick that account.`),n}function Fn(e,t){return e.trim().toLowerCase()===t.trim().toLowerCase()}async function pl(e){if(e.sso)return fl({...e,domain:e.domain??e.email});let t=e.provider??"google";if(t==="google")return cl(e);let{verifier:n,challenge:r}=Mn(),{code:o}=await Ln({callbackPath:"/callback",timeoutMs:e.timeoutMs,noBrowser:e.noBrowser,authorizeUrl:s=>{let a=new URL(`${de}/auth/v1/authorize`);return a.searchParams.set("provider",t),a.searchParams.set("redirect_to",s),a.searchParams.set("code_challenge",r),a.searchParams.set("code_challenge_method","s256"),a.searchParams.set("prompt","select_account"),a.toString()}});return Mo(o,n)}function gl(e){return e.replace(/[&<>"]/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[t])}var tl,nl,Gt=I(()=>{"use strict";z();re();ke();te();Ge();K();tl="https://accounts.google.com/o/oauth2/v2/auth",nl="https://oauth2.googleapis.com/token"});import{createInterface as hl}from"node:readline/promises";async function Bn(e){let t=hl({input:process.stdin,output:process.stderr});try{let n=(await t.question(`${e} [Y/n] `)).trim().toLowerCase();return n===""||n==="y"||n==="yes"}finally{t.close()}}async function yl(){let e=await Nt();if(!e.available)return(e.skip==="handoff-disabled"||e.skip==="server-disabled")&&c(b("SnoutData Studio is running but is not set to hand out sign-ins.")),null;let t=e.who.email??"the signed-in account";if(c(""),c(`SnoutData Studio is open here, signed in as ${x(t)}.`),c(b("It can create an access token for this terminal. Your sign-in itself is not handed over,")),c(b("and you can revoke the token later with `snoutdata tokens revoke`.")),!await Bn("Use it?"))return null;c("Asking SnoutData Studio. Approve it there.");let n=await ko(e.config,process.argv.slice(2).join(" ").slice(0,60)||"snoutdata");if("declined"in n)return c(`The app did not create a token: ${n.reason}`),null;let r={accessToken:n.token};return le(r),c(`Signed in with "${n.name}".`),r}async function wl(){if(!await Bn("Sign in with a browser?"))return null;try{let e=await Ht({});return c(`Signed in${e.email?` as ${e.email}`:""}.`),{accessToken:e.accessToken,refreshToken:e.refreshToken,expiresAt:e.expiresAt}}catch(e){return c(`That did not finish: ${e instanceof Error?e.message:String(e)}`),null}}async function bl(){if(!await Bn("Sign in by typing a code into a browser on another machine?"))return null;let e=await jt({name:Dt(),platform:process.platform});c(""),c(` Open ${x(e.verifyUrl)}`),c(` Type ${x(e.code)}`),c(""),c(b("The code is typed rather than in the link on purpose: it is what proves the person")),c(b("at the browser is the person at this terminal. Waiting\u2026"));let t=await Lt(e.secret,{intervalMs:Math.max(1,e.interval)*1e3,expiresAt:e.expiresAt}),n={accessToken:t.token};return le(n),c(`Signed in${t.name?` with "${t.name}"`:""}.`),n}async function Lo(){if(!$e())return null;let e=await yl();if(e)return e;let t=await wl();return t||bl()}var Uo=I(()=>{"use strict";ke();It();In();Ge();Gt();K()});var we={};Xr(we,{ACCOUNTS_URL:()=>de,ANON_KEY:()=>ge,ApiError:()=>ue,FUNCTIONS:()=>Bo,NotSignedIn:()=>Ae,call:()=>S,connection:()=>ne,exportStatus:()=>Ke,isSettled:()=>Ho,listProjects:()=>oe,listTeams:()=>qt,localStack:()=>ye,noticeFrom:()=>Ut,outdated:()=>Ft,requestExport:()=>at,waitForReady:()=>ct,waitForSettled:()=>it,whoami:()=>Ne});function Sl(){let e=He();return e?`not signed in, and ${Be(e)}. Set SNOUTDATA_ACCESS_TOKEN to a token made at https://dashboard.snoutdata.com/#/tokens, or by \`snoutdata tokens create\` on a machine that is signed in.`:"not signed in: run `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN"}async function _l(){let e=$t();if(e||(e=await Lo()),!e)throw new Ae;if(!ao(e)||!e.refreshToken)return e.accessToken;let t=await fetch(`${de}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:ge,"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refreshToken})});if(!t.ok)throw new Ae;let n=await t.json(),r={...e,accessToken:n.access_token,refreshToken:n.refresh_token,expiresAt:n.expires_at};return le(r),r.accessToken}async function S(e,t={},n="POST"){let r=typeof t=="object"&&t?t.ref:void 0;if(typeof r=="string"){let u=ye(r);if(u)throw new C("usage",`${r} is a local project (${u.folder}), and this command is about SnoutData Cloud. Locally: db url, db psql, db push, gen types, keys, status, start, stop, functions, secrets.`,{ref:r,local:u.folder})}let o=await _l(),s=n==="GET"&&typeof t=="object"&&t?`?${new URLSearchParams(t).toString()}`:"",a=await fetch(`${Bo}/${e}${s}`,{method:n,headers:{Authorization:`Bearer ${o}`,apikey:ge,"Content-Type":"application/json","x-client-info":Ct},body:n==="GET"?void 0:JSON.stringify(t)});Ut(a);let d=await a.text(),i;try{i=d?JSON.parse(d):{}}catch{throw new ue(a.status,d.slice(0,300)||a.statusText)}if(!a.ok){let u=typeof i=="object"&&i&&"error"in i?String(i.error):`${a.status} ${a.statusText}`;if(a.status===401)throw new Ae(u);if(a.status===410)throw Ft(a,u);let l=typeof i=="object"&&i&&"code"in i&&typeof i.code=="string"?i.code:null;throw new ue(a.status,u,l)}return i}function Ut(e){let t=e.headers.get("x-snoutdata-cli-notice");!t||Fo||(Fo=!0,J(`${t} ${Dn({minimum:e.headers.get("x-snoutdata-cli-minimum")})}`))}function Ft(e,t){let n=e.headers.get("x-snoutdata-cli-minimum");return new C("outdated",`${t.replace(/\s+$/,"")} ${Dn({minimum:n})}`,{status:410,installed:X,minimum:n??null})}async function Ne(){return S("cloud-whoami")}async function oe(){return S("cloud-project-list")}async function ne(e){let t=ye(e);return t?mo(t):S("cloud-project-connection",{ref:e})}function ye(e){return fo(e,Le(process.cwd())?.local??null)}async function qt(){return S("cloud-team-list")}async function at(e){return S("cloud-project-export",{ref:e})}async function Ke(e){return S("cloud-project-export",{ref:e},"GET")}async function it(e,t,n={}){let r=Date.now()+(n.timeoutMs??3e5);for(;;){let{projects:o}=await oe(),s=o.find(d=>d.ref===e)??null,a=s?.state??"deleted";if(n.onTick?.(a),Ho(t,a))return s;if(Date.now()>=r)throw new ue(504,`${e} is still ${a} after waiting`);await new Promise(d=>setTimeout(d,2e3))}}function Ho(e,t){let n={pause:"paused",resume:"ready",delete:"deleted"}[e];return t===n||t==="error"}async function ct(e,t={}){let n=Date.now()+(t.timeoutMs??3e5);for(;;){let{projects:r}=await oe(),o=r.find(s=>s.ref===e);if(!o)throw new ue(404,`no project ${e}`);if(t.onTick?.(o.state),o.state==="ready")return o;if(Date.now()>=n)throw new ue(504,`${e} is still ${o.state} after waiting`);await new Promise(s=>setTimeout(s,2e3))}}var de,ge,Bo,ue,Ae,Fo,z=I(()=>{"use strict";ke();tt();te();Ge();Uo();K();rt();de=process.env.SNOUTDATA_ACCOUNTS_URL??"https://accounts.snoutdata.com",ge=process.env.SNOUTDATA_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzbm91dGRhdGEiLCJyZWYiOiJ6ZjdnNjVtYjhmYXJhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3OTA4MDcwNDcsImV4cCI6MjEwNDg3NTUyNH0.M-Cb0wFEhJXU21cXtQxZAn-WnRcq4rK2uNSkeHdLucY",Bo=`${de}/functions/v1`,ue=class extends Error{status;serverCode;constructor(t,n,r=null){super(n),this.status=t,this.serverCode=r}},Ae=class extends Error{constructor(t){super(t??Sl())}};Fo=!1});function _r(e,t){let n=256-256%t.length;for(;;){let r=e(1)[0];if(r<n)return t[r%t.length]}}function va(e,t,n){let r="";for(;r.length<n;)r+=_r(e,t);return r}var Sr,ka,Ea=I(()=>{"use strict";Sr="0123456789abcdefghjkmnpqrstvwxyz",ka="abcdefghjkmnpqrstvwxyz"});function Aa(e){return _r(e,$a)+va(e,Sr,Pa-1)}function ju(e){return Mu.test(e)}function Q(e){if(!ju(e))throw new Error(`not a project ref: ${JSON.stringify(e)}`);return e}function L(e,t=Lu){return Q(e),{ref:e,database:e,ownerRole:`${e}_owner`,pod:`snoutpod-${e}`,host:`${e}.${t}`}}var $a,Pa,Mu,Lu,Se=I(()=>{"use strict";Ea();$a=ka,Pa=13,Mu=new RegExp(`^[${$a}][${Sr}]{${Pa-1}}$`);Lu="db.snoutdata.com"});var Ta=I(()=>{"use strict";Se()});function Oa(e,t){let n=Ra(e,7),r=Ra(t,24);return Math.max(2,Math.ceil(n*24/r))}function Ra(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:t}var xr=I(()=>{"use strict"});function kr(e){let t=e.trim();if(!/^\d+$/.test(t))return null;let n=Number(t);return Number.isSafeInteger(n)?n:null}var vr=I(()=>{"use strict"});var Na=I(()=>{"use strict"});var Dh,Da=I(()=>{"use strict";Se();Dh=7*24});var Ch,Ia=I(()=>{"use strict";Ch=7*24*60*60*1e3});var Uu,Ca=I(()=>{"use strict";Ia();Uu=60*60*1e3});var Ye,Bh,Ma=I(()=>{"use strict";Ye="snout_storage_admin",Bh=`do $$ begin
26
26
  if exists (select from pg_roles where rolname = 'supabase_storage_admin') then
27
- if exists (select from pg_roles where rolname = '${Ke}') then
28
- perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${Ke}';
29
- reassign owned by ${Ke} to supabase_storage_admin;
30
- drop owned by ${Ke};
31
- drop role ${Ke};
27
+ if exists (select from pg_roles where rolname = '${Ye}') then
28
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${Ye}';
29
+ reassign owned by ${Ye} to supabase_storage_admin;
30
+ drop owned by ${Ye};
31
+ drop role ${Ye};
32
32
  end if;
33
- alter role supabase_storage_admin rename to ${Ke};
33
+ alter role supabase_storage_admin rename to ${Ye};
34
34
  end if;
35
- end $$;`});var $e,Mh,an,Ca,jh,Ma=D(()=>{"use strict";$e="snout_realtime_admin",Mh=`do $snoutpod$
35
+ end $$;`});var Te,Gh,cn,ja,qh,La=I(()=>{"use strict";Te="snout_realtime_admin",Gh=`do $snoutpod$
36
36
  begin
37
37
  if exists (select from pg_roles where rolname = 'supabase_admin') then
38
- if exists (select from pg_roles where rolname = '${$e}') then
39
- perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${$e}';
40
- reassign owned by ${$e} to supabase_admin;
41
- drop owned by ${$e};
42
- drop role ${$e};
38
+ if exists (select from pg_roles where rolname = '${Te}') then
39
+ perform pg_terminate_backend(pid) from pg_stat_activity where usename = '${Te}';
40
+ reassign owned by ${Te} to supabase_admin;
41
+ drop owned by ${Te};
42
+ drop role ${Te};
43
43
  end if;
44
- alter role supabase_admin rename to ${$e};
44
+ alter role supabase_admin rename to ${Te};
45
45
  end if;
46
46
  if exists (select from pg_roles where rolname = 'supabase_realtime_admin')
47
- and exists (select from pg_roles where rolname = '${$e}') then
47
+ and exists (select from pg_roles where rolname = '${Te}') then
48
48
  begin
49
- reassign owned by supabase_realtime_admin to ${$e};
49
+ reassign owned by supabase_realtime_admin to ${Te};
50
50
  drop owned by supabase_realtime_admin;
51
51
  drop role supabase_realtime_admin;
52
52
  exception when others then
@@ -55,26 +55,26 @@ begin
55
55
  end if;
56
56
  end
57
57
  $snoutpod$;
58
- `,an="snoutdata_realtime",Ca="supabase_realtime",jh=`do $snoutpod$
58
+ `,cn="snoutdata_realtime",ja="supabase_realtime",qh=`do $snoutpod$
59
59
  begin
60
- if exists (select 1 from pg_publication where pubname = '${an}') then
60
+ if exists (select 1 from pg_publication where pubname = '${cn}') then
61
61
  return;
62
62
  end if;
63
- if exists (select 1 from pg_publication where pubname = '${Ca}') then
64
- alter publication ${Ca} rename to ${an};
63
+ if exists (select 1 from pg_publication where pubname = '${ja}') then
64
+ alter publication ${ja} rename to ${cn};
65
65
  return;
66
66
  end if;
67
- create publication ${an};
68
- execute format('alter publication ${an} owner to %I',
67
+ create publication ${cn};
68
+ execute format('alter publication ${cn} owner to %I',
69
69
  (select pg_get_userbyid(datdba) from pg_database where datname = current_database()));
70
70
  end
71
71
  $snoutpod$;
72
- `});var Uh,Fh,ja=D(()=>{"use strict";Uh=2*1024*1024,Fh=64*1024});var La,Ua,Cu,Mu,ju,Fa=D(()=>{"use strict";La="/snoutfn",Ua="/_snoutpod/health",Cu="x-snoutdata-door",Mu="SNOUT_FUNCTIONS_DOOR_SECRET",ju=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
72
+ `});var Jh,Wh,Ua=I(()=>{"use strict";Jh=2*1024*1024,Wh=64*1024});var Fa,Ba,Fu,Bu,Hu,Ha=I(()=>{"use strict";Fa="/snoutfn",Ba="/_snoutpod/health",Fu="x-snoutdata-door",Bu="SNOUT_FUNCTIONS_DOOR_SECRET",Hu=`// Written by the SnoutData host agent. Do not edit on the host: it is rewritten
73
73
  // every time the agent starts, from packages/snoutpod/src/host/functionMain.ts.
74
- const MOUNT = ${JSON.stringify(La)};
75
- const HEALTH = ${JSON.stringify(Ua)};
76
- const DOOR_HEADER = ${JSON.stringify(Cu)};
77
- const DOOR = new TextEncoder().encode(Deno.env.get(${JSON.stringify(Mu)}) ?? '');
74
+ const MOUNT = ${JSON.stringify(Fa)};
75
+ const HEALTH = ${JSON.stringify(Ba)};
76
+ const DOOR_HEADER = ${JSON.stringify(Fu)};
77
+ const DOOR = new TextEncoder().encode(Deno.env.get(${JSON.stringify(Bu)}) ?? '');
78
78
  if (DOOR.length === 0) {
79
79
  console.error('WARNING: no door secret is set, so a request is not asked to prove it came through the front door');
80
80
  }
@@ -235,13 +235,13 @@ Deno.serve(async (req) => {
235
235
  return refuse(500, 'This function could not be run.', detail);
236
236
  }
237
237
  });
238
- `});function vr(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function cn(e){let t=["network","create"],n=e.allow,r=e.deny;if(n!=null&&r!=null)throw new Error("a network is policed by an allow-list or by a deny-list, not by both");if(r!=null)return Uu(e,r);if(n==null){if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no egress policy, which does nothing");return t.push(e.name),t}if(n.length>0)throw new Error("an egress allow-list is not expressible on this runtime: --internal blocks everything or nothing, and a route-based block does not block (measured on podman 5.8.6 / netavark 1.17.2)");if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no allowance to route through it");return t.push("--internal",e.name),t}function Uu(e,t){if(t.length===0)throw new Error("an empty deny-list is not a policy: leave it absent to create an ordinary network");let n=e.subnet,r=e.sink;if(!n||!r)throw new Error("a deny-list needs a pinned subnet and a sink address on it");if(vr("the subnet",n,Ba),vr("the sink",r,Lu),!Ga(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!Ga(e.gateway,n))throw new Error(`the gateway ${e.gateway} is not inside ${n}`);if(e.gateway&&e.gateway===r)throw new Error("the sink is the gateway, which routes denied traffic at the host rather than nowhere");let o=["network","create","--subnet",n];e.gateway&&o.push("--gateway",e.gateway);for(let s of t)o.push("--route",`${vr("a denied prefix",s,Ba)},${r}`);return o.push(e.name),o}function Ha(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function Ga(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return(Ha(e)&s)>>>0===(Ha(n??"")&s)>>>0}function qa(e){return e?.internal===!0}var Ba,Lu,kr=D(()=>{"use strict";Ba=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\/(3[0-2]|[12]?[0-9])$/,Lu=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/});var Fu,ln=D(()=>{"use strict";Fu=7*24*60*60});var Ka=D(()=>{"use strict";ye();ja();Fa();kr();ln()});function Ja(e){let t=[];for(let n of e.split(`
239
- `)){let r=n.trim();if(!r)continue;let[o,s,a,l]=r.split("|");!o||s===void 0||t.push({name:o,active:s==="t",walStatus:a??"",inactiveSeconds:l?Number(l):null})}return t}var Wa,Er=D(()=>{"use strict";Wa="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var cy,ly,za=D(()=>{"use strict";ye();ln();cy=6*60*60,ly=5*60});var Va=D(()=>{"use strict"});function Xa(e){if(!Array.isArray(e))throw new Error("the database access list is not a list");let t=new Set;return e.map(n=>{let r=n,o=r?.role,s=r?.level;if(typeof o!="string"||!Gu.test(o))throw new Error(`not a database access role: ${JSON.stringify(o)}`);if(typeof s!="string"||!Hu.includes(s))throw new Error(`not a database access level: ${JSON.stringify(s)}`);if(t.has(o))throw new Error(`${o} is on the database access list twice`);return t.add(o),{role:o,level:s}})}function Za(e,t){return`${e}.nspname !~ '^pg_'
240
- and ${e}.nspname <> all (array[${Ya.map(n=>`'${n}'`).join(", ")}]::text[])
238
+ `});function Er(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can route: ${JSON.stringify(t)}`);return t}function ln(e){let t=["network","create"],n=e.allow,r=e.deny;if(n!=null&&r!=null)throw new Error("a network is policed by an allow-list or by a deny-list, not by both");if(r!=null)return qu(e,r);if(n==null){if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no egress policy, which does nothing");return t.push(e.name),t}if(n.length>0)throw new Error("an egress allow-list is not expressible on this runtime: --internal blocks everything or nothing, and a route-based block does not block (measured on podman 5.8.6 / netavark 1.17.2)");if(e.subnet||e.gateway)throw new Error("a subnet or gateway was given with no allowance to route through it");return t.push("--internal",e.name),t}function qu(e,t){if(t.length===0)throw new Error("an empty deny-list is not a policy: leave it absent to create an ordinary network");let n=e.subnet,r=e.sink;if(!n||!r)throw new Error("a deny-list needs a pinned subnet and a sink address on it");if(Er("the subnet",n,Ga),Er("the sink",r,Gu),!Ka(r,n))throw new Error(`the sink ${r} is not inside ${n}, so denied traffic would be routed at the host`);if(e.gateway&&!Ka(e.gateway,n))throw new Error(`the gateway ${e.gateway} is not inside ${n}`);if(e.gateway&&e.gateway===r)throw new Error("the sink is the gateway, which routes denied traffic at the host rather than nowhere");let o=["network","create","--subnet",n];e.gateway&&o.push("--gateway",e.gateway);for(let s of t)o.push("--route",`${Er("a denied prefix",s,Ga)},${r}`);return o.push(e.name),o}function qa(e){return e.split(".").reduce((t,n)=>t*256+Number(n),0)}function Ka(e,t){let[n,r]=t.split("/"),o=Number(r),s=o===0?0:4294967295<<32-o>>>0;return(qa(e)&s)>>>0===(qa(n??"")&s)>>>0}function Ja(e){return e?.internal===!0}var Ga,Gu,$r=I(()=>{"use strict";Ga=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\/(3[0-2]|[12]?[0-9])$/,Gu=/^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/});var Ku,dn=I(()=>{"use strict";Ku=7*24*60*60});var Wa=I(()=>{"use strict";Se();Ua();Ha();$r();dn()});function Va(e){let t=[];for(let n of e.split(`
239
+ `)){let r=n.trim();if(!r)continue;let[o,s,a,d]=r.split("|");!o||s===void 0||t.push({name:o,active:s==="t",walStatus:a??"",inactiveSeconds:d?Number(d):null})}return t}var za,Pr=I(()=>{"use strict";za="select slot_name, active, wal_status, coalesce(extract(epoch from (now() - inactive_since))::bigint::text, '') from pg_replication_slots"});var gy,hy,Ya=I(()=>{"use strict";Se();dn();gy=6*60*60,hy=5*60});var Xa=I(()=>{"use strict"});var Za=I(()=>{"use strict"});var Qa=I(()=>{"use strict";Za()});function ti(e){if(!Array.isArray(e))throw new Error("the database access list is not a list");let t=new Set;return e.map(n=>{let r=n,o=r?.role,s=r?.level;if(typeof o!="string"||!zu.test(o))throw new Error(`not a database access role: ${JSON.stringify(o)}`);if(typeof s!="string"||!Wu.includes(s))throw new Error(`not a database access level: ${JSON.stringify(s)}`);if(t.has(o))throw new Error(`${o} is on the database access list twice`);return t.add(o),{role:o,level:s}})}function ni(e,t){return`${e}.nspname !~ '^pg_'
240
+ and ${e}.nspname <> all (array[${ei.map(n=>`'${n}'`).join(", ")}]::text[])
241
241
  and not ${t}.rolsuper
242
- and ${t}.rolname !~ '${qu.source}'`}function ei(e){let t=[];for(let n of e.split(`
243
- `)){let r=/^(created-group|created|applied|dropped-group|dropped|refused|ignored) (\S+)$/.exec(n.trim());r&&t.push({action:r[1],role:r[2]})}return t}function ti(e,t){return Z(e),JSON.stringify(Xa(t).map(n=>({role:n.role,level:n.level})))}var Hu,We,Gu,U,Ya,qu,un,Ku,dn,Wu,Qa,$r=D(()=>{"use strict";ye();Hu=["full","read"],We="snout_oauth",Gu=/^oauth_[0-9a-f]{32}$/,U="snout_oauth_read",Ya=["information_schema","auth","storage","realtime","snout_realtime","_realtime","push","graphql","graphql_public","extensions","cron","net","vault","pgsodium","pgbouncer","snouttime","snouttime_internal","supabase_functions"],qu=/^(snout_[a-z]+_admin|supabase_[a-z_]+|authenticator|pgbouncer)$/;un="SNOUTPOD_DB_ACCESS",Ku=/^[a-z_][a-z0-9_]*$/;for(let e of Ya)if(!Ku.test(e))throw new Error(`not a schema name: ${JSON.stringify(e)}`);dn="snoutpod_oauth_read_new_schema",Wu=`create schema if not exists extensions;
244
- create or replace function extensions.${dn}() returns event_trigger
242
+ and ${t}.rolname !~ '${Vu.source}'`}function oi(e){let t=[];for(let n of e.split(`
243
+ `)){let r=/^(created-group|created|applied|dropped-group|dropped|refused|ignored) (\S+)$/.exec(n.trim());r&&t.push({action:r[1],role:r[2]})}return t}function si(e,t){return Q(e),JSON.stringify(ti(t).map(n=>({role:n.role,level:n.level})))}var Wu,Xe,zu,U,ei,Vu,mn,Yu,un,Xu,ri,Ar=I(()=>{"use strict";Se();Wu=["full","read"],Xe="snout_oauth",zu=/^oauth_[0-9a-f]{32}$/,U="snout_oauth_read",ei=["information_schema","auth","storage","realtime","snout_realtime","_realtime","push","graphql","graphql_public","extensions","cron","net","vault","pgsodium","pgbouncer","snouttime","snouttime_internal","supabase_functions"],Vu=/^(snout_[a-z]+_admin|supabase_[a-z_]+|authenticator|pgbouncer)$/;mn="SNOUTPOD_DB_ACCESS",Yu=/^[a-z_][a-z0-9_]*$/;for(let e of ei)if(!Yu.test(e))throw new Error(`not a schema name: ${JSON.stringify(e)}`);un="snoutpod_oauth_read_new_schema",Xu=`create schema if not exists extensions;
244
+ create or replace function extensions.${un}() returns event_trigger
245
245
  language plpgsql
246
246
  security invoker
247
247
  set search_path = pg_catalog, pg_temp
@@ -261,7 +261,7 @@ begin
261
261
  where c.command_tag = 'CREATE SCHEMA'
262
262
  and c.classid = 'pg_catalog.pg_namespace'::pg_catalog.regclass
263
263
  and not c.in_extension
264
- and ${Za("n","r")}
264
+ and ${ni("n","r")}
265
265
  loop
266
266
  begin
267
267
  execute pg_catalog.format('grant usage on schema %I to ${U}', created.nspname);
@@ -277,15 +277,15 @@ end
277
277
  $snoutpod$;
278
278
  do $snoutpod$
279
279
  begin
280
- if not exists (select from pg_catalog.pg_event_trigger where evtname = '${dn}') then
281
- create event trigger ${dn} on ddl_command_end
280
+ if not exists (select from pg_catalog.pg_event_trigger where evtname = '${un}') then
281
+ create event trigger ${un} on ddl_command_end
282
282
  when tag in ('CREATE SCHEMA')
283
- execute function extensions.${dn}();
283
+ execute function extensions.${un}();
284
284
  end if;
285
285
  end
286
286
  $snoutpod$;
287
- `,Qa=`\\getenv db_access ${un}
288
- ${Wu}create function pg_temp.snoutpod_member(member_name text, group_name text)
287
+ `,ri=`\\getenv db_access ${mn}
288
+ ${Xu}create function pg_temp.snoutpod_member(member_name text, group_name text)
289
289
  returns boolean
290
290
  language sql
291
291
  stable
@@ -306,7 +306,7 @@ create function pg_temp.snoutpod_customer_schema(schema_oid oid)
306
306
  set search_path = pg_catalog, pg_temp
307
307
  as $snoutpod$
308
308
  -- isCustomerSchema in dbAccess.ts, from the same SYSTEM_SCHEMAS and PLATFORM_OWNER.
309
- select ${Za("n","r")}
309
+ select ${ni("n","r")}
310
310
  from pg_namespace n
311
311
  join pg_roles r on r.oid = n.nspowner
312
312
  where n.oid = schema_oid
@@ -341,9 +341,9 @@ begin
341
341
  end if;
342
342
  any_read := exists (select from jsonb_array_elements(wanted) w where w->>'level' = 'read');
343
343
 
344
- if not exists (select from pg_roles where rolname = '${We}') then
345
- create role ${We} nologin;
346
- change := 'created-group'; role_name := '${We}'; return next;
344
+ if not exists (select from pg_roles where rolname = '${Xe}') then
345
+ create role ${Xe} nologin;
346
+ change := 'created-group'; role_name := '${Xe}'; return next;
347
347
  end if;
348
348
 
349
349
  -- Taken away: every member of the group the list does not name.
@@ -352,7 +352,7 @@ begin
352
352
  from pg_auth_members am
353
353
  join pg_roles g on g.oid = am.roleid
354
354
  join pg_roles m on m.oid = am.member
355
- where g.rolname = '${We}'
355
+ where g.rolname = '${Xe}'
356
356
  order by 1
357
357
  loop
358
358
  continue when exists (select from jsonb_array_elements(wanted) w where w->>'role' = found_role);
@@ -424,7 +424,7 @@ begin
424
424
  wanted_role := entry->>'role';
425
425
  wanted_level := entry->>'level';
426
426
  if exists (select from pg_roles where rolname = wanted_role) then
427
- if not pg_temp.snoutpod_member(wanted_role, '${We}') then
427
+ if not pg_temp.snoutpod_member(wanted_role, '${Xe}') then
428
428
  -- The name is right and the role is not ours: made by somebody else, who holds
429
429
  -- ADMIN on it. Never adopted (see dbAccess.ts).
430
430
  change := 'refused'; role_name := wanted_role; return next;
@@ -432,7 +432,7 @@ begin
432
432
  end if;
433
433
  change := 'applied';
434
434
  else
435
- execute pg_catalog.format('create role %I login in role ${We}', wanted_role);
435
+ execute pg_catalog.format('create role %I login in role ${Xe}', wanted_role);
436
436
  change := 'created';
437
437
  end if;
438
438
  -- No password, ever: the pg_hba line for the group is oauth, and a password could not be
@@ -495,8 +495,8 @@ begin
495
495
  end
496
496
  $snoutpod$;
497
497
  select change || ' ' || role_name from pg_temp.snoutpod_db_access(:'db_access'::jsonb);
498
- `});var ni,Yy,Xy,Zy,Qy,ew,tw,ri=D(()=>{"use strict";Aa();Sr();xr();Ta();Oa();Da();Ia();Ma();Ka();Er();za();Va();ln();$r();ni=120,Yy=2*6e4,Xy=15*60*1e3,Zy=60*60*1e3,Qy=60*60*1e3,ew=5*60*1e3,tw=10*60*1e3});import{execFile as Ju}from"node:child_process";function oi(){return(e,t,n={})=>new Promise((r,o)=>{let s=Ju(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,l,i)=>{if(a&&typeof a.code=="string"){o(a);return}let u=a&&typeof a.code=="number"?a.code:0;r({code:u,stdout:String(l),stderr:String(i)})});n.stdin!==void 0&&s.stdin?.end(n.stdin)})}async function L(e,t,n,r){let o=await e(t,n,r);if(o.code!==0){let s=(o.stderr||o.stdout).trim().split(`
499
- `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var ht=D(()=>{"use strict"});import{readdir as zu,readFile as aw}from"node:fs/promises";async function si(e,t,n={}){let r=n.binary??"podman",o=j(t).pod,s=await e(r,["volume","inspect",o,"--format","json"]);if(s.code!==0)return!1;let a=JSON.parse(s.stdout||"[]"),i=(Array.isArray(a)?a[0]:a)?.Mountpoint;return i?await(n.look??Vu)(i)!=="empty":!1}async function Vu(e){try{return(await zu(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var ai=D(()=>{"use strict";ye();ht()});function ii(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can pass to a pod: ${JSON.stringify(t)}`);return t}async function di(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function ui(e){let t=!!(e?.key&&e?.secret),n=["--env",`SNOUTPOD_S3_KEY_TYPE=${t?"shared":"auto"}`];return e&&(t&&(n.push("--env",`SNOUTPOD_S3_KEY=${ii("the access key",e.key,Xu)}`),n.push("--env",ci),e.token&&n.push("--env",li)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${ii("the endpoint",e.endpoint,Yu)}`),e.uriStyle&&n.push("--env",`SNOUTPOD_S3_URI_STYLE=${e.uriStyle==="path"?"path":"host"}`),e.verifyTls===!1&&n.push("--env","SNOUTPOD_S3_VERIFY_TLS=n")),n}function mi(e){if(!e?.key||!e?.secret)return{};if(/[\r\n]/.test(e.secret))throw new Error("the repository secret contains a newline, which would write a second setting");if(e.token&&/[\r\n]/.test(e.token))throw new Error("the session token contains a newline, which would write a second setting");return{[ci]:e.secret,...e.token?{[li]:e.token}:{}}}var ci,li,Yu,Xu,fi=D(()=>{"use strict";ci="SNOUTPOD_S3_SECRET",li="SNOUTPOD_S3_TOKEN",Yu=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,Xu=/^[A-Za-z0-9/+=_.-]{1,256}$/});function mn(e){return Zu[e]??e}var pi,gi,we,Zu,hi,yi=D(()=>{"use strict";pi="authenticator",gi=`do $snoutpod$
498
+ `});var ai,uw,mw,fw,pw,gw,hw,ii=I(()=>{"use strict";Ta();xr();vr();Na();Da();Ca();Ma();La();Wa();Pr();Ya();Xa();Qa();dn();Ar();ai=120,uw=2*6e4,mw=15*60*1e3,fw=60*60*1e3,pw=60*60*1e3,gw=5*60*1e3,hw=10*60*1e3});import{execFile as Zu}from"node:child_process";function ci(){return(e,t,n={})=>new Promise((r,o)=>{let s=Zu(e,[...t],{timeout:n.timeoutMs??0,maxBuffer:8388608,env:n.env?{...process.env,...n.env}:process.env},(a,d,i)=>{if(a&&typeof a.code=="string"){o(a);return}let u=a&&typeof a.code=="number"?a.code:0;r({code:u,stdout:String(d),stderr:String(i)})});n.stdin!==void 0&&s.stdin?.end(n.stdin)})}async function j(e,t,n,r){let o=await e(t,n,r);if(o.code!==0){let s=(o.stderr||o.stdout).trim().split(`
499
+ `).slice(-3).join("; ");throw new Error(`${t} ${n[0]??""} failed (${o.code}): ${s}`)}return o.stdout}var ht=I(()=>{"use strict"});import{readdir as Qu,readFile as _w}from"node:fs/promises";async function li(e,t,n={}){let r=n.binary??"podman",o=L(t).pod,s=await e(r,["volume","inspect",o,"--format","json"]);if(s.code!==0)return!1;let a=JSON.parse(s.stdout||"[]"),i=(Array.isArray(a)?a[0]:a)?.Mountpoint;return i?await(n.look??em)(i)!=="empty":!1}async function em(e){try{return(await Qu(e)).length>0?"data":"empty"}catch(t){return t.code==="ENOENT"?"empty":"hidden"}}var di=I(()=>{"use strict";Se();ht()});function ui(e,t,n){if(!n.test(t))throw new Error(`${e} is not a value this can pass to a pod: ${JSON.stringify(t)}`);return t}async function pi(e,t){if(!t)return e??null;let n=await t();return{...e??{},key:n.accessKeyId,secret:n.secretAccessKey,token:n.sessionToken}}function gi(e){let t=!!(e?.key&&e?.secret),n=["--env",`SNOUTPOD_S3_KEY_TYPE=${t?"shared":"auto"}`];return e&&(t&&(n.push("--env",`SNOUTPOD_S3_KEY=${ui("the access key",e.key,nm)}`),n.push("--env",mi),e.token&&n.push("--env",fi)),e.endpoint&&n.push("--env",`SNOUTPOD_S3_ENDPOINT=${ui("the endpoint",e.endpoint,tm)}`),e.uriStyle&&n.push("--env",`SNOUTPOD_S3_URI_STYLE=${e.uriStyle==="path"?"path":"host"}`),e.verifyTls===!1&&n.push("--env","SNOUTPOD_S3_VERIFY_TLS=n")),n}function hi(e){if(!e?.key||!e?.secret)return{};if(/[\r\n]/.test(e.secret))throw new Error("the repository secret contains a newline, which would write a second setting");if(e.token&&/[\r\n]/.test(e.token))throw new Error("the session token contains a newline, which would write a second setting");return{[mi]:e.secret,...e.token?{[fi]:e.token}:{}}}var mi,fi,tm,nm,yi=I(()=>{"use strict";mi="SNOUTPOD_S3_SECRET",fi="SNOUTPOD_S3_TOKEN",tm=/^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$/i,nm=/^[A-Za-z0-9/+=_.-]{1,256}$/});function fn(e){return rm[e]??e}var wi,bi,_e,rm,Si,_i=I(()=>{"use strict";wi="authenticator",bi=`do $snoutpod$
500
500
  declare
501
501
  wanted text[];
502
502
  begin
@@ -514,21 +514,21 @@ begin
514
514
  end loop;
515
515
  end
516
516
  $snoutpod$;
517
- `,we="snout_auth_admin",Zu={supabase_auth_admin:we};hi=`do $snoutpod$
517
+ `,_e="snout_auth_admin",rm={supabase_auth_admin:_e};Si=`do $snoutpod$
518
518
  begin
519
519
  if exists (select from pg_catalog.pg_roles where rolname = 'supabase_auth_admin') then
520
- if exists (select from pg_catalog.pg_roles where rolname = '${we}') then
521
- perform pg_catalog.pg_terminate_backend(pid) from pg_catalog.pg_stat_activity where usename = '${we}';
522
- reassign owned by ${we} to supabase_auth_admin;
523
- drop owned by ${we};
524
- drop role ${we};
520
+ if exists (select from pg_catalog.pg_roles where rolname = '${_e}') then
521
+ perform pg_catalog.pg_terminate_backend(pid) from pg_catalog.pg_stat_activity where usename = '${_e}';
522
+ reassign owned by ${_e} to supabase_auth_admin;
523
+ drop owned by ${_e};
524
+ drop role ${_e};
525
525
  end if;
526
- alter role supabase_auth_admin rename to ${we};
526
+ alter role supabase_auth_admin rename to ${_e};
527
527
  end if;
528
528
  end
529
529
  $snoutpod$;
530
- `});function bi(e){let t=[];for(let n of e.split(`
531
- `)){let r=n.split(Pr);if(r.length!==4||!Ar.includes(r[0]))continue;let[o,s,a,l]=r;t.push(l?{name:o,from:s,error:l}:{name:o,from:s,to:a})}return t}var Ar,Pr,wi,Si=D(()=>{"use strict";Ar=["snouttime"];for(let e of Ar)if(!/^[a-z][a-z0-9_]{0,62}$/.test(e))throw new Error(`not an extension name: ${JSON.stringify(e)}`);Pr="",wi=`
530
+ `});function ki(e){let t=[];for(let n of e.split(`
531
+ `)){let r=n.split(Rr);if(r.length!==4||!Tr.includes(r[0]))continue;let[o,s,a,d]=r;t.push(d?{name:o,from:s,error:d}:{name:o,from:s,to:a})}return t}var Tr,Rr,xi,vi=I(()=>{"use strict";Tr=["snouttime"];for(let e of Tr)if(!/^[a-z][a-z0-9_]{0,62}$/.test(e))throw new Error(`not an extension name: ${JSON.stringify(e)}`);Rr="",xi=`
532
532
  SET search_path = pg_catalog, pg_temp;
533
533
  CREATE TEMP TABLE snoutpod_extension_updates (name text, from_version text, to_version text, error text);
534
534
  DO $snoutpod$
@@ -542,7 +542,7 @@ BEGIN
542
542
  SELECT e.extname, e.extversion
543
543
  FROM pg_extension e
544
544
  JOIN pg_available_extensions a ON a.name = e.extname
545
- WHERE e.extname = ANY (ARRAY[${Ar.map(e=>`'${e}'`).join(", ")}])
545
+ WHERE e.extname = ANY (ARRAY[${Tr.map(e=>`'${e}'`).join(", ")}])
546
546
  AND a.default_version IS DISTINCT FROM e.extversion
547
547
  LOOP
548
548
  BEGIN
@@ -573,97 +573,97 @@ BEGIN
573
573
  END
574
574
  $snoutpod$;
575
575
  SELECT name, from_version, coalesce(to_version, ''), coalesce(error, '') FROM snoutpod_extension_updates ORDER BY name;
576
- `});function xi(e){if(typeof e!="string"||!/^https?:\/\//.test(e)||e.length>255)throw new Error(`not an OAuth issuer a pod can be given: ${JSON.stringify(e)}`);if(/[\s"\\\u0000-\u001f\u007f]/.test(e))throw new Error(`an OAuth issuer may not hold spaces, quotes, backslashes or control characters: ${JSON.stringify(e)}`);return e}var _i,gw,hw,vi=D(()=>{"use strict";ht();Rr();_i="SNOUTPOD_OAUTH_ISSUER",gw=256*1024,hw=5*6e4});import{createHash as Nr,randomBytes as Qu}from"node:crypto";import{access as em,constants as tm}from"node:fs/promises";import{delimiter as nm,join as rm}from"node:path";function om(e,t){return Nr("sha256").update(Dr(e)).update(e.smtpSecretEnv&&t?Nr("sha256").update(t).digest("hex"):"").digest("hex").slice(0,32)}function Dr(e){if(Array.isArray(e))return`[${e.map(Dr).join(",")}]`;if(e!==null&&typeof e=="object"){let t=e;return`{${Object.keys(t).sort().filter(r=>t[r]!==void 0).map(r=>`${JSON.stringify(r)}:${Dr(t[r])}`).join(",")}}`}return JSON.stringify(e??null)}function Mr(e){if(!am.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function wt(e){if(!im.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function cm(e,t,n){return`postgres://${wt(t)}:${n}@127.0.0.1:5432/${Z(e)}`}function $i(e,t,n,r){let o={...t.env};if(t.dbUriEnv&&t.dbUriRole){let s=mn(t.dbUriRole),a=n.get(s);if(a===void 0)throw new Error(`no password was applied for role ${JSON.stringify(t.dbUriRole)}, which ${JSON.stringify(t.name)} needs to reach its database`);o[t.dbUriEnv]=cm(e.ref,s,a)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),lm(t.image,o)}function lm(e,t){if(!/(^|\/)snout-auth([:@]|$)/.test(e))return{...t};let n={};for(let[r,o]of Object.entries(t)){let s=r.startsWith("GOTRUE_")?`AUTH_${r.slice(7)}`:r==="API_EXTERNAL_URL"?"AUTH_API_EXTERNAL_URL":r;s!==r&&s in t||(n[s]=o)}return n}function Ai(e,t){return`${j(e).pod}-${Mr(t)}`}function fn(e){return`snoutpod-host-${Mr(e)}`}function dm(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return Nr("sha256").update(JSON.stringify([e.image,e.port,e.hostPort,e.healthPath??null,e.memoryMb,t,e.adminPort??null,e.adminHostPort??null,e.command??null,e.volumes??null,e.network??null])).digest("hex").slice(0,32)}function um(e,t){let n=[],r=Ir(t);for(let o of e??[])if(Mr(o.name),Cr(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){Cr(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function Ir(e){let t=e===void 0?[]:typeof e=="string"?[e]:[...e];return["127.0.0.1",...new Set(t.filter(n=>n!=="127.0.0.1"))]}function Cr(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function Ri(e){let t=[];for(let n of Object.keys(e)){if(!mm.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function fm(e){let t=e.healthPath;if(t==null||t==="")return[];if(!Ni.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(Cr(e.hostPort,`${e.name}: hostPort`),["--label",`${jr}=${e.hostPort}:${t}`])}function pm(e){if(!e)return null;let t=e.indexOf(":");if(t<=0)return null;let n=Number(e.slice(0,t)),r=e.slice(t+1);return!Number.isInteger(n)||n<1||n>65535||!Ni.test(r)?null:{port:n,path:r}}async function hm(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>em(o,tm.X_OK));for(let o of n.split(nm))if(o)try{let s=rm(o,e);return await r(s),s}catch{}return null}function ym(){return["\\set ON_ERROR_STOP 0","set search_path = pg_catalog, pg_temp;","select 'create extension if not exists pg_prewarm schema public' where exists (select 1 from pg_available_extensions where name = 'pg_prewarm') \\gexec","select pg_catalog.format($snoutpod$with candidate as ("," select c.oid, pg_catalog.pg_relation_size(c.oid) as bytes"," from pg_class c join pg_namespace n on n.oid = c.relnamespace"," where c.relkind in ('r', 'i', 'm')"," and c.relpersistence = 'p'"," and n.nspname not in ('pg_catalog', 'information_schema', 'pg_toast')","), budgeted as ("," select oid, bytes,"," sum(bytes) over (order by bytes, oid rows between unbounded preceding and current row) as running"," from candidate",")","select coalesce(sum(%I.pg_prewarm(oid::pg_catalog.regclass, 'buffer'::pg_catalog.text, 'main'::pg_catalog.text, null::pg_catalog.int8, null::pg_catalog.int8)), 0) from budgeted","where bytes > 0 and running <= pg_size_bytes(current_setting('shared_buffers')) * 3 / 4$snoutpod$, n.nspname)","from pg_extension e join pg_namespace n on n.oid = e.extnamespace where e.extname = 'pg_prewarm' \\gexec",""].join(`
577
- `)}function bm(e){return/^(\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)\//.exec(e)?.[1]??null}function Sm(e,t){for(let r of e??[]){let o=/^PG_MAJOR=(\d+)$/.exec(r.trim());if(o)return o[1]??null}let n=t.split("@")[0]??"";return/:(\d+)(?:[.-][^/:]*)?$/.exec(n)?.[1]??null}function Lr(e){let t=(e??"").trim();return/^\d+(?:\.\d+)?$/.test(t)?t:null}function _m(e,t){return t.image===null||t.data===null||t.image===t.data?null:`${e} was not started: its data directory was made by Postgres ${t.data} and it was handed a Postgres ${t.image} image. A data directory starts only on the major that made it. Its data is untouched, and it starts again on a ${t.data} image.`}async function xm(e,t,n,r){return{image:await vm(e,t,r),data:await km(e,t,n)}}async function vm(e,t,n){let r=null;try{let o=await e(t,["image","inspect","--format","{{json .Config.Env}}",n]);if(o.code===0&&o.stdout.trim()){let s=JSON.parse(o.stdout.trim());r=Array.isArray(s)?s.filter(a=>typeof a=="string"):null}}catch{r=null}return Sm(r,n)}async function km(e,t,n){try{let r=await e(t,["volume","inspect","--format","{{.Mountpoint}}",j(n).pod]),o=r.code===0?r.stdout.trim().replace(/\/+$/,""):"";if(!o)return null;let s=await e(t,["unshare","cat",`${o}/PG_VERSION`]);return s.code===0?Lr(s.stdout):null}catch{return null}}function Em(e){if(!e)return[];let t=[],n=/^[1-9][0-9]{0,6}(kB|MB|GB)$/;typeof e.sharedBuffers=="string"&&n.test(e.sharedBuffers)&&t.push(`SNOUTPOD_SHARED_BUFFERS=${e.sharedBuffers}`),typeof e.workMem=="string"&&n.test(e.workMem)&&t.push(`SNOUTPOD_WORK_MEM=${e.workMem}`);let r=typeof e.maxConnections=="number"&&Number.isInteger(e.maxConnections)&&e.maxConnections>=1&&e.maxConnections<=1e4?e.maxConnections:Ti;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,Ti)}`),typeof e.privilegedExtensions=="string"&&/^[a-z][a-z0-9_-]*(,[a-z][a-z0-9_-]*)*$/.test(e.privilegedExtensions)&&t.push(`SNOUTPOD_PRIVILEGED_EXTENSIONS=${e.privilegedExtensions}`),t}function $m(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??gm;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await hm(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function Ii(e){let t=e.binary??"podman",n=e.exec,r=$m(e),o=e.probe??Di;async function s(m){try{await n(t,["exec",...Q,"--interactive",j(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"--quiet","--file","-"],{timeoutMs:wm,stdin:ym()})}catch{}}async function a(){let m=await L(n,t,["ps","--all","--filter",`label=${Ie}`,"--format","json"]),p=m.trim()?JSON.parse(m):[],S=new Map,w=new Map;for(let $ of p){let T=$.Labels?.[Ie],v=$.Labels?.[Je];if(!T||!v)continue;let M=S.get(T)??[];M.push(v),S.set(T,M);let C=Number($.Labels?.[Or]??0),Y=Number.isInteger(C)?C:0;w.set(T,Math.max(w.get(T)??Y,Y))}let x=[];for(let $ of p){let T=$.Labels?.[Ie];if(!T||$.Labels?.[Je])continue;let v=Number($.Labels?.[Tr]);x.push({ref:T,status:Am($.State),generation:Number.isInteger(v)?v:null,hasData:await e.hasData(T),services:S.get(T)??[],...w.has(T)?{servicesGeneration:w.get(T)}:{}})}return x}async function l(m){let p=await(e.majors??((S,w)=>xm(n,t,S,w)))(m.ref,m.image);return _m(m.ref,p)}async function i(m){let p=j(m.ref),S=await l(m);if(S)throw new Error(S);let w=e.credentials,x=await di(e.repository,w?()=>w.forProject(m.ref,m.bucket):null),$={SNOUTPOD_OWNER_PASSWORD:m.ownerPassword,POSTGRES_PASSWORD:m.adminPassword,...mi(x)},T=[...await r(),"run","--detach","--name",p.pod,"--label",`${Ie}=${m.ref}`,"--label",`${Tr}=${m.generation}`,"--userns=auto","--memory",`${m.memoryMb}m`,...Tm(m.cpus),"--restart","no",...e.network?["--network",e.network]:[],...m.port?Ir(e.podBind).flatMap(v=>["--publish",`${v}:${m.port}:5432`]):[],...um(m.services,e.podBind),"--volume",`${p.pod}:/var/lib/postgresql/data:U`,"--env","SNOUTPOD_OWNER_PASSWORD","--env","POSTGRES_PASSWORD","--env",`SNOUTPOD_REF=${m.ref}`,"--env","POSTGRES_USER=snoutpod_admin","--env","POSTGRES_DB=postgres","--env",`SNOUTPOD_S3_BUCKET=${m.bucket}`,"--env",`SNOUTPOD_S3_REGION=${m.region}`,...e.egressDenied?["--env","SNOUTPOD_EGRESS=denied"]:[],...Rm(m.oauth),...Em(m.tuning).flatMap(v=>["--env",v]),"--env",`SNOUTPOD_RETENTION_FULL=${Ra(m.backup?.retentionDays,m.backup?.fullIntervalHours)}`,...ui(x),m.image,"snoutpod-postgres"];await L(n,t,T,{env:$})}async function u(m){let p=await L(n,t,["ps","--all","--filter",`label=${Ie}=${Z(m)}`,"--filter",`label=${Je}`,"--format","json"]),S=p.trim()?JSON.parse(p):[],w=[];for(let x of S){let $=x.Labels?.[Je];if(!$)continue;let T=Number(x.Labels?.[Or]??0);w.push({name:$,container:Ai(m,$),spec:x.Labels?.[ki]??"",pod:x.Labels?.[Ei]??"",running:x.State==="running",generation:Number.isInteger(T)?T:0})}return w}async function d(m){let p=await n(t,["container","inspect","--format","{{.Id}}",j(m).pod]);return p.code===0?p.stdout.trim():""}async function f(m,p){let S=[];for(let v of p){let M=v.dbUriRole?mn(v.dbUriRole):null;v.dbUriEnv&&M&&!S.includes(M)&&S.push(wt(M))}let w=new Map;if(S.length===0)return w;let x={},$=[],T=S.includes(we)?[hi]:[];return S.includes(pi)&&T.push(gi),S.forEach((v,M)=>{let C=Qu(24).toString("base64url"),Y=`SNOUTPOD_SERVICE_PASSWORD_${M}`;w.set(v,C),x[Y]=C,$.push("--env",Y),T.push(`\\getenv service_password_${M} ${Y}`),T.push(`\\set service_role_${M} ${v}`),T.push(`alter role :"service_role_${M}" with password :'service_password_${M}';`)}),T.push(""),await L(n,t,["exec",...Q,"--interactive",...$,j(m.ref).pod,"psql","--username","snoutpod_admin","--dbname",j(m.ref).database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:x,stdin:T.join(`
578
- `)}),w}async function y(m){let p=m.services??[],S=await u(m.ref),w;p.some(P=>P.smtpSecretEnv)&&(w=await e.smtpSecret?.().catch(()=>{}));let x=p.length>0?await d(m.ref):"",$=new Map(S.map(P=>[P.name,P])),T=new Map(p.map(P=>[P.name,om(P,w)])),v=new Set(p.filter(P=>{let ae=$.get(P.name);return!ae||!ae.running||ae.spec!==T.get(P.name)||x===""||ae.pod!==x}).map(P=>P.name)),M=m.servicesGeneration??0;if(v.size===0&&p.length>0&&!S.some(P=>P.generation>=M))for(let P of p)v.add(P.name);let C=P=>P.dbUriEnv&&P.dbUriRole?mn(P.dbUriRole):null;for(let P=!0;P;){P=!1;let ae=new Set(p.filter(se=>v.has(se.name)).map(C));for(let se of p){let zr=C(se);!v.has(se.name)&&zr!==null&&ae.has(zr)&&(v.add(se.name),P=!0)}}for(let P of S)(v.has(P.name)||!T.has(P.name))&&await L(n,t,["rm","--force","--ignore",P.container]);let Y=p.filter(P=>v.has(P.name)),Ye=await f(m,Y);for(let P of Y){let ae=Ai(m.ref,P.name),se=[...await r(),"run","--detach","--name",ae,"--label",`${Ie}=${m.ref}`,"--label",`${Tr}=${m.generation}`,"--label",`${Je}=${P.name}`,"--label",`${Or}=${m.servicesGeneration??0}`,"--label",`${ki}=${T.get(P.name)??""}`,"--label",`${Ei}=${x}`,"--network",`container:${j(m.ref).pod}`,"--userns",`container:${j(m.ref).pod}`,"--memory",`${P.memoryMb}m`,"--restart","no",...fm(P),...Ri($i(m,P,Ye,w)),P.image];await L(n,t,se,{env:$i(m,P,Ye,w)})}}return{list:a,preflight:l,create:i,createServices:y,async start(m){await L(n,t,["start",j(m).pod])},async startServices(m){for(let p of await u(m))await L(n,t,["start",p.container])},async applyHostServices(m){let p=[],S=await L(n,t,["ps","--all","--filter",`label=${yt}`,"--format","json"]),w=S.trim()?JSON.parse(S):[],x=new Map;for(let v of w){let M=v.Labels?.[yt];M&&x.set(M,{spec:v.Labels?.[Pi]??"",state:v.State??""})}for(let v of m)try{await T(v)}catch(M){p.push({name:v.name,action:"failed",reason:M instanceof Error?M.message:String(M)})}async function $(v){let M=await r();if((await n(t,[...M,"image","exists",v])).code===0)return;let C=await n(t,[...M,"pull","--quiet",v]);if(C.code===0)return;let Y=bm(v);if(!Y||!e.registryLogin)throw new Error(`${v} could not be pulled: ${(C.stderr||C.stdout).trim()}`);await e.registryLogin(Y),await L(n,t,[...M,"pull","--quiet",v])}async function T(v){let M=e.hostServiceLocal?.(v,m),C=M?{...v,...M.env&&Object.keys(M.env).length>0?{env:{...v.env,...M.env}}:{},...M.volumes?{volumes:M.volumes}:{},...M.network?{network:M.network}:{}}:v,Y=fn(C.name),Ye=dm(C),P=x.get(C.name);if(x.delete(C.name),P&&P.spec===Ye){P.state!=="running"&&(await L(n,t,["start",Y]),p.push({name:C.name,action:"started",reason:P.state}));return}let ae=P?"the spec changed":"it was not here";await $(C.image),P&&await L(n,t,["rm","--force","--ignore",Y]),await L(n,t,[...await r(),"run","--detach","--name",Y,"--label",`${yt}=${C.name}`,"--label",`${Pi}=${Ye}`,...C.healthPath?["--label",`${jr}=${C.hostPort}:${C.healthPath}`]:[],"--memory",`${C.memoryMb}m`,"--restart","no",...C.network?["--network",C.network]:e.network?["--network",e.network]:[],...(C.volumes??[]).flatMap(se=>["--volume",se]),...Ir(e.podBind).flatMap(se=>["--publish",`${se}:${C.hostPort}:${C.port}`]),...C.adminPort&&C.adminHostPort?["--publish",`127.0.0.1:${C.adminHostPort}:${C.adminPort}`]:[],...Ri(C.env),C.image,...C.command??[]],{env:{...C.env}}),p.push({name:C.name,action:P?"recreated":"created",reason:ae})}for(let v of x.keys())await L(n,t,["rm","--force","--ignore",fn(v)]),p.push({name:v,action:"removed",reason:"it is no longer on the list"});return p},async hostServiceHealth(m){let p=await L(n,t,["ps","--all","--filter",`label=${yt}`,"--format","json"]),S=(p.trim()?JSON.parse(p):[]).map(x=>x.Labels?.[yt]??"").filter(x=>x!=="");if(S.length===0)return[];let w=Date.now()+m;for(;;){let x=[];for(let $ of S)x.push(await Oi(n,t,$,fn($),o));if(x.every($=>$.ok)||Date.now()>=w)return x;await new Promise($=>setTimeout($,500))}},async serviceHealth(m,p){let S=await u(m);if(S.length===0)return[];let w=Date.now()+p;for(;;){let x=[];for(let $ of S)x.push(await Oi(n,t,$.name,$.container,o));if(x.every($=>$.ok)||Date.now()>=w)return x;await new Promise($=>setTimeout($,500))}},async stop(m,p){for(let S of await u(m))await L(n,t,["stop","--time",String(sm),S.container]);await L(n,t,["stop","--time",String(p),j(m).pod])},async remove(m){for(let p of await u(m))await L(n,t,["rm","--force","--ignore",p.container]);await L(n,t,["rm","--force","--ignore","--volumes",j(m).pod])},async removeData(m){await L(n,t,["volume","rm","--force",j(m).pod])},async applyOwnerPassword(m){let p=["\\getenv owner_password SNOUTPOD_OWNER_PASSWORD",`\\set owner_role ${m.ref}_owner`,`alter role :"owner_role" with password :'owner_password';`,""].join(`
579
- `);await L(n,t,["exec",...Q,"--interactive","--env","SNOUTPOD_OWNER_PASSWORD",j(m.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_OWNER_PASSWORD:m.ownerPassword},stdin:p})},async applyRolePassword(m,p,S){let w=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${wt(p)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
580
- `);await L(n,t,["exec",...Q,"--interactive","--env","SNOUTPOD_ROLE_PASSWORD",j(m).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_ROLE_PASSWORD:S},stdin:w})},async updateExtensions(m){let p=j(m),S=await L(n,t,["exec",...Q,"--interactive",p.pod,"psql","--username","snoutpod_admin","--dbname",p.database,"--quiet","-tA","-F",Pr,"-v","ON_ERROR_STOP=1","--file","-"],{stdin:wi});return bi(S)},async applyDbAccess(m,p){let S=j(m),w=await L(n,t,["exec",...Q,"--interactive","--env",un,S.pod,"psql","--username","snoutpod_admin","--dbname",S.database,"--quiet","-tA","-v","ON_ERROR_STOP=1","--file","-"],{env:{[un]:ti(m,p)},stdin:Qa,timeoutMs:6e4});return ei(w)},async runProjectSql(m,p){let S=j(m);await L(n,t,["exec",...Q,"--interactive",S.pod,"psql","--username","snoutpod_admin","--dbname",S.database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:p})},async ensureHostServiceDatabase(m,p,S){let w=fn(m),x=wt(p),$=wt(S),T=M=>["exec",w,"psql","--username",x,"--dbname","postgres","--quiet","-tA","-v","ON_ERROR_STOP=1","-c",M];(await L(n,t,T(`select 1 from pg_database where datname = '${$}'`))).trim()!=="1"&&await L(n,t,T(`create database "${$}"`))},async replicationSlots(m){let p=await L(n,t,["exec",...Q,j(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tA","-F","|","-c",Wa]);return Ja(p)},async dropReplicationSlot(m,p){if(!/^[a-z0-9_]{1,63}$/.test(p))throw new Error(`not a replication slot name: ${JSON.stringify(p)}`);await L(n,t,["exec",...Q,j(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-v","ON_ERROR_STOP=1","-c",`select pg_catalog.pg_drop_replication_slot('${p}'::pg_catalog.name)`])},async renameProject(m,p){p!==m.ref&&await L(n,t,["exec",...Q,"--interactive",j(m.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:Pm(p,m.ref)})},async waitReady(m,p){let S=j(m).pod,w=Date.now()+p;for(;;){let x=await n(t,["exec",...Q,S,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname","postgres","-tAc","select not pg_is_in_recovery()"]);if(x.code===0&&x.stdout.trim()==="t")return s(m),!0;if(Date.now()>=w)return!1;await new Promise($=>setTimeout($,500))}},async capacity(){let m=await a(),p=await e.capacity();return{pods:m.length,...p}},async databaseBytes(m){let p=await n(t,["exec",...Q,j(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tAc","select pg_database_size(current_database())"]);return p.code===0?_r(p.stdout):null},async storageBytes(m){let p=await n(t,["exec",...Q,j(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tAc","select coalesce(sum((metadata->>'size')::bigint), 0) from storage.objects"]);return p.code===0?_r(p.stdout):null},async setReadOnly(m,p){let S=j(m);await L(n,t,["exec",...Q,S.pod,"psql","--username","snoutpod_admin","--dbname","postgres","-tAc",`alter database ${S.database} set default_transaction_read_only = ${p?"on":"off"}`])},async archiveState(m){let p=await n(t,["exec",...Q,j(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tA","-F","|","-c",`select coalesce(to_char(last_archived_time, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF'), ''), case when last_failed_time > coalesce(last_archived_time, '-infinity') then failed_count else 0 end from pg_stat_archiver`]);if(p.code!==0)return null;let[S="",w=""]=p.stdout.trim().split(`
581
- `)[0]?.split("|")??[],x=Number(w);return{lastArchivedAt:S.trim()||null,failures:Number.isInteger(x)&&x>=0?x:0}}}}async function Oi(e,t,n,r,o=Di){let s=await e(t,["inspect","--format","json",r]);if(s.code!==0)return{name:n,ok:!1,detail:"no such container"};let a,l;try{let f=JSON.parse(s.stdout);a=f[0]?.State,l=f[0]?.Config?.Labels}catch{return{name:n,ok:!1,detail:"podman inspect could not be read"}}let i=a?.Status??"unknown";if(i!=="running")return{name:n,ok:!1,detail:i};let u=pm(l?.[jr]);if(!u)return{name:n,ok:!0,detail:"running"};let d=await o(u.port,u.path);return d===null?{name:n,ok:!1,detail:"not answering yet"}:{name:n,ok:!0,detail:`answered ${d}`}}function Am(e){switch((e??"").toLowerCase()){case"running":return"running";case"created":case"configured":return"starting";case"exited":case"stopped":return"stopped";default:return"unknown"}}function Pm(e,t){return[`\\set from_db ${e}`,`\\set to_db ${t}`,`\\set from_role ${e}_owner`,`\\set to_role ${t}_owner`,'alter database :"from_db" rename to :"to_db";','alter role :"from_role" rename to :"to_role";',"alter system reset restore_command;","alter system reset recovery_target_time;","alter system reset recovery_target_action;",""].join(`
582
- `)}function Rm(e){if(!e?.issuer)return[];try{return["--env",`${_i}=${xi(e.issuer)}`]}catch{return[]}}function Tm(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var Ie,Tr,Je,Or,ki,Ei,sm,am,im,yt,Pi,mm,Ni,jr,Di,gm,Q,wm,Ti,Rr=D(()=>{"use strict";ye();Sr();Er();ht();fi();xr();yi();Si();$r();vi();Ie="snoutpod.ref",Tr="snoutpod.generation",Je="snoutpod.service",Or="snoutpod.services-generation",ki="snoutpod.service-spec",Ei="snoutpod.service-pod";sm=10,am=/^[a-z][a-z0-9-]{0,30}$/;im=/^[a-z][a-z0-9_]{0,62}$/;yt="snoutpod.host-service",Pi="snoutpod.host-service-spec";mm=/^[A-Za-z_][A-Za-z0-9_]*$/;Ni=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,jr="snoutpod.health";Di=async(e,t)=>{let{request:n}=await import("node:http");return new Promise(r=>{let o=n({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:2e3},s=>{s.resume(),r(s.statusCode??null)});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null)}),o.end()})},gm="crun";Q=["--env","PGOPTIONS=-c search_path=pg_catalog,pg_temp"],wm=3e4;Ti=200});var Vi={};Vr(Vi,{DEFAULT_LOCAL_MEMORY_MB:()=>Ui,DEFAULT_LOCAL_PORT:()=>Cm,ENGINE:()=>B,ENGINE_NAME:()=>Ur,IMAGE:()=>Hr,NETWORK:()=>Ae,NEW_PROJECT_MAJOR:()=>Br,NO_BLACKHOLE_ROUTES:()=>Gi,PUBLIC_IMAGE:()=>Um,PUBLISHED_MAJORS:()=>ze,capacity:()=>Hi,chooseLocalImage:()=>_t,dropIfUnused:()=>Ji,ensureImage:()=>Fm,ensureNetwork:()=>Km,exec:()=>z,hasData:()=>yn,localImageFor:()=>Bi,localPsql:()=>zm,localRuntime:()=>qm,localSpec:()=>Wi,localStatus:()=>Gr,localUri:()=>Ki,networkPolicyInPlace:()=>qi,newLocalPassword:()=>jm,newLocalRef:()=>Mm,podLog:()=>zi,podName:()=>Ve,podmanReady:()=>Hm,removeLocal:()=>Xm,staleImage:()=>Fr,startLocal:()=>Jm,stopLocal:()=>Ym,volumePgVersion:()=>pn});import{randomBytes as ji}from"node:crypto";import{existsSync as Ci}from"node:fs";import{platform as hn}from"node:os";import{delimiter as Om,join as Nm}from"node:path";function Dm(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=hn()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(Om))if(t&&e.some(n=>Ci(Nm(t,n))))return"podman";return Ci("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function _t(e,t=process.env.SNOUTPOD_IMAGE){if(t)return{name:t,published:null,major:null};if(e===null)return St(Br);let n=Number(Lr(e));if(!ze.includes(n)){let r=e.trim()||"(empty)",o=ze.map(s=>St(s).published).join(" or ");throw new Error(`its data directory's PG_VERSION says ${r}, and there is an image only for Postgres ${ze.join(" and ")}. Set SNOUTPOD_IMAGE to an image of that major (${o} for those).`)}return St(n)}function St(e){return{name:`${Im}:${e}`,published:`${Li}:${e}`,major:e}}function Mm(){return $a(e=>ji(e))}function jm(){return ji(18).toString("base64url")}async function Fm(e,t=z,n=_t(null)){let r=n.published;if(!r)return null;if((await t(B,["image","exists",n.name])).code===0)return await Bm(e,t,n.name,r),null;e?.(`Fetching the database image (${r}). This happens once.`);let o=await t(B,["pull",r],{timeoutMs:Fi}).catch(a=>({code:1,stdout:"",stderr:a instanceof Error?a.message:String(a)}));if(o.code!==0)return`could not fetch the database image ${r}: ${o.stderr.trim()||o.stdout.trim()||"the pull failed"}`;let s=await t(B,["tag",r,n.name]);return s.code!==0?`fetched ${r} but could not tag it as ${n.name}: ${s.stderr.trim()}`:(e?.("Got it."),null)}async function Bm(e,t,n,r){let o=await t(B,["image","inspect","--format","{{.Id}}",n,r]).catch(()=>null),[s,a]=o?.code===0?o.stdout.trim().split(`
583
- `):[];if(!s||s!==a)return;let l=setTimeout(()=>e?.(`Fetching a newer database image (${r}).`),3e3),i=await t(B,["pull",r],{timeoutMs:Fi}).catch(y=>({code:1,stdout:"",stderr:y instanceof Error?y.message:String(y)})).finally(()=>clearTimeout(l));if(i.code!==0){let y=(i.stderr.trim()||i.stdout.trim()||"the pull failed").split(`
584
- `).at(-1);e?.(`Could not check for a newer database image (${y}); using the one on this machine.`);return}let u=await t(B,["image","inspect","--format","{{.Id}}",r]).catch(()=>null),d=u?.code===0?u.stdout.trim():"";if(!d||d===s)return;let f=await t(B,["tag",r,n]);if(f.code!==0){e?.(`Fetched a newer ${r} but could not tag it as ${n}: ${f.stderr.trim()}`);return}e?.(`Fetched a newer database image (${r}).`)}async function Hm(e=Lm,t=Hr){if(Ur!=="podman")return`SNOUTPOD_ENGINE=${Ur}: a local project is Podman-only, because a database that works under Docker and not under Podman is a database that works here and not in the cloud`;let n;try{n=await z(B,["--version"])}catch{return`${B} is not installed. macOS: brew install podman, then podman machine init && podman machine set --rootful=false && podman machine start. Windows: winget install RedHat.Podman, then the same three machine commands. Linux: your distribution's podman package.`}if(n.code!==0)return`${B} --version failed: ${n.stderr.trim()||n.stdout.trim()}`;if((await z(B,["image","exists",t])).code!==0)return`the pod image ${t} is not on this machine. ${e}`;let o=await z(B,["machine","inspect","--format","{{.Rootful}}"]);return o.code===0&&o.stdout.trim().startsWith("true")?"the podman machine is ROOTFUL, and the hosts are not: podman machine stop && podman machine set --rootful=false && podman machine start":null}function Gm(e){let t=`if out=$(ls -A ${JSON.stringify(e)} 2>/dev/null); then if [ -n "$out" ]; then echo DATA; else echo EMPTY; fi; else echo HIDDEN; fi`;return z(B,["machine","ssh",t]).then(n=>{let r=n.stdout.trim().split(`
585
- `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function yn(e){let t=hn()==="darwin"||hn()==="win32";return si(z,e,{binary:B,look:t?Gm:void 0})}async function pn(e,t=z,n=hn()){let r=j(e).pod;if((await t(B,["volume","exists",r])).code!==0)return null;let o;if(n==="darwin"||n==="win32"){let s=null;for(let a of ze){let l=St(a).name;if((await t(B,["image","exists",l])).code===0){s=l;break}}if(!s)throw new Error(`${r} holds a database and there is no pod image on this machine to read its Postgres version with. Set SNOUTPOD_IMAGE to the image it was made with (${ze.map(a=>St(a).published).join(" or ")}).`);o=await t(B,["run","--rm","--pull=never","--network=none","--userns=host","--user=0","--entrypoint=cat","--volume",`${r}:/data:ro`,s,"/data/PG_VERSION"])}else{let s=await t(B,["volume","inspect","--format","{{.Mountpoint}}",r]),a=s.code===0?s.stdout.trim().replace(/\/+$/,""):"";if(!a)throw new Error(`could not find where ${r} is: ${s.stderr.trim()||"no mountpoint"}`);o=await t(B,["unshare","cat",`${a}/PG_VERSION`])}if(o.code===0)return o.stdout;if(/no such file/i.test(o.stderr))return null;throw new Error(`could not read the Postgres version in ${r}: ${o.stderr.trim()||o.stdout.trim()||`exit ${o.code}`}`)}function Bi(e,t=pn){if(!e||process.env.SNOUTPOD_IMAGE)return Promise.resolve(_t(null));Z(e);let n=t===pn?gn.get(e):void 0;if(n)return n;let r=t(e).then(o=>{try{return _t(o)}catch(s){throw new Error(`${Ve(e)}: ${s instanceof Error?s.message:String(s)}`)}});return t===pn&&(gn.set(e,r),r.catch(()=>gn.delete(e))),r}async function Hi(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function qm(e){return Ii({exec:z,binary:B,network:Ae,hasData:yn,capacity:Hi,repository:e})}function Ve(e){return j(e).pod}async function Km(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await Wm()||await z(B,cn({name:Ae}));return}let t=cn({name:Ae,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await qi())return;await e?.(),await z(B,["network","rm","--force",Ae]).catch(()=>{});let o=await z(B,t);if(o.code===0)return;if(n=`${o.stderr}${o.stdout}`.trim(),/invalid route gateway/i.test(n))throw new Error(`${Gi}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${Ae} with egress blocked${n?`: ${n}`:""}`)}async function Wm(){return(await z(B,["network","inspect",Ae,"--format","json"])).code===0}async function qi(){let e=await z(B,["network","inspect",Ae,"--format","json"]);if(e.code!==0)return!1;try{return qa(JSON.parse(e.stdout)?.[0])}catch{return!1}}function Ki(e){let t=j(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function Wi(e,t={}){return Z(e.ref),{ref:e.ref,generation:1,image:t.image??Hr,memoryMb:t.memoryMb??Ui,cpus:null,bucket:"",region:"us-east-1",ownerPassword:e.ownerPassword,adminPassword:e.adminPassword,port:e.port,tuning:t.tuning??null,backup:null,services:t.services??null}}function bt(e,t){e.onProgress?.(t)}async function Jm(e,t,n={}){let r=n.image??(await Bi(t.ref,n.pgVersion)).name,o=Wi(t,{...n,image:r}),s=n.timeoutMs??12e4,a=n.hasData??yn,l=(await e.list()).find(u=>u.ref===t.ref),i=null;if(l?l.status==="stopped"&&(i=await(n.staleImage??Fr)(t.ref,r))?(bt(n,"Starting the database on the newer image. Its data stays as it is."),await e.remove(t.ref),await e.create(o),await(n.dropImage??Ji)(i)):l.status==="stopped"?(bt(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):(bt(n,"The database is already running."),l.status==="running"&&await(n.staleImage??Fr)(t.ref,r)&&bt(n,"A newer database image is on this machine. To move onto it: snoutdata stop, then snoutdata start.")):(bt(n,await a(t.ref)?"Starting the database over its existing data.":"Creating the database."),await e.create(o)),!await e.waitReady(t.ref,s))throw new Error(await Vm(t.ref,s,n.podLog??zi));return await e.applyOwnerPassword(o),await e.createServices(o),Gr(e,t,n)}function Mi(e){return e.trim().replace(/^sha256:/,"")}async function Fr(e,t){let[n,r]=await Promise.all([z(B,["container","inspect","--format","{{.Image}}",Ve(e)]).catch(()=>null),z(B,["image","inspect","--format","{{.Id}}",t]).catch(()=>null)]);if(n?.code!==0||r?.code!==0)return null;let o=Mi(n.stdout),s=Mi(r.stdout);return o&&s&&o!==s?o:null}async function Ji(e){let t=await z(B,["image","inspect","--format","{{len .RepoTags}}",e]).catch(()=>null);t?.code===0&&t.stdout.trim()==="0"&&await z(B,["rmi",e]).catch(()=>{})}async function zm(e,t){let n=j(e.ref);return z(B,["exec","--interactive","-e","PGPASSWORD",Ve(e.ref),"psql","--host","127.0.0.1","--port","5432","--username",n.ownerRole,"--dbname",n.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA","--file","-"],{stdin:t,env:{PGPASSWORD:e.ownerPassword}})}async function zi(e){let t=await z(B,["logs","--tail","20",Ve(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function Vm(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${Ve(e)} never became ready in ${t}ms
576
+ `});function $i(e){if(typeof e!="string"||!/^https?:\/\//.test(e)||e.length>255)throw new Error(`not an OAuth issuer a pod can be given: ${JSON.stringify(e)}`);if(/[\s"\\\u0000-\u001f\u007f]/.test(e))throw new Error(`an OAuth issuer may not hold spaces, quotes, backslashes or control characters: ${JSON.stringify(e)}`);return e}var Ei,Rw,Ow,Pi=I(()=>{"use strict";ht();Or();Ei="SNOUTPOD_OAUTH_ISSUER",Rw=256*1024,Ow=5*6e4});import{createHash as Ir,randomBytes as om}from"node:crypto";import{access as sm,constants as am}from"node:fs/promises";import{delimiter as im,join as cm}from"node:path";function lm(e,t){return Ir("sha256").update(Cr(e)).update(e.smtpSecretEnv&&t?Ir("sha256").update(t).digest("hex"):"").digest("hex").slice(0,32)}function Cr(e){if(Array.isArray(e))return`[${e.map(Cr).join(",")}]`;if(e!==null&&typeof e=="object"){let t=e;return`{${Object.keys(t).sort().filter(r=>t[r]!==void 0).map(r=>`${JSON.stringify(r)}:${Cr(t[r])}`).join(",")}}`}return JSON.stringify(e??null)}function Lr(e){if(!um.test(e))throw new Error(`not a service name: ${JSON.stringify(e)}`);return e}function wt(e){if(!mm.test(e))throw new Error(`not a database role: ${JSON.stringify(e)}`);return e}function fm(e,t,n){return`postgres://${wt(t)}:${n}@127.0.0.1:5432/${Q(e)}`}function Ri(e,t,n,r){let o={...t.env};if(t.dbUriEnv&&t.dbUriRole){let s=fn(t.dbUriRole),a=n.get(s);if(a===void 0)throw new Error(`no password was applied for role ${JSON.stringify(t.dbUriRole)}, which ${JSON.stringify(t.name)} needs to reach its database`);o[t.dbUriEnv]=fm(e.ref,s,a)}return t.smtpSecretEnv&&r&&(o[t.smtpSecretEnv]=r),pm(t.image,o)}function pm(e,t){if(!/(^|\/)snout-auth([:@]|$)/.test(e))return{...t};let n={};for(let[r,o]of Object.entries(t)){let s=r.startsWith("GOTRUE_")?`AUTH_${r.slice(7)}`:r==="API_EXTERNAL_URL"?"AUTH_API_EXTERNAL_URL":r;s!==r&&s in t||(n[s]=o)}return n}function Oi(e,t){return`${L(e).pod}-${Lr(t)}`}function pn(e){return`snoutpod-host-${Lr(e)}`}function gm(e){let t=Object.keys(e.env).sort().map(n=>[n,e.env[n]]);return Ir("sha256").update(JSON.stringify([e.image,e.port,e.hostPort,e.healthPath??null,e.memoryMb,t,e.adminPort??null,e.adminHostPort??null,e.command??null,e.volumes??null,e.network??null])).digest("hex").slice(0,32)}function hm(e,t){let n=[],r=Mr(t);for(let o of e??[])if(Lr(o.name),jr(o.port,`${o.name}: port`),!(o.hostPort===null||o.hostPort===void 0)){jr(o.hostPort,`${o.name}: hostPort`);for(let s of r)n.push("--publish",`${s}:${o.hostPort}:${o.port}`)}return n}function Mr(e){let t=e===void 0?[]:typeof e=="string"?[e]:[...e];return["127.0.0.1",...new Set(t.filter(n=>n!=="127.0.0.1"))]}function jr(e,t){if(!Number.isInteger(e)||e<1||e>65535)throw new Error(`${t} is not a port: ${JSON.stringify(e)}`);return e}function Di(e){let t=[];for(let n of Object.keys(e)){if(!ym.test(n))throw new Error(`not an environment variable name: ${JSON.stringify(n)}`);t.push("--env",n)}return t}function wm(e){let t=e.healthPath;if(t==null||t==="")return[];if(!Mi.test(t))throw new Error(`${e.name}: not a health path: ${JSON.stringify(t)}`);return e.hostPort===null||e.hostPort===void 0?[]:(jr(e.hostPort,`${e.name}: hostPort`),["--label",`${Ur}=${e.hostPort}:${t}`])}function bm(e){if(!e)return null;let t=e.indexOf(":");if(t<=0)return null;let n=Number(e.slice(0,t)),r=e.slice(t+1);return!Number.isInteger(n)||n<1||n>65535||!Mi.test(r)?null:{port:n,path:r}}async function _m(e,t={}){let n=t.path??process.env.PATH??"",r=t.look??(o=>sm(o,am.X_OK));for(let o of n.split(im))if(o)try{let s=cm(o,e);return await r(s),s}catch{}return null}function xm(){return["\\set ON_ERROR_STOP 0","set search_path = pg_catalog, pg_temp;","select 'create extension if not exists pg_prewarm schema public' where exists (select 1 from pg_available_extensions where name = 'pg_prewarm') \\gexec","select pg_catalog.format($snoutpod$with candidate as ("," select c.oid, pg_catalog.pg_relation_size(c.oid) as bytes"," from pg_class c join pg_namespace n on n.oid = c.relnamespace"," where c.relkind in ('r', 'i', 'm')"," and c.relpersistence = 'p'"," and n.nspname not in ('pg_catalog', 'information_schema', 'pg_toast')","), budgeted as ("," select oid, bytes,"," sum(bytes) over (order by bytes, oid rows between unbounded preceding and current row) as running"," from candidate",")","select coalesce(sum(%I.pg_prewarm(oid::pg_catalog.regclass, 'buffer'::pg_catalog.text, 'main'::pg_catalog.text, null::pg_catalog.int8, null::pg_catalog.int8)), 0) from budgeted","where bytes > 0 and running <= pg_size_bytes(current_setting('shared_buffers')) * 3 / 4$snoutpod$, n.nspname)","from pg_extension e join pg_namespace n on n.oid = e.extnamespace where e.extname = 'pg_prewarm' \\gexec",""].join(`
577
+ `)}function vm(e){return/^(\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com)\//.exec(e)?.[1]??null}function Em(e,t){for(let r of e??[]){let o=/^PG_MAJOR=(\d+)$/.exec(r.trim());if(o)return o[1]??null}let n=t.split("@")[0]??"";return/:(\d+)(?:[.-][^/:]*)?$/.exec(n)?.[1]??null}function Fr(e){let t=(e??"").trim();return/^\d+(?:\.\d+)?$/.test(t)?t:null}function $m(e,t){return t.image===null||t.data===null||t.image===t.data?null:`${e} was not started: its data directory was made by Postgres ${t.data} and it was handed a Postgres ${t.image} image. A data directory starts only on the major that made it. Its data is untouched, and it starts again on a ${t.data} image.`}async function Pm(e,t,n,r){return{image:await Am(e,t,r),data:await Tm(e,t,n)}}async function Am(e,t,n){let r=null;try{let o=await e(t,["image","inspect","--format","{{json .Config.Env}}",n]);if(o.code===0&&o.stdout.trim()){let s=JSON.parse(o.stdout.trim());r=Array.isArray(s)?s.filter(a=>typeof a=="string"):null}}catch{r=null}return Em(r,n)}async function Tm(e,t,n){try{let r=await e(t,["volume","inspect","--format","{{.Mountpoint}}",L(n).pod]),o=r.code===0?r.stdout.trim().replace(/\/+$/,""):"";if(!o)return null;let s=await e(t,["unshare","cat",`${o}/PG_VERSION`]);return s.code===0?Fr(s.stdout):null}catch{return null}}function Rm(e){if(!e)return[];let t=[],n=/^[1-9][0-9]{0,6}(kB|MB|GB)$/;typeof e.sharedBuffers=="string"&&n.test(e.sharedBuffers)&&t.push(`SNOUTPOD_SHARED_BUFFERS=${e.sharedBuffers}`),typeof e.workMem=="string"&&n.test(e.workMem)&&t.push(`SNOUTPOD_WORK_MEM=${e.workMem}`);let r=typeof e.maxConnections=="number"&&Number.isInteger(e.maxConnections)&&e.maxConnections>=1&&e.maxConnections<=1e4?e.maxConnections:Ii;return t.push(`SNOUTPOD_MAX_CONNECTIONS=${Math.max(r,Ii)}`),typeof e.privilegedExtensions=="string"&&/^[a-z][a-z0-9_-]*(,[a-z][a-z0-9_-]*)*$/.test(e.privilegedExtensions)&&t.push(`SNOUTPOD_PRIVILEGED_EXTENSIONS=${e.privilegedExtensions}`),t}function Om(e){let t=null;return()=>(t===null&&(t=n()),t);async function n(){if(e.runtime===null)return[];let r=e.runtime??Sm;return e.runtime!==void 0&&e.runtime!==""?["--runtime",r]:await _m(r,{look:e.lookExecutable})===null?[]:["--runtime",r]}}function Li(e){let t=e.binary??"podman",n=e.exec,r=Om(e),o=e.probe??ji;async function s(m){try{await n(t,["exec",...ee,"--interactive",L(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"--quiet","--file","-"],{timeoutMs:km,stdin:xm()})}catch{}}async function a(){let m=await j(n,t,["ps","--all","--filter",`label=${ie}`,"--format","json"]),p=m.trim()?JSON.parse(m):[],h=new Map,$=new Map;for(let _ of p){let O=_.Labels?.[ie],D=_.Labels?.[je];if(!O||!D)continue;let Y=h.get(O)??[];Y.push(D),h.set(O,Y);let xe=Number(_.Labels?.[Dr]??0),v=Number.isInteger(xe)?xe:0;$.set(O,Math.max($.get(O)??v,v))}let k=p.filter(_=>_.Labels?.[ie]&&!_.Labels?.[je]),A=k.map(_=>Im(_.State)),H=await Dm(k,Nm,(_,O)=>A[O]==="running"?Promise.resolve(!0):e.hasData(_.Labels?.[ie]??""));return k.map((_,O)=>{let D=_.Labels?.[ie]??"",Y=Number(_.Labels?.[Nr]);return{ref:D,status:A[O]??"unknown",generation:Number.isInteger(Y)?Y:null,hasData:H[O]??!1,services:h.get(D)??[],...$.has(D)?{servicesGeneration:$.get(D)}:{}}})}async function d(){let m=await j(n,t,["ps","--all","--filter",`label=${ie}`,"--format","json"]);return(m.trim()?JSON.parse(m):[]).filter(h=>h.Labels?.[ie]&&!h.Labels?.[je]).length}async function i(m){let p=await(e.majors??((h,$)=>Pm(n,t,h,$)))(m.ref,m.image);return $m(m.ref,p)}async function u(m){let p=L(m.ref),h=await i(m);if(h)throw new Error(h);let $=e.credentials,k=await pi(e.repository,$?()=>$.forProject(m.ref,m.bucket):null),A={SNOUTPOD_OWNER_PASSWORD:m.ownerPassword,POSTGRES_PASSWORD:m.adminPassword,...hi(k)},H=[...await r(),"run","--detach","--name",p.pod,"--label",`${ie}=${m.ref}`,"--label",`${Nr}=${m.generation}`,"--userns=auto","--memory",`${m.memoryMb}m`,...jm(m.cpus),"--restart","no",...e.network?["--network",e.network]:[],...m.port?Mr(e.podBind).flatMap(_=>["--publish",`${_}:${m.port}:5432`]):[],...hm(m.services,e.podBind),"--volume",`${p.pod}:/var/lib/postgresql/data:U`,"--env","SNOUTPOD_OWNER_PASSWORD","--env","POSTGRES_PASSWORD","--env",`SNOUTPOD_REF=${m.ref}`,"--env","POSTGRES_USER=snoutpod_admin","--env","POSTGRES_DB=postgres","--env",`SNOUTPOD_S3_BUCKET=${m.bucket}`,"--env",`SNOUTPOD_S3_REGION=${m.region}`,...e.egressDenied?["--env","SNOUTPOD_EGRESS=denied"]:[],...Mm(m.oauth),...Rm(m.tuning).flatMap(_=>["--env",_]),"--env",`SNOUTPOD_RETENTION_FULL=${Oa(m.backup?.retentionDays,m.backup?.fullIntervalHours)}`,...gi(k),m.image,"snoutpod-postgres"];await j(n,t,H,{env:A})}async function l(m){let p=await j(n,t,["ps","--all","--filter",`label=${ie}=${Q(m)}`,"--filter",`label=${je}`,"--format","json"]),h=p.trim()?JSON.parse(p):[],$=[];for(let k of h){let A=k.Labels?.[je];if(!A)continue;let H=Number(k.Labels?.[Dr]??0);$.push({name:A,container:Oi(m,A),spec:k.Labels?.[Ai]??"",pod:k.Labels?.[Ti]??"",running:k.State==="running",generation:Number.isInteger(H)?H:0})}return $}async function f(m){let p=await n(t,["container","inspect","--format","{{.Id}}",L(m).pod]);return p.code===0?p.stdout.trim():""}async function w(m,p){let h=[];for(let _ of p){let O=_.dbUriRole?fn(_.dbUriRole):null;_.dbUriEnv&&O&&!h.includes(O)&&h.push(wt(O))}let $=new Map;if(h.length===0)return $;let k={},A=[],H=h.includes(_e)?[Si]:[];return h.includes(wi)&&H.push(bi),h.forEach((_,O)=>{let D=om(24).toString("base64url"),Y=`SNOUTPOD_SERVICE_PASSWORD_${O}`;$.set(_,D),k[Y]=D,A.push("--env",Y),H.push(`\\getenv service_password_${O} ${Y}`),H.push(`\\set service_role_${O} ${_}`),H.push(`alter role :"service_role_${O}" with password :'service_password_${O}';`)}),H.push(""),await j(n,t,["exec",...ee,"--interactive",...A,L(m.ref).pod,"psql","--username","snoutpod_admin","--dbname",L(m.ref).database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:k,stdin:H.join(`
578
+ `)}),$}async function R(m){let p=m.services??[],h=await l(m.ref),$;p.some(v=>v.smtpSecretEnv)&&($=await e.smtpSecret?.().catch(()=>{}));let k=p.length>0?await f(m.ref):"",A=new Map(h.map(v=>[v.name,v])),H=new Map(p.map(v=>[v.name,lm(v,$)])),_=new Set(p.filter(v=>{let ce=A.get(v.name);return!ce||!ce.running||ce.spec!==H.get(v.name)||k===""||ce.pod!==k}).map(v=>v.name)),O=m.servicesGeneration??0;if(_.size===0&&p.length>0&&!h.some(v=>v.generation>=O))for(let v of p)_.add(v.name);let D=v=>v.dbUriEnv&&v.dbUriRole?fn(v.dbUriRole):null;for(let v=!0;v;){v=!1;let ce=new Set(p.filter(ae=>_.has(ae.name)).map(D));for(let ae of p){let Yr=D(ae);!_.has(ae.name)&&Yr!==null&&ce.has(Yr)&&(_.add(ae.name),v=!0)}}for(let v of h)(_.has(v.name)||!H.has(v.name))&&await j(n,t,["rm","--force","--ignore",v.container]);let Y=p.filter(v=>_.has(v.name)),xe=await w(m,Y);for(let v of Y){let ce=Oi(m.ref,v.name),ae=[...await r(),"run","--detach","--name",ce,"--label",`${ie}=${m.ref}`,"--label",`${Nr}=${m.generation}`,"--label",`${je}=${v.name}`,"--label",`${Dr}=${m.servicesGeneration??0}`,"--label",`${Ai}=${H.get(v.name)??""}`,"--label",`${Ti}=${k}`,"--network",`container:${L(m.ref).pod}`,"--userns",`container:${L(m.ref).pod}`,"--memory",`${v.memoryMb}m`,"--restart","no",...wm(v),...Di(Ri(m,v,xe,$)),v.image];await j(n,t,ae,{env:Ri(m,v,xe,$)})}}return{list:a,preflight:i,create:u,createServices:R,async start(m){await j(n,t,["start",L(m).pod])},async startServices(m){let p=(await l(m)).map(h=>h.container);p.length>0&&await j(n,t,["start",...p])},async applyHostServices(m){let p=[],h=await j(n,t,["ps","--all","--filter",`label=${yt}`,"--format","json"]),$=h.trim()?JSON.parse(h):[],k=new Map;for(let _ of $){let O=_.Labels?.[yt];O&&k.set(O,{spec:_.Labels?.[Ni]??"",state:_.State??""})}for(let _ of m)try{await H(_)}catch(O){p.push({name:_.name,action:"failed",reason:O instanceof Error?O.message:String(O)})}async function A(_){let O=await r();if((await n(t,[...O,"image","exists",_])).code===0)return;let D=await n(t,[...O,"pull","--quiet",_]);if(D.code===0)return;let Y=vm(_);if(!Y||!e.registryLogin)throw new Error(`${_} could not be pulled: ${(D.stderr||D.stdout).trim()}`);await e.registryLogin(Y),await j(n,t,[...O,"pull","--quiet",_])}async function H(_){let O=e.hostServiceLocal?.(_,m),D=O?{..._,...O.env&&Object.keys(O.env).length>0?{env:{..._.env,...O.env}}:{},...O.volumes?{volumes:O.volumes}:{},...O.network?{network:O.network}:{}}:_,Y=pn(D.name),xe=gm(D),v=k.get(D.name);if(k.delete(D.name),v&&v.spec===xe){v.state!=="running"&&(await j(n,t,["start",Y]),p.push({name:D.name,action:"started",reason:v.state}));return}let ce=v?"the spec changed":"it was not here";await A(D.image),v&&await j(n,t,["rm","--force","--ignore",Y]),await j(n,t,[...await r(),"run","--detach","--name",Y,"--label",`${yt}=${D.name}`,"--label",`${Ni}=${xe}`,...D.healthPath?["--label",`${Ur}=${D.hostPort}:${D.healthPath}`]:[],"--memory",`${D.memoryMb}m`,"--restart","no",...D.network?["--network",D.network]:e.network?["--network",e.network]:[],...(D.volumes??[]).flatMap(ae=>["--volume",ae]),...Mr(e.podBind).flatMap(ae=>["--publish",`${ae}:${D.hostPort}:${D.port}`]),...D.adminPort&&D.adminHostPort?["--publish",`127.0.0.1:${D.adminHostPort}:${D.adminPort}`]:[],...Di(D.env),D.image,...D.command??[]],{env:{...D.env}}),p.push({name:D.name,action:v?"recreated":"created",reason:ce})}for(let _ of k.keys())await j(n,t,["rm","--force","--ignore",pn(_)]),p.push({name:_,action:"removed",reason:"it is no longer on the list"});return p},async hostServiceHealth(m){let p=await j(n,t,["ps","--all","--filter",`label=${yt}`,"--format","json"]),h=(p.trim()?JSON.parse(p):[]).map(k=>k.Labels?.[yt]??"").filter(k=>k!=="");if(h.length===0)return[];let $=Date.now()+m;for(;;){let k=[];for(let A of h)k.push(await Ci(n,t,A,pn(A),o));if(k.every(A=>A.ok)||Date.now()>=$)return k;await new Promise(A=>setTimeout(A,500))}},async serviceHealth(m,p){let h=await l(m);if(h.length===0)return[];let $=Date.now()+p;for(;;){let k=await Promise.all(h.map(A=>Ci(n,t,A.name,A.container,o)));if(k.every(A=>A.ok)||Date.now()>=$)return k;await new Promise(A=>setTimeout(A,500))}},async stop(m,p){for(let h of await l(m))await j(n,t,["stop","--time",String(dm),h.container]);await j(n,t,["stop","--time",String(p),L(m).pod])},async remove(m){for(let p of await l(m))await j(n,t,["rm","--force","--ignore",p.container]);await j(n,t,["rm","--force","--ignore","--volumes",L(m).pod])},async removeData(m){await j(n,t,["volume","rm","--force",L(m).pod])},async applyOwnerPassword(m){let p=["\\getenv owner_password SNOUTPOD_OWNER_PASSWORD",`\\set owner_role ${m.ref}_owner`,`alter role :"owner_role" with password :'owner_password';`,""].join(`
579
+ `);await j(n,t,["exec",...ee,"--interactive","--env","SNOUTPOD_OWNER_PASSWORD",L(m.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_OWNER_PASSWORD:m.ownerPassword},stdin:p})},async applyRolePassword(m,p,h){let $=["\\getenv role_password SNOUTPOD_ROLE_PASSWORD",`\\set target_role ${wt(p)}`,`alter role :"target_role" with password :'role_password';`,""].join(`
580
+ `);await j(n,t,["exec",...ee,"--interactive","--env","SNOUTPOD_ROLE_PASSWORD",L(m).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{env:{SNOUTPOD_ROLE_PASSWORD:h},stdin:$})},async updateExtensions(m){let p=L(m),h=await j(n,t,["exec",...ee,"--interactive",p.pod,"psql","--username","snoutpod_admin","--dbname",p.database,"--quiet","-tA","-F",Rr,"-v","ON_ERROR_STOP=1","--file","-"],{stdin:xi});return ki(h)},async applyDbAccess(m,p){let h=L(m),$=await j(n,t,["exec",...ee,"--interactive","--env",mn,h.pod,"psql","--username","snoutpod_admin","--dbname",h.database,"--quiet","-tA","-v","ON_ERROR_STOP=1","--file","-"],{env:{[mn]:si(m,p)},stdin:ri,timeoutMs:6e4});return oi($)},async runProjectSql(m,p){let h=L(m);await j(n,t,["exec",...ee,"--interactive",h.pod,"psql","--username","snoutpod_admin","--dbname",h.database,"--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:p})},async ensureHostServiceDatabase(m,p,h){let $=pn(m),k=wt(p),A=wt(h),H=O=>["exec",$,"psql","--username",k,"--dbname","postgres","--quiet","-tA","-v","ON_ERROR_STOP=1","-c",O];(await j(n,t,H(`select 1 from pg_database where datname = '${A}'`))).trim()!=="1"&&await j(n,t,H(`create database "${A}"`))},async replicationSlots(m){let p=await j(n,t,["exec",...ee,L(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tA","-F","|","-c",za]);return Va(p)},async dropReplicationSlot(m,p){if(!/^[a-z0-9_]{1,63}$/.test(p))throw new Error(`not a replication slot name: ${JSON.stringify(p)}`);await j(n,t,["exec",...ee,L(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-v","ON_ERROR_STOP=1","-c",`select pg_catalog.pg_drop_replication_slot('${p}'::pg_catalog.name)`])},async renameProject(m,p){p!==m.ref&&await j(n,t,["exec",...ee,"--interactive",L(m.ref).pod,"psql","--username","snoutpod_admin","--dbname","postgres","--quiet","-v","ON_ERROR_STOP=1","--file","-"],{stdin:Cm(p,m.ref)})},async waitReady(m,p){let h=L(m).pod,$=Date.now()+p;for(;;){let k=await n(t,["exec",...ee,h,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname","postgres","-tAc","select not pg_is_in_recovery()"]);if(k.code===0&&k.stdout.trim()==="t")return s(m),!0;if(Date.now()>=$)return!1;await new Promise(A=>setTimeout(A,500))}},async capacity(){let[m,p]=await Promise.all([d(),e.capacity()]);return{pods:m,...p}},async databaseBytes(m){let p=await n(t,["exec",...ee,L(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tAc","select pg_database_size(current_database())"]);return p.code===0?kr(p.stdout):null},async storageBytes(m){let p=await n(t,["exec",...ee,L(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tAc","select coalesce(sum((metadata->>'size')::bigint), 0) from storage.objects"]);return p.code===0?kr(p.stdout):null},async setReadOnly(m,p){let h=L(m);await j(n,t,["exec",...ee,h.pod,"psql","--username","snoutpod_admin","--dbname","postgres","-tAc",`alter database ${h.database} set default_transaction_read_only = ${p?"on":"off"}`])},async archiveState(m){let p=await n(t,["exec",...ee,L(m).pod,"psql","--host","127.0.0.1","--username","snoutpod_admin","--dbname",m,"-tA","-F","|","-c",`select coalesce(to_char(last_archived_time, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF'), ''), case when last_failed_time > coalesce(last_archived_time, '-infinity') then failed_count else 0 end from pg_stat_archiver`]);if(p.code!==0)return null;let[h="",$=""]=p.stdout.trim().split(`
581
+ `)[0]?.split("|")??[],k=Number($);return{lastArchivedAt:h.trim()||null,failures:Number.isInteger(k)&&k>=0?k:0}}}}async function Ci(e,t,n,r,o=ji){let s=await e(t,["inspect","--format","json",r]);if(s.code!==0)return{name:n,ok:!1,detail:"no such container"};let a,d;try{let f=JSON.parse(s.stdout);a=f[0]?.State,d=f[0]?.Config?.Labels}catch{return{name:n,ok:!1,detail:"podman inspect could not be read"}}let i=a?.Status??"unknown";if(i!=="running")return{name:n,ok:!1,detail:i};let u=bm(d?.[Ur]);if(!u)return{name:n,ok:!0,detail:"running"};let l=await o(u.port,u.path);return l===null?{name:n,ok:!1,detail:"not answering yet"}:{name:n,ok:!0,detail:`answered ${l}`}}async function Dm(e,t,n){let r=new Array(e.length),o=0;async function s(){for(;o<e.length;){let a=o++;r[a]=await n(e[a],a)}}return await Promise.all(Array.from({length:Math.min(Math.max(1,t),e.length)},s)),r}function Im(e){switch((e??"").toLowerCase()){case"running":return"running";case"created":case"configured":return"starting";case"exited":case"stopped":return"stopped";default:return"unknown"}}function Cm(e,t){return[`\\set from_db ${e}`,`\\set to_db ${t}`,`\\set from_role ${e}_owner`,`\\set to_role ${t}_owner`,'alter database :"from_db" rename to :"to_db";','alter role :"from_role" rename to :"to_role";',"alter system reset restore_command;","alter system reset recovery_target_time;","alter system reset recovery_target_action;",""].join(`
582
+ `)}function Mm(e){if(!e?.issuer)return[];try{return["--env",`${Ei}=${$i(e.issuer)}`]}catch{return[]}}function jm(e){return typeof e!="number"||!Number.isFinite(e)||e<=0?[]:["--cpus",e.toFixed(2)]}var ie,Nr,je,Dr,Ai,Ti,dm,um,mm,yt,Ni,ym,Mi,Ur,ji,Sm,ee,km,Ii,Nm,Or=I(()=>{"use strict";Se();xr();Pr();ht();yi();vr();_i();vi();Ar();Pi();ie="snoutpod.ref",Nr="snoutpod.generation",je="snoutpod.service",Dr="snoutpod.services-generation",Ai="snoutpod.service-spec",Ti="snoutpod.service-pod";dm=10,um=/^[a-z][a-z0-9-]{0,30}$/;mm=/^[a-z][a-z0-9_]{0,62}$/;yt="snoutpod.host-service",Ni="snoutpod.host-service-spec";ym=/^[A-Za-z_][A-Za-z0-9_]*$/;Mi=/^\/[A-Za-z0-9\/_\-.~%?=&]*$/,Ur="snoutpod.health";ji=async(e,t)=>{let{request:n}=await import("node:http");return new Promise(r=>{let o=n({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:2e3},s=>{s.resume(),r(s.statusCode??null)});o.on("error",()=>r(null)),o.on("timeout",()=>{o.destroy(),r(null)}),o.end()})},Sm="crun";ee=["--env","PGOPTIONS=-c search_path=pg_catalog,pg_temp"],km=3e4;Ii=200;Nm=8});var Qi={};Xr(Qi,{DEFAULT_LOCAL_MEMORY_MB:()=>Gi,DEFAULT_LOCAL_PORT:()=>Hm,ENGINE:()=>B,ENGINE_NAME:()=>Br,IMAGE:()=>qr,NETWORK:()=>Re,NEW_PROJECT_MAJOR:()=>Gr,NO_BLACKHOLE_ROUTES:()=>Wi,PUBLIC_IMAGE:()=>Jm,PUBLISHED_MAJORS:()=>Ze,capacity:()=>Ji,chooseLocalImage:()=>_t,dropIfUnused:()=>Xi,ensureImage:()=>Wm,ensureNetwork:()=>Zm,exec:()=>V,hasData:()=>wn,localImageFor:()=>Ki,localPsql:()=>tf,localRuntime:()=>Xm,localSpec:()=>Yi,localStatus:()=>Kr,localUri:()=>Vi,networkPolicyInPlace:()=>zi,newLocalPassword:()=>qm,newLocalRef:()=>Gm,podLog:()=>Zi,podName:()=>Qe,podmanReady:()=>Vm,removeLocal:()=>of,staleImage:()=>Hr,startLocal:()=>ef,stopLocal:()=>rf,volumePgVersion:()=>gn});import{randomBytes as Bi}from"node:crypto";import{existsSync as Ui}from"node:fs";import{platform as yn}from"node:os";import{delimiter as Lm,join as Um}from"node:path";function Fm(){if(process.env.SNOUTPOD_ENGINE)return process.env.SNOUTPOD_ENGINE;let e=yn()==="win32"?["podman.exe","podman"]:["podman"];for(let t of(process.env.PATH??"").split(Lm))if(t&&e.some(n=>Ui(Um(t,n))))return"podman";return Ui("/opt/podman/bin/podman")?"/opt/podman/bin/podman":"podman"}function _t(e,t=process.env.SNOUTPOD_IMAGE){if(t)return{name:t,published:null,major:null};if(e===null)return St(Gr);let n=Number(Fr(e));if(!Ze.includes(n)){let r=e.trim()||"(empty)",o=Ze.map(s=>St(s).published).join(" or ");throw new Error(`its data directory's PG_VERSION says ${r}, and there is an image only for Postgres ${Ze.join(" and ")}. Set SNOUTPOD_IMAGE to an image of that major (${o} for those).`)}return St(n)}function St(e){return{name:`${Bm}:${e}`,published:`${Hi}:${e}`,major:e}}function Gm(){return Aa(e=>Bi(e))}function qm(){return Bi(18).toString("base64url")}async function Wm(e,t=V,n=_t(null)){let r=n.published;if(!r)return null;if((await t(B,["image","exists",n.name])).code===0)return await zm(e,t,n.name,r),null;e?.(`Fetching the database image (${r}). This happens once.`);let o=await t(B,["pull",r],{timeoutMs:qi}).catch(a=>({code:1,stdout:"",stderr:a instanceof Error?a.message:String(a)}));if(o.code!==0)return`could not fetch the database image ${r}: ${o.stderr.trim()||o.stdout.trim()||"the pull failed"}`;let s=await t(B,["tag",r,n.name]);return s.code!==0?`fetched ${r} but could not tag it as ${n.name}: ${s.stderr.trim()}`:(e?.("Got it."),null)}async function zm(e,t,n,r){let o=await t(B,["image","inspect","--format","{{.Id}}",n,r]).catch(()=>null),[s,a]=o?.code===0?o.stdout.trim().split(`
583
+ `):[];if(!s||s!==a)return;let d=setTimeout(()=>e?.(`Fetching a newer database image (${r}).`),3e3),i=await t(B,["pull",r],{timeoutMs:qi}).catch(w=>({code:1,stdout:"",stderr:w instanceof Error?w.message:String(w)})).finally(()=>clearTimeout(d));if(i.code!==0){let w=(i.stderr.trim()||i.stdout.trim()||"the pull failed").split(`
584
+ `).at(-1);e?.(`Could not check for a newer database image (${w}); using the one on this machine.`);return}let u=await t(B,["image","inspect","--format","{{.Id}}",r]).catch(()=>null),l=u?.code===0?u.stdout.trim():"";if(!l||l===s)return;let f=await t(B,["tag",r,n]);if(f.code!==0){e?.(`Fetched a newer ${r} but could not tag it as ${n}: ${f.stderr.trim()}`);return}e?.(`Fetched a newer database image (${r}).`)}async function Vm(e=Km,t=qr){if(Br!=="podman")return`SNOUTPOD_ENGINE=${Br}: a local project is Podman-only, because a database that works under Docker and not under Podman is a database that works here and not in the cloud`;let n;try{n=await V(B,["--version"])}catch{return`${B} is not installed. macOS: brew install podman, then podman machine init && podman machine set --rootful=false && podman machine start. Windows: winget install RedHat.Podman, then the same three machine commands. Linux: your distribution's podman package.`}if(n.code!==0)return`${B} --version failed: ${n.stderr.trim()||n.stdout.trim()}`;if((await V(B,["image","exists",t])).code!==0)return`the pod image ${t} is not on this machine. ${e}`;let o=await V(B,["machine","inspect","--format","{{.Rootful}}"]);return o.code===0&&o.stdout.trim().startsWith("true")?"the podman machine is ROOTFUL, and the hosts are not: podman machine stop && podman machine set --rootful=false && podman machine start":null}function Ym(e){let t=`if out=$(ls -A ${JSON.stringify(e)} 2>/dev/null); then if [ -n "$out" ]; then echo DATA; else echo EMPTY; fi; else echo HIDDEN; fi`;return V(B,["machine","ssh",t]).then(n=>{let r=n.stdout.trim().split(`
585
+ `).at(-1);return r==="DATA"?"data":r==="EMPTY"?"empty":"hidden"},()=>"hidden")}function wn(e){let t=yn()==="darwin"||yn()==="win32";return li(V,e,{binary:B,look:t?Ym:void 0})}async function gn(e,t=V,n=yn()){let r=L(e).pod;if((await t(B,["volume","exists",r])).code!==0)return null;let o;if(n==="darwin"||n==="win32"){let s=null;for(let a of Ze){let d=St(a).name;if((await t(B,["image","exists",d])).code===0){s=d;break}}if(!s)throw new Error(`${r} holds a database and there is no pod image on this machine to read its Postgres version with. Set SNOUTPOD_IMAGE to the image it was made with (${Ze.map(a=>St(a).published).join(" or ")}).`);o=await t(B,["run","--rm","--pull=never","--network=none","--userns=host","--user=0","--entrypoint=cat","--volume",`${r}:/data:ro`,s,"/data/PG_VERSION"])}else{let s=await t(B,["volume","inspect","--format","{{.Mountpoint}}",r]),a=s.code===0?s.stdout.trim().replace(/\/+$/,""):"";if(!a)throw new Error(`could not find where ${r} is: ${s.stderr.trim()||"no mountpoint"}`);o=await t(B,["unshare","cat",`${a}/PG_VERSION`])}if(o.code===0)return o.stdout;if(/no such file/i.test(o.stderr))return null;throw new Error(`could not read the Postgres version in ${r}: ${o.stderr.trim()||o.stdout.trim()||`exit ${o.code}`}`)}function Ki(e,t=gn){if(!e||process.env.SNOUTPOD_IMAGE)return Promise.resolve(_t(null));Q(e);let n=t===gn?hn.get(e):void 0;if(n)return n;let r=t(e).then(o=>{try{return _t(o)}catch(s){throw new Error(`${Qe(e)}: ${s instanceof Error?s.message:String(s)}`)}});return t===gn&&(hn.set(e,r),r.catch(()=>hn.delete(e))),r}async function Ji(){return{ramFreeMb:4096,ramTotalMb:8192,diskFreeMb:32768,diskTotalMb:65536}}function Xm(e){return Li({exec:V,binary:B,network:Re,hasData:wn,capacity:Ji,repository:e})}function Qe(e){return L(e).pod}async function Zm(e){if(process.env.SNOUTPOD_LOCAL_ALLOW_EGRESS==="1"){await Qm()||await V(B,ln({name:Re}));return}let t=ln({name:Re,allow:[]}),n="";for(let r=0;r<10;r+=1){if(await zi())return;await e?.(),await V(B,["network","rm","--force",Re]).catch(()=>{});let o=await V(B,t);if(o.code===0)return;if(n=`${o.stderr}${o.stdout}`.trim(),/invalid route gateway/i.test(n))throw new Error(`${Wi}: ${n}`);await new Promise(s=>setTimeout(s,500))}throw new Error(`could not create ${Re} with egress blocked${n?`: ${n}`:""}`)}async function Qm(){return(await V(B,["network","inspect",Re,"--format","json"])).code===0}async function zi(){let e=await V(B,["network","inspect",Re,"--format","json"]);if(e.code!==0)return!1;try{return Ja(JSON.parse(e.stdout)?.[0])}catch{return!1}}function Vi(e){let t=L(e.ref);return`postgres://${t.ownerRole}:${encodeURIComponent(e.ownerPassword)}@127.0.0.1:${e.port}/${t.database}?sslmode=disable`}function Yi(e,t={}){return Q(e.ref),{ref:e.ref,generation:1,image:t.image??qr,memoryMb:t.memoryMb??Gi,cpus:null,bucket:"",region:"us-east-1",ownerPassword:e.ownerPassword,adminPassword:e.adminPassword,port:e.port,tuning:t.tuning??null,backup:null,services:t.services??null}}function bt(e,t){e.onProgress?.(t)}async function ef(e,t,n={}){let r=n.image??(await Ki(t.ref,n.pgVersion)).name,o=Yi(t,{...n,image:r}),s=n.timeoutMs??12e4,a=n.hasData??wn,d=(await e.list()).find(u=>u.ref===t.ref),i=null;if(d?d.status==="stopped"&&(i=await(n.staleImage??Hr)(t.ref,r))?(bt(n,"Starting the database on the newer image. Its data stays as it is."),await e.remove(t.ref),await e.create(o),await(n.dropImage??Xi)(i)):d.status==="stopped"?(bt(n,"Starting the database."),await e.start(t.ref),await e.startServices(t.ref)):(bt(n,"The database is already running."),d.status==="running"&&await(n.staleImage??Hr)(t.ref,r)&&bt(n,"A newer database image is on this machine. To move onto it: snoutdata stop, then snoutdata start.")):(bt(n,await a(t.ref)?"Starting the database over its existing data.":"Creating the database."),await e.create(o)),!await e.waitReady(t.ref,s))throw new Error(await nf(t.ref,s,n.podLog??Zi));return await e.applyOwnerPassword(o),await e.createServices(o),Kr(e,t,n)}function Fi(e){return e.trim().replace(/^sha256:/,"")}async function Hr(e,t){let[n,r]=await Promise.all([V(B,["container","inspect","--format","{{.Image}}",Qe(e)]).catch(()=>null),V(B,["image","inspect","--format","{{.Id}}",t]).catch(()=>null)]);if(n?.code!==0||r?.code!==0)return null;let o=Fi(n.stdout),s=Fi(r.stdout);return o&&s&&o!==s?o:null}async function Xi(e){let t=await V(B,["image","inspect","--format","{{len .RepoTags}}",e]).catch(()=>null);t?.code===0&&t.stdout.trim()==="0"&&await V(B,["rmi",e]).catch(()=>{})}async function tf(e,t){let n=L(e.ref);return V(B,["exec","--interactive","-e","PGPASSWORD",Qe(e.ref),"psql","--host","127.0.0.1","--port","5432","--username",n.ownerRole,"--dbname",n.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA","--file","-"],{stdin:t,env:{PGPASSWORD:e.ownerPassword}})}async function Zi(e){let t=await V(B,["logs","--tail","20",Qe(e)]).catch(()=>null);return t?`${t.stdout}${t.stderr}`:"(no log: the container is not there)"}async function nf(e,t,n){let r=await n(e).catch(()=>"(no log)");return`${Qe(e)} never became ready in ${t}ms
586
586
  --- pod log ---
587
- ${r}`}async function Ym(e,t,n={}){Z(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,ni),Gr(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function Xm(e,t,n){Z(t),await e.remove(t),n&&(await e.removeData(t),gn.delete(t))}async function Gr(e,t,n={}){Z(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??yn;return{ref:t.ref,exists:!!r,status:r?.status??"absent",ready:o?await e.waitReady(t.ref,1):!1,hasData:r?.hasData??await s(t.ref),port:t.port,databaseBytes:o?await e.databaseBytes(t.ref):null,services:r?.services??[],uri:t.port>0&&t.ownerPassword?Ki(t):null}}var Ur,B,Br,ze,Im,Li,Hr,Ae,Cm,Ui,z,Lm,Um,Fi,gn,Gi,Yi=D(()=>{"use strict";ri();ht();ai();kr();Rr();ye();Ur=process.env.SNOUTPOD_ENGINE??"podman";B=Dm(),Br=18,ze=[17,18],Im="snoutpod/postgres",Li="ghcr.io/snoutdata/snoutpod-postgres";Hr=_t(null).name,Ae=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",Cm=54322,Ui=512,z=oi();Lm="cd packages/snoutpod && npm run image:build",Um=`${Li}:${Br}`;Fi=20*60*1e3;gn=new Map;Gi="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});ne();var _n=[{name:"init",summary:"Create or link a project for this folder, and print its DATABASE_URL or write it to .env",flags:["--name","--region","--env","--ref"]},{name:"login",summary:"Sign in through a browser, with --sso through your company's identity provider, or with --device by typing a code into one anywhere",flags:["--email","--provider","--sso","--domain","--no-browser","--device","--timeout"]},{name:"logout",summary:"Forget the stored session",flags:[]},{name:"whoami",summary:"Who this credential belongs to, and whether it is the account login --email asked for",flags:[]},{name:"upgrade",summary:"Install the newest CLI the way this one was installed (binary or npm), checksum verified",flags:["--check"]},{name:"tokens list",summary:"Long-lived access tokens on this account",flags:[]},{name:"tokens create",summary:"Mint an sdt_ token for CI or an agent",flags:["--name","--expires","--project"]},{name:"tokens revoke",summary:"Revoke one by id or prefix",flags:[]},{name:"projects list",summary:"Every project on this account",flags:[]},{name:"projects create",summary:"Make a hosted database, and print its ref",flags:["--name","--region","--team","--no-wait","--timeout","--show-url"]},{name:"projects pause",summary:"Stop a project, and wait until it is paused",flags:["--ref","--no-wait","--timeout"]},{name:"projects resume",summary:"Start a paused project, and wait until it is ready",flags:["--ref","--no-wait","--timeout"]},{name:"projects delete",summary:"Delete a project, and wait until it is gone",flags:["--ref","--no-wait","--timeout"]},{name:"projects show",summary:"One project: state, products (Realtime included), functions, secrets, domains",flags:["--ref"]},{name:"products",summary:"Auth, storage, the data API and push: on or off. Realtime is always on, and listed",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage, data-api or push on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage, data-api or push off",flags:["--ref"]},{name:"push credentials",summary:"Push keys: what is set for APNs, FCM and Web Push (never the keys)",flags:["--ref"]},{name:"push credentials set",summary:"Set the APNs key (a .p8) or the FCM service account, checked before it is stored",flags:["--ref","--p8","--key-id","--team-id","--topic","--environment","--file"]},{name:"push credentials remove",summary:"Remove the APNs or FCM credentials",flags:["--ref"]},{name:"auth",summary:"Auth settings: Google sign-in and redirect addresses",flags:["--ref"]},{name:"auth google",summary:"Sign in with Google, with your own client (secret on stdin)",flags:["--ref","--client-id","--stdin"]},{name:"auth anonymous",summary:"Guest sign-in on or off: signInAnonymously() gives a browser a session with no email or password",flags:["--ref"]},{name:"auth redirects",summary:"The site URL and the addresses a sign-in may return to",flags:["--ref","--site-url","--allow"]},{name:"auth templates",summary:"The five auth emails, ours or your own",flags:["--ref"]},{name:"auth template",summary:"Use your own subject and HTML for one, or reset it",flags:["--ref","--file","--subject"]},{name:"realtime inspect",summary:"The channels open now: each client, its presence, when it was last heard, and the last minute of messages",flags:["--channel","--watch","--ref"]},{name:"realtime logs",summary:"The connection log: connects, joins, leaves and disconnects, with the reason each ended",flags:["--since","--channel","--ref"]},{name:"domains",summary:"Your own domains in front of the project API",flags:["--ref"]},{name:"domains add",summary:"Add one, and print the DNS records to publish",flags:["--ref"]},{name:"domains verify",summary:"Check its records and verify it",flags:["--ref"]},{name:"domains remove",summary:"Stop serving it",flags:["--ref"]},{name:"functions deploy",summary:"Put a folder of TypeScript on the edge",flags:["--ref","--dir","--entrypoint","--no-verify-jwt"]},{name:"functions list",summary:"What this project has deployed",flags:["--ref"]},{name:"functions size",summary:"Its memory and workers, within the plan",flags:["--ref","--memory","--concurrency","--reset"]},{name:"functions delete",summary:"Remove one",flags:["--ref"]},{name:"secrets set",summary:"Set the environment functions run with",flags:["--ref","--stdin"]},{name:"secrets list",summary:"The names, never the values",flags:["--ref"]},{name:"secrets unset",summary:"Remove one",flags:["--ref"]},{name:"keys",summary:"The anon and service_role API keys for a project",flags:["--ref"]},{name:"keys rotate",summary:"New keys, breaking every one already issued",flags:["--ref","--force"]},{name:"teams",summary:"Teams this account belongs to",flags:[]},{name:"link",summary:"Point this folder at a project",flags:["--ref"]},{name:"usage",summary:"Size and compute against the plan limit",flags:["--ref","--days","--history"]},{name:"start",summary:"A Postgres on this machine, with your migrations applied",flags:["--port","--dir","--no-migrations","--out-of-order"]},{name:"stop",summary:"Stop the local database. The data stays",flags:[]},{name:"status",summary:"What the local database is doing",flags:[]},{name:"gen types typescript",summary:"The schema as a TypeScript Database type, on stdout",flags:["--ref","--db-url","--local","--schema","--out"]},{name:"db url",summary:"A connection string",flags:["--ref"]},{name:"db psql",summary:"Open psql, or run statements after --",flags:["--ref"]},{name:"db reset-password",summary:"Rotate the project password",flags:["--ref"]},{name:"db access",summary:"Who signs in to the database as themselves with OAuth (Postgres 18), at which level, as which role",flags:["--ref"]},{name:"db access grant",summary:"Let a person sign in to the database as themselves, and print their connection string",flags:["--ref","--level"]},{name:"db access revoke",summary:"Take it away, by email or role; a token already issued can work until it expires (up to 1 hour)",flags:["--ref"]},{name:"db export",summary:"Take or download a dump",flags:["--ref","--out","--status","--timeout"]},{name:"db push",summary:"Apply migrations from a folder",flags:["--dir","--dry-run","--out-of-order"]},{name:"db restore",summary:"Put a dump into a project, or rewind it to a moment into a new project",flags:["--file","--force","--at","--name","--window","--ref"]},{name:"mcp",summary:"Serve these operations as tools over stdio",flags:["--allow-delete"]}],Xr={"--ref":"The project. Defaults to SNOUTDATA_PROJECT, then .snoutdata/project.json here or in a parent","--name":"The project's name","tokens create --name":"What the token is for and where it lives, shown in tokens list","db restore --name":"The name of the new project the restore makes","--region":"Where the project runs. Defaults to the nearest","--env":"Write DATABASE_URL into .env in this folder, and add .env to .gitignore since it holds the password. Without it the URL is printed","--email":"The account to sign in as. The browser offers it, and a different account is reported at once and by whoami","--provider":"google (the default) or github","--sso":"Sign in with your company's identity provider","--domain":"With --sso: your work email or company domain. Only the domain is sent","--no-browser":"Print the sign-in URL instead of opening a browser","--device":"Sign in by typing a code into a browser on any machine: SSH, containers, agents","--timeout":"Seconds to wait before giving up","--check":"Say whether a newer version exists, and install nothing","--expires":"Days until the token stops working. Without it, it works until revoked","--project":"Limit the token to one project ref","--team":"Create it in a team (name or id), shared with its members","--no-wait":"Return as soon as it is asked for, without waiting for it to be true","--show-url":"Also print the connection string, which holds the password. Otherwise db url prints it when wanted","--p8":"The APNs .p8 key file (a path, never the key itself)","--key-id":"The APNs key id","--team-id":"Your Apple developer team id","--topic":"The app's bundle id","--environment":"production or sandbox","--file":"A file to read","push credentials set --file":"The FCM service-account JSON","auth template --file":"The email's HTML body","db restore --file":"The dump to restore (from db export or pg_dump -Fc)","--client-id":"Your Google OAuth client id; the secret is read from stdin with --stdin","--stdin":"Read the secret value from a pipe, so it is not in shell history","--site-url":"Where sign-in emails send people by default","--allow":"Comma-separated addresses a sign-in may return to","--subject":"The email's subject line","--dir":"The folder to read from","db push --dir":"The migrations folder (default ./migrations)","start --dir":"The migrations folder (default ./migrations)","functions deploy --dir":"The function's folder (default ./functions/<name>)","--entrypoint":"The file the runtime starts, when it is not index.ts","--no-verify-jwt":"Let anybody with the URL call it (webhooks). By default a caller needs a project key or a user session","--memory":"Memory per worker, in MB, within the plan","--concurrency":"How many workers it may run at once, within the plan","--reset":"Back to the plan's default size","--force":"Do it although it cannot be undone","--days":"How many days of history","--history":"Print the day-by-day table too","--port":"The port the local database listens on (default 54322)","--no-migrations":"Start the database and apply nothing","--out-of-order":"Apply a new migration that sorts before one already applied","--db-url":"Read a postgres:// database instead of a project","--local":"Read the database snoutdata start is running","--schema":"Comma-separated schemas (default public)","--out":"Write to this file instead of stdout","db export --out":"Where to save the dump","--status":"Show the last export without taking another","--dry-run":"Show what would run, and change nothing","--at":"The moment to restore to (an ISO time), into a NEW project","--window":"How far back a point-in-time restore can go","--allow-delete":"Let the agent's delete_project actually delete","--channel":"Only this channel","--since":"How far back, like 10m, 2h or 1d (default 1h)","--watch":"Keep printing what changes until interrupted","--level":"full (everything the project password can do) or read (reads the project's own tables, never the auth or storage schemas, and writes nothing). Default read"};function xn(e,t){return Xr[`${e} ${t}`]??Xr[t]??""}J();ee();q();import{spawn as wl}from"node:child_process";function bl(e){return`snout-${e}`}function Be(e,t,n){return new Promise((r,o)=>{let s=wl("docker",["compose","-p",bl(e.ref),"--project-directory",e.folder,...t],{cwd:e.folder,stdio:["ignore","pipe","pipe"]}),a="",l="";s.stdout.on("data",i=>{a+=String(i)}),s.stderr.on("data",i=>{if(l+=String(i),n)for(let u of String(i).split(/\r?\n/).filter(Boolean))c(b(` ${u.trim()}`))}),s.on("error",i=>{if(i.code==="ENOENT"){o(new N("tool-missing","docker is not installed, or not on PATH. A local project runs in Docker (Docker Desktop on Windows and macOS)."));return}o(new N("failed",String(i)))}),s.on("close",i=>r({code:i??0,out:a,err:l}))})}async function Sl(e){let t=await Be(e,["ps","-a","--format","json"],!1);if(t.code!==0)throw new N("failed",t.err.trim()||"docker compose ps failed. Is Docker running?");let n=t.out.trim();return(n.startsWith("[")?JSON.parse(n):n.split(/\r?\n/).filter(Boolean).map(o=>JSON.parse(o))).map(o=>({service:o.Service??"?",state:o.State==="exited"&&o.ExitCode===0?"done":o.State??"?",health:o.Health??""})).sort((o,s)=>o.service.localeCompare(s.service))}async function Bo(e){let t=await Sl(e),n=t.filter(o=>o.state==="running").length,r=t.filter(o=>o.state!=="done").length;return g({ref:e.ref,local:e.folder,api:e.apiUrl,dbPort:e.dbPort,running:n,services:t},()=>{if(c(`${k(e.name??e.ref)} (local, ${e.folder})`),t.length===0){c("Not started. `snoutdata start` brings it up.");return}process.stdout.write(`${F([["SERVICE","STATE","HEALTH"],...t.map(o=>[o.service,o.state,o.health])])}
588
- `),c(`${n} of ${r} services running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}.`)}),0}async function Ho(e){c(`Starting ${e.name??e.ref} (local)\u2026`);let t=await Be(e,["up","-d","--wait","--wait-timeout","600"],!0);if(t.code!==0)throw new N("failed",`docker compose up failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,api:e.apiUrl,dbPort:e.dbPort,started:!0},()=>{c(`Running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}. \`snoutdata db url\` prints the connection string.`)}),0}async function Go(e){let t=await Be(e,["stop"],!0);if(t.code!==0)throw new N("failed",`docker compose stop failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,stopped:!0},()=>c(`Stopped ${e.name??e.ref}. Its data is kept; \`snoutdata start\` brings it back.`)),0}function qo(e){g({ref:e.ref,name:e.name,local:e.folder,api:e.apiUrl,dbPort:e.dbPort},()=>{process.stdout.write(`${F([["name",e.name??"(not set up by Studio)"],["ref",e.ref],["where",`local, ${e.folder}`],["api",e.apiUrl],["database",`127.0.0.1:${e.dbPort}`]])}
589
- `)})}tt();import{cpSync as Jo,existsSync as dt,readdirSync as Pl,readFileSync as zo,rmSync as Vo,statSync as Rl,writeFileSync as Yo}from"node:fs";import{join as oe,resolve as qt}from"node:path";var _l=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function xl(e){return _l.test(e)}function Te(e){if(!xl(e))throw new Error(`not a function name: ${JSON.stringify(e)}. Letters, digits, hyphens and underscores, up to 48 characters, starting with a letter or a digit.`);return e}var vl=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function kl(e){if(e.length===0||e.length>255||e.includes("\\")||e.startsWith("/"))return!1;let t=e.split("/");return t.length>8?!1:t.every(n=>n!=="."&&n!==".."&&vl.test(n))}var Fn=2*1024*1024,Ko=200;function Wo(e){let{entrypoint:t}=e;if(e.files.length===0)throw new Error("there is nothing in this function to deploy");if(e.files.length>Ko)throw new Error(`a function may hold ${Ko} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!kl(a.path))throw new Error(`${JSON.stringify(a.path)} is not a path a function may contain`);if(n.has(a.path))throw new Error(`${a.path} appears twice`);n.add(a.path)}if(!n.has(t))throw new Error(`the entrypoint ${JSON.stringify(t)} is not one of the files`);let r=[...e.files].sort((a,l)=>a.path<l.path?-1:a.path>l.path?1:0),o={snoutfn:1,entrypoint:t,files:r},s=El(o);if(s.length>Fn)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${Fn/1024} KB. A function is source code; put large files in Storage.`);return o}function El(e){let t=e.files.map(r=>`{"path":${JSON.stringify(r.path)},"text":${JSON.stringify(r.text)}}`).join(","),n=`{"snoutfn":${e.snoutfn},"entrypoint":${JSON.stringify(e.entrypoint)},"files":[${t}]}`;return new TextEncoder().encode(n)}var $l=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/;var Al=["SNOUTDATA_","SNOUT_"];function lt(e){if(!$l.test(e))return`${JSON.stringify(e)} is not an environment variable name: letters, digits and underscores, not starting with a digit.`;let t=Al.find(n=>e.startsWith(n));return t?`names beginning with ${t} are set by the platform, so ${e} cannot be one of yours.`:null}var Mp=64*1024;ee();ne();q();function Xo(e){let t=oe(e.folder,"functions");return dt(t)?Pl(t).filter(n=>!n.startsWith(".")&&!n.startsWith("_")&&Rl(oe(t,n)).isDirectory()).sort():[]}async function Kt(e){c(b(" docker compose run --rm functions-deploy"));let t=await Be(e,["run","--rm","functions-deploy"],!0);if(t.code!==0)throw new N("failed",`functions-deploy failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`)}function Zo(e,t,n){let r=oe(e.folder,".env"),o=zo(r,"utf8"),s=(Pt(o).get("FUNCTIONS_NO_VERIFY_JWT")??"").split(",").map(u=>u.trim()).filter(Boolean),a=n?[...new Set([...s,t])]:s.filter(u=>u!==t);if(a.join(",")===s.join(","))return!1;let l=`FUNCTIONS_NO_VERIFY_JWT=${a.join(",")}`,i=/^FUNCTIONS_NO_VERIFY_JWT=.*$/m;return Yo(r,i.test(o)?o.replace(i,l):`${o.replace(/\n?$/,`
590
- `)}${l}
591
- `),!0}async function Bn(e,t,n){Te(t);let r=qt(n.dir??oe(process.cwd(),"functions",t));if(!dt(oe(r,"index.ts")))throw new h(`${r} has no index.ts. A local function is a folder with an index.ts in it.`);let o=oe(e.folder,"functions",t);if(qt(o)!==r){Vo(o,{recursive:!0,force:!0}),Jo(r,o,{recursive:!0});let a=oe(r,"..","_shared");dt(a)&&qt(a)!==qt(oe(e.folder,"functions","_shared"))&&Jo(a,oe(e.folder,"functions","_shared"),{recursive:!0})}let s=Zo(e,t,!n.verifyJwt);if(await Kt(e),s){let a=await Be(e,["up","-d","--wait","--wait-timeout","600"],!1);if(a.code!==0)throw new N("failed",`docker compose up failed after changing who may call ${t}: ${a.err.trim().split(/\r?\n/).slice(-2).join(" ")}`)}return{ref:e.ref,name:t,url:`${e.apiUrl}/functions/v1/${t}`,verifyJwt:n.verifyJwt,local:e.folder}}async function Qo(e,t,n){let r=await Bn(e,t,n);g(r,()=>{c(`${k(t)} deployed locally: ${r.url}`),n.verifyJwt||c(b(" Callable with no key (FUNCTIONS_NO_VERIFY_JWT), so it must check what calls it itself."))})}function Wt(e){return{ref:e.ref,local:e.folder,functions:Xo(e).map(t=>({name:t,url:`${e.apiUrl}/functions/v1/${t}`}))}}function es(e){let t=Xo(e);g(Wt(e),()=>{if(t.length===0){c(`No functions in ${oe(e.folder,"functions")}.`);return}process.stdout.write(`${F([["NAME","URL"],...t.map(n=>[n,`${e.apiUrl}/functions/v1/${n}`])])}
592
- `)})}async function Hn(e,t){Te(t);let n=oe(e.folder,"functions",t);if(!dt(n))throw new N("not-found",`this local project has no function called ${t}`);return Vo(n,{recursive:!0,force:!0}),Zo(e,t,!1),await Kt(e),{ref:e.ref,name:t,removed:!0}}async function ts(e,t){g(await Hn(e,t),()=>c(`${t} is gone.`))}function Jt(e){return oe(e.folder,"functions",".env")}function Gn(e){let t=Jt(e);return dt(t)?Pt(zo(t,"utf8")):new Map}function ns(e,t){let n=r=>r.includes("'")?JSON.stringify(r):`'${r}'`;Yo(Jt(e),[...t].map(([r,o])=>`${r}=${n(o)}`).join(`
587
+ ${r}`}async function rf(e,t,n={}){Q(t);let r=(await e.list()).find(o=>o.ref===t);return r&&r.status!=="stopped"&&await e.stop(t,ai),Kr(e,{ref:t,port:0,ownerPassword:"",adminPassword:""},n)}async function of(e,t,n){Q(t),await e.remove(t),n&&(await e.removeData(t),hn.delete(t))}async function Kr(e,t,n={}){Q(t.ref);let r=(await e.list()).find(a=>a.ref===t.ref),o=r?.status==="running",s=n.hasData??wn;return{ref:t.ref,exists:!!r,status:r?.status??"absent",ready:o?await e.waitReady(t.ref,1):!1,hasData:r?.hasData??await s(t.ref),port:t.port,databaseBytes:o?await e.databaseBytes(t.ref):null,services:r?.services??[],uri:t.port>0&&t.ownerPassword?Vi(t):null}}var Br,B,Gr,Ze,Bm,Hi,qr,Re,Hm,Gi,V,Km,Jm,qi,hn,Wi,ec=I(()=>{"use strict";ii();ht();di();$r();Or();Se();Br=process.env.SNOUTPOD_ENGINE??"podman";B=Fm(),Gr=18,Ze=[17,18],Bm="snoutpod/postgres",Hi="ghcr.io/snoutdata/snoutpod-postgres";qr=_t(null).name,Re=process.env.SNOUTPOD_LOCAL_NETWORK??"snoutpod-local",Hm=54322,Gi=512,V=ci();Km="cd packages/snoutpod && npm run image:build",Jm=`${Hi}:${Gr}`;qi=20*60*1e3;hn=new Map;Wi="this podman cannot blackhole a route, so the local tier cannot isolate its containers"});re();var xn=[{name:"init",summary:"Create or link a project for this folder, and print its DATABASE_URL or write it to .env",flags:["--name","--region","--env","--ref"]},{name:"login",summary:"Sign in through a browser, with --sso through your company's identity provider, or with --device by typing a code into one anywhere",flags:["--email","--provider","--sso","--domain","--no-browser","--device","--timeout"]},{name:"logout",summary:"Forget the stored session",flags:[]},{name:"whoami",summary:"Who this credential belongs to, and whether it is the account login --email asked for",flags:[]},{name:"upgrade",summary:"Install the newest CLI the way this one was installed (binary or npm), checksum verified",flags:["--check"]},{name:"tokens list",summary:"Long-lived access tokens on this account",flags:[]},{name:"tokens create",summary:"Mint an sdt_ token for CI or an agent",flags:["--name","--expires","--project"]},{name:"tokens revoke",summary:"Revoke one by id or prefix",flags:[]},{name:"projects list",summary:"Every project on this account",flags:[]},{name:"projects create",summary:"Make a hosted database, and print its ref",flags:["--name","--region","--team","--no-wait","--timeout","--show-url"]},{name:"projects pause",summary:"Stop a project, and wait until it is paused",flags:["--ref","--no-wait","--timeout"]},{name:"projects resume",summary:"Start a paused project, and wait until it is ready",flags:["--ref","--no-wait","--timeout"]},{name:"projects delete",summary:"Delete a project, and wait until it is gone",flags:["--ref","--no-wait","--timeout"]},{name:"projects show",summary:"One project: state, products (Realtime included), functions, secrets, domains",flags:["--ref"]},{name:"products",summary:"Auth, storage, the data API and push: on or off. Realtime is always on, and listed",flags:["--ref"]},{name:"products enable",summary:"Turn auth, storage, data-api or push on",flags:["--ref"]},{name:"products disable",summary:"Turn auth, storage, data-api or push off",flags:["--ref"]},{name:"push credentials",summary:"Push keys: what is set for APNs, FCM and Web Push (never the keys)",flags:["--ref"]},{name:"push credentials set",summary:"Set the APNs key (a .p8) or the FCM service account, checked before it is stored",flags:["--ref","--p8","--key-id","--team-id","--topic","--environment","--file"]},{name:"push credentials remove",summary:"Remove the APNs or FCM credentials",flags:["--ref"]},{name:"auth",summary:"Auth settings: Google sign-in and redirect addresses",flags:["--ref"]},{name:"auth google",summary:"Sign in with Google, with your own client (secret on stdin)",flags:["--ref","--client-id","--stdin"]},{name:"auth anonymous",summary:"Guest sign-in on or off: signInAnonymously() gives a browser a session with no email or password",flags:["--ref"]},{name:"auth redirects",summary:"The site URL and the addresses a sign-in may return to",flags:["--ref","--site-url","--allow"]},{name:"auth templates",summary:"The five auth emails, ours or your own",flags:["--ref"]},{name:"auth template",summary:"Use your own subject and HTML for one, or reset it",flags:["--ref","--file","--subject"]},{name:"realtime inspect",summary:"The channels open now: each client, its presence, when it was last heard, and the last minute of messages",flags:["--channel","--watch","--ref"]},{name:"realtime logs",summary:"The connection log: connects, joins, leaves and disconnects, with the reason each ended",flags:["--since","--channel","--ref"]},{name:"domains",summary:"Your own domains in front of the project API",flags:["--ref"]},{name:"domains add",summary:"Add one, and print the DNS records to publish",flags:["--ref"]},{name:"domains verify",summary:"Check its records and verify it",flags:["--ref"]},{name:"domains remove",summary:"Stop serving it",flags:["--ref"]},{name:"functions deploy",summary:"Put a folder of TypeScript on the edge",flags:["--ref","--dir","--entrypoint","--no-verify-jwt"]},{name:"functions list",summary:"What this project has deployed",flags:["--ref"]},{name:"functions size",summary:"Its memory and workers, within the plan",flags:["--ref","--memory","--concurrency","--reset"]},{name:"functions delete",summary:"Remove one",flags:["--ref"]},{name:"secrets set",summary:"Set the environment functions run with",flags:["--ref","--stdin"]},{name:"secrets list",summary:"The names, never the values",flags:["--ref"]},{name:"secrets unset",summary:"Remove one",flags:["--ref"]},{name:"keys",summary:"The anon and service_role API keys for a project",flags:["--ref"]},{name:"keys rotate",summary:"New keys, breaking every one already issued",flags:["--ref","--force"]},{name:"teams",summary:"Teams this account belongs to",flags:[]},{name:"link",summary:"Point this folder at a project",flags:["--ref"]},{name:"usage",summary:"Size and compute against the plan limit",flags:["--ref","--days","--history"]},{name:"start",summary:"A Postgres on this machine, with your migrations applied",flags:["--port","--dir","--no-migrations","--out-of-order"]},{name:"stop",summary:"Stop the local database. The data stays",flags:[]},{name:"status",summary:"What the local database is doing",flags:[]},{name:"gen types typescript",summary:"The schema as a TypeScript Database type, on stdout",flags:["--ref","--db-url","--local","--schema","--out"]},{name:"db url",summary:"A connection string",flags:["--ref"]},{name:"db psql",summary:"Open psql, or run statements after --",flags:["--ref"]},{name:"db reset-password",summary:"Rotate the project password",flags:["--ref"]},{name:"db access",summary:"Who signs in to the database as themselves with OAuth (Postgres 18), at which level, as which role",flags:["--ref"]},{name:"db access grant",summary:"Let a person sign in to the database as themselves, and print their connection string",flags:["--ref","--level"]},{name:"db access revoke",summary:"Take it away, by email or role; a token already issued can work until it expires (up to 1 hour)",flags:["--ref"]},{name:"db export",summary:"Take or download a dump",flags:["--ref","--out","--status","--timeout"]},{name:"db push",summary:"Apply migrations from a folder",flags:["--dir","--dry-run","--out-of-order"]},{name:"db restore",summary:"Put a dump into a project, or rewind it to a moment into a new project",flags:["--file","--force","--at","--name","--window","--ref"]},{name:"mcp",summary:"Serve these operations as tools over stdio",flags:["--allow-delete"]}],Qr={"--ref":"The project. Defaults to SNOUTDATA_PROJECT, then .snoutdata/project.json here or in a parent","--name":"The project's name","tokens create --name":"What the token is for and where it lives, shown in tokens list","db restore --name":"The name of the new project the restore makes","--region":"Where the project runs. Defaults to the nearest","--env":"Write DATABASE_URL into .env in this folder, and add .env to .gitignore since it holds the password. Without it the URL is printed","--email":"The account to sign in as. The browser offers it, and a different account is reported at once and by whoami","--provider":"google (the default) or github","--sso":"Sign in with your company's identity provider","--domain":"With --sso: your work email or company domain. Only the domain is sent","--no-browser":"Print the sign-in URL instead of opening a browser","--device":"Sign in by typing a code into a browser on any machine: SSH, containers, agents","--timeout":"Seconds to wait before giving up","--check":"Say whether a newer version exists, and install nothing","--expires":"Days until the token stops working. Without it, it works until revoked","--project":"Limit the token to one project ref","--team":"Create it in a team (name or id), shared with its members","--no-wait":"Return as soon as it is asked for, without waiting for it to be true","--show-url":"Also print the connection string, which holds the password. Otherwise db url prints it when wanted","--p8":"The APNs .p8 key file (a path, never the key itself)","--key-id":"The APNs key id","--team-id":"Your Apple developer team id","--topic":"The app's bundle id","--environment":"production or sandbox","--file":"A file to read","push credentials set --file":"The FCM service-account JSON","auth template --file":"The email's HTML body","db restore --file":"The dump to restore (from db export or pg_dump -Fc)","--client-id":"Your Google OAuth client id; the secret is read from stdin with --stdin","--stdin":"Read the secret value from a pipe, so it is not in shell history","--site-url":"Where sign-in emails send people by default","--allow":"Comma-separated addresses a sign-in may return to","--subject":"The email's subject line","--dir":"The folder to read from","db push --dir":"The migrations folder (default ./migrations)","start --dir":"The migrations folder (default ./migrations)","functions deploy --dir":"The function's folder (default ./functions/<name>)","--entrypoint":"The file the runtime starts, when it is not index.ts","--no-verify-jwt":"Let anybody with the URL call it (webhooks). By default a caller needs a project key or a user session","--memory":"Memory per worker, in MB, within the plan","--concurrency":"How many workers it may run at once, within the plan","--reset":"Back to the plan's default size","--force":"Do it although it cannot be undone","--days":"How many days of history","--history":"Print the day-by-day table too","--port":"The port the local database listens on (default 54322)","--no-migrations":"Start the database and apply nothing","--out-of-order":"Apply a new migration that sorts before one already applied","--db-url":"Read a postgres:// database instead of a project","--local":"Read the database snoutdata start is running","--schema":"Comma-separated schemas (default public)","--out":"Write to this file instead of stdout","db export --out":"Where to save the dump","--status":"Show the last export without taking another","--dry-run":"Show what would run, and change nothing","--at":"The moment to restore to (an ISO time), into a NEW project","--window":"How far back a point-in-time restore can go","--allow-delete":"Let the agent's delete_project actually delete","--channel":"Only this channel","--since":"How far back, like 10m, 2h or 1d (default 1h)","--watch":"Keep printing what changes until interrupted","--level":"full (everything the project password can do) or read (reads the project's own tables, never the auth or storage schemas, and writes nothing). Default read"};function kn(e,t){return Qr[`${e} ${t}`]??Qr[t]??""}z();te();K();import{spawn as xl}from"node:child_process";function kl(e){return`snout-${e}`}function Je(e,t,n){return new Promise((r,o)=>{let s=xl("docker",["compose","-p",kl(e.ref),"--project-directory",e.folder,...t],{cwd:e.folder,stdio:["ignore","pipe","pipe"]}),a="",d="";s.stdout.on("data",i=>{a+=String(i)}),s.stderr.on("data",i=>{if(d+=String(i),n)for(let u of String(i).split(/\r?\n/).filter(Boolean))c(b(` ${u.trim()}`))}),s.on("error",i=>{if(i.code==="ENOENT"){o(new C("tool-missing","docker is not installed, or not on PATH. A local project runs in Docker (Docker Desktop on Windows and macOS)."));return}o(new C("failed",String(i)))}),s.on("close",i=>r({code:i??0,out:a,err:d}))})}async function vl(e){let t=await Je(e,["ps","-a","--format","json"],!1);if(t.code!==0)throw new C("failed",t.err.trim()||"docker compose ps failed. Is Docker running?");let n=t.out.trim();return(n.startsWith("[")?JSON.parse(n):n.split(/\r?\n/).filter(Boolean).map(o=>JSON.parse(o))).map(o=>({service:o.Service??"?",state:o.State==="exited"&&o.ExitCode===0?"done":o.State??"?",health:o.Health??""})).sort((o,s)=>o.service.localeCompare(s.service))}async function Go(e){let t=await vl(e),n=t.filter(o=>o.state==="running").length,r=t.filter(o=>o.state!=="done").length;return g({ref:e.ref,local:e.folder,api:e.apiUrl,dbPort:e.dbPort,running:n,services:t},()=>{if(c(`${x(e.name??e.ref)} (local, ${e.folder})`),t.length===0){c("Not started. `snoutdata start` brings it up.");return}process.stdout.write(`${F([["SERVICE","STATE","HEALTH"],...t.map(o=>[o.service,o.state,o.health])])}
588
+ `),c(`${n} of ${r} services running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}.`)}),0}async function qo(e){c(`Starting ${e.name??e.ref} (local)\u2026`);let t=await Je(e,["up","-d","--wait","--wait-timeout","600"],!0);if(t.code!==0)throw new C("failed",`docker compose up failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,api:e.apiUrl,dbPort:e.dbPort,started:!0},()=>{c(`Running. API ${e.apiUrl}, database 127.0.0.1:${e.dbPort}. \`snoutdata db url\` prints the connection string.`)}),0}async function Ko(e){let t=await Je(e,["stop"],!0);if(t.code!==0)throw new C("failed",`docker compose stop failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`);return g({ref:e.ref,stopped:!0},()=>c(`Stopped ${e.name??e.ref}. Its data is kept; \`snoutdata start\` brings it back.`)),0}function Jo(e){g({ref:e.ref,name:e.name,local:e.folder,api:e.apiUrl,dbPort:e.dbPort},()=>{process.stdout.write(`${F([["name",e.name??"(not set up by Studio)"],["ref",e.ref],["where",`local, ${e.folder}`],["api",e.apiUrl],["database",`127.0.0.1:${e.dbPort}`]])}
589
+ `)})}tt();import{cpSync as Vo,existsSync as dt,readdirSync as Nl,readFileSync as Yo,rmSync as Xo,statSync as Dl,writeFileSync as Zo}from"node:fs";import{join as se,resolve as Kt}from"node:path";var El=/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/;function $l(e){return El.test(e)}function De(e){if(!$l(e))throw new Error(`not a function name: ${JSON.stringify(e)}. Letters, digits, hyphens and underscores, up to 48 characters, starting with a letter or a digit.`);return e}var Pl=/^[A-Za-z0-9_][A-Za-z0-9._-]*$/;function Al(e){if(e.length===0||e.length>255||e.includes("\\")||e.startsWith("/"))return!1;let t=e.split("/");return t.length>8?!1:t.every(n=>n!=="."&&n!==".."&&Pl.test(n))}var Hn=2*1024*1024,Wo=200;function zo(e){let{entrypoint:t}=e;if(e.files.length===0)throw new Error("there is nothing in this function to deploy");if(e.files.length>Wo)throw new Error(`a function may hold ${Wo} files and this one has ${e.files.length}`);let n=new Set;for(let a of e.files){if(!Al(a.path))throw new Error(`${JSON.stringify(a.path)} is not a path a function may contain`);if(n.has(a.path))throw new Error(`${a.path} appears twice`);n.add(a.path)}if(!n.has(t))throw new Error(`the entrypoint ${JSON.stringify(t)} is not one of the files`);let r=[...e.files].sort((a,d)=>a.path<d.path?-1:a.path>d.path?1:0),o={snoutfn:1,entrypoint:t,files:r},s=Tl(o);if(s.length>Hn)throw new Error(`this function is ${Math.round(s.length/1024)} KB and the limit is ${Hn/1024} KB. A function is source code; put large files in Storage.`);return o}function Tl(e){let t=e.files.map(r=>`{"path":${JSON.stringify(r.path)},"text":${JSON.stringify(r.text)}}`).join(","),n=`{"snoutfn":${e.snoutfn},"entrypoint":${JSON.stringify(e.entrypoint)},"files":[${t}]}`;return new TextEncoder().encode(n)}var Rl=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/;var Ol=["SNOUTDATA_","SNOUT_"];function lt(e){if(!Rl.test(e))return`${JSON.stringify(e)} is not an environment variable name: letters, digits and underscores, not starting with a digit.`;let t=Ol.find(n=>e.startsWith(n));return t?`names beginning with ${t} are set by the platform, so ${e} cannot be one of yours.`:null}var Gp=64*1024;te();re();K();function Qo(e){let t=se(e.folder,"functions");return dt(t)?Nl(t).filter(n=>!n.startsWith(".")&&!n.startsWith("_")&&Dl(se(t,n)).isDirectory()).sort():[]}async function Jt(e){c(b(" docker compose run --rm functions-deploy"));let t=await Je(e,["run","--rm","functions-deploy"],!0);if(t.code!==0)throw new C("failed",`functions-deploy failed: ${t.err.trim().split(/\r?\n/).slice(-3).join(" ")}`)}function es(e,t,n){let r=se(e.folder,".env"),o=Yo(r,"utf8"),s=(Tt(o).get("FUNCTIONS_NO_VERIFY_JWT")??"").split(",").map(u=>u.trim()).filter(Boolean),a=n?[...new Set([...s,t])]:s.filter(u=>u!==t);if(a.join(",")===s.join(","))return!1;let d=`FUNCTIONS_NO_VERIFY_JWT=${a.join(",")}`,i=/^FUNCTIONS_NO_VERIFY_JWT=.*$/m;return Zo(r,i.test(o)?o.replace(i,d):`${o.replace(/\n?$/,`
590
+ `)}${d}
591
+ `),!0}async function Gn(e,t,n){De(t);let r=Kt(n.dir??se(process.cwd(),"functions",t));if(!dt(se(r,"index.ts")))throw new y(`${r} has no index.ts. A local function is a folder with an index.ts in it.`);let o=se(e.folder,"functions",t);if(Kt(o)!==r){Xo(o,{recursive:!0,force:!0}),Vo(r,o,{recursive:!0});let a=se(r,"..","_shared");dt(a)&&Kt(a)!==Kt(se(e.folder,"functions","_shared"))&&Vo(a,se(e.folder,"functions","_shared"),{recursive:!0})}let s=es(e,t,!n.verifyJwt);if(await Jt(e),s){let a=await Je(e,["up","-d","--wait","--wait-timeout","600"],!1);if(a.code!==0)throw new C("failed",`docker compose up failed after changing who may call ${t}: ${a.err.trim().split(/\r?\n/).slice(-2).join(" ")}`)}return{ref:e.ref,name:t,url:`${e.apiUrl}/functions/v1/${t}`,verifyJwt:n.verifyJwt,local:e.folder}}async function ts(e,t,n){let r=await Gn(e,t,n);g(r,()=>{c(`${x(t)} deployed locally: ${r.url}`),n.verifyJwt||c(b(" Callable with no key (FUNCTIONS_NO_VERIFY_JWT), so it must check what calls it itself."))})}function Wt(e){return{ref:e.ref,local:e.folder,functions:Qo(e).map(t=>({name:t,url:`${e.apiUrl}/functions/v1/${t}`}))}}function ns(e){let t=Qo(e);g(Wt(e),()=>{if(t.length===0){c(`No functions in ${se(e.folder,"functions")}.`);return}process.stdout.write(`${F([["NAME","URL"],...t.map(n=>[n,`${e.apiUrl}/functions/v1/${n}`])])}
592
+ `)})}async function qn(e,t){De(t);let n=se(e.folder,"functions",t);if(!dt(n))throw new C("not-found",`this local project has no function called ${t}`);return Xo(n,{recursive:!0,force:!0}),es(e,t,!1),await Jt(e),{ref:e.ref,name:t,removed:!0}}async function rs(e,t){g(await qn(e,t),()=>c(`${t} is gone.`))}function zt(e){return se(e.folder,"functions",".env")}function Kn(e){let t=zt(e);return dt(t)?Tt(Yo(t,"utf8")):new Map}function os(e,t){let n=r=>r.includes("'")?JSON.stringify(r):`'${r}'`;Zo(zt(e),[...t].map(([r,o])=>`${r}=${n(o)}`).join(`
593
593
  `)+(t.size?`
594
- `:""))}async function rs(e,t){for(let r of t){let o=lt(r.name);if(o)throw new h(o)}let n=Gn(e);for(let r of t)n.set(r.name,r.value);ns(e,n),await Kt(e),g({ref:e.ref,set:t.map(r=>r.name)},()=>{for(let r of t)c(`${k(r.name)} set in ${Jt(e)}`)})}function zt(e){return{ref:e.ref,local:e.folder,secrets:[...Gn(e).keys()].sort().map(t=>({name:t}))}}function os(e){let t=zt(e),n=t.secrets.map(r=>r.name);g(t,()=>{c(n.length?n.join(`
595
- `):`No function secrets in ${Jt(e)}.`)})}async function ss(e,t){let n=Gn(e);if(!n.delete(t))throw new N("not-found",`${t} is not set for this local project`);ns(e,n),await Kt(e),g({ref:e.ref,unset:t},()=>c(`${t} unset.`))}ee();Le();be();Dn();Dt();q();Ht();J();J();ee();be();q();async function as(){let{projects:e,allowance:t}=await re();g({projects:e,allowance:t},()=>{if(e.length===0){c('No projects yet. `snoutdata projects create --name "my project"`.');return}process.stdout.write(`${F([["REF","NAME","STATE","REGION","LAST CONNECTION"],...e.map(n=>[n.ref,n.name,n.readOnly?k("read-only"):n.state==="ready"?n.state:k(n.state),n.region,pe(n.lastConnectionAt)])])}
596
- `)})}async function qn(e){let t=await _("cloud-project-create",{name:e.name,region:e.region,teamId:e.teamId}),n=t.project.ref;c(`Created ${n}.`);let r=t.project;if(e.wait){c("Waiting for it to start\u2026");let s="";r=await ct(n,{timeoutMs:e.timeoutMs,onTick:a=>{a!==s&&(s=a,c(b(` ${a}`)))}})}e.link&&Ze(e.link,{ref:n,name:r.name});let o=`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require`;g({project:r,ref:n,host:r.host,database:r.database,user:r.user,...e.showUrl?{password:t.password,uri:o}:{connectionString:"snoutdata db url"}},()=>{process.stdout.write(`${e.showUrl?o:n}
597
- `),c(""),c(`Ready. ${k(n)} in ${r.region}, at ${r.host}.`),c(e.showUrl?"The password is stored for you: `snoutdata db url` prints this again.":"`snoutdata db url` prints the connection string (it holds the password, so it is not shown here).")})}async function is(e,t,n={}){let r=`cloud-project-${e}`,o=await _(r,{ref:t}),s=e==="delete"?"deleted":`${e}d`;if(n.wait===!1){g({...o,settled:!1},()=>c(`${t}: asked to be ${s}. \`snoutdata projects list\` shows when it is.`));return}let a=o.project?.state??"",l=await it(t,e,{timeoutMs:n.timeoutMs,onTick:u=>{u!==a&&(a=u,c(b(` ${u}`)))}}),i=l?.state??"deleted";if(i==="error")throw new N("failed",`${t} went to error instead of ${s}${l?.stateDetail?`: ${l.stateDetail}`:""}.`,{ref:t,state:i});g({...o,...l?{project:l}:{},settled:!0},()=>{c(o.changed?`${t}: ${s}.`:`${t} was already ${s}.`)})}async function cs(e){let t=await _("cloud-project-reset-password",{ref:e});g(t,()=>{process.stdout.write(`${t.password}
598
- `),c(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function ls(){let{teams:e}=await Gt();g({teams:e},()=>{if(e.length===0){c("You are not on any teams.");return}process.stdout.write(`${F([["NAME","ID","SHARE"],...e.map(t=>[t.name,t.id,t.mayShare?"yes":t.why??"no"])])}
599
- `)})}async function ds(e){let{teams:t}=await Gt(),n=t.find(s=>s.id===e),r=n?[n]:t.filter(s=>s.name.toLowerCase()===e.toLowerCase());if(r.length===0)throw new Error(t.length===0?`You are not on any teams, so there is no "${e}" to share with.`:`No team called "${e}". You are on: ${t.map(s=>s.name).join(", ")}.`);if(r.length>1)throw new Error(`More than one team is called "${e}". Use the id: ${r.map(s=>s.id).join(", ")}.`);let o=r[0];if(!o.mayShare)throw new Error(`${o.name}: ${o.why??"you cannot share a project into this team"}.`);return o.id}J();q();async function us(e){let t=await _("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays,...e.project?{project:e.project}:{}});g(t,()=>{process.stdout.write(`${t.token}
600
- `),c(""),c(`${k(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),c(t.project?`Reaches project ${t.project} only.`:"Reaches every project on this account."),c("This is the only time it is shown: only its hash is stored."),c(b(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function ms(){let{tokens:e}=await _("cloud-token-list");g({tokens:e},()=>{if(e.length===0){c("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${F([["PREFIX","NAME","PROJECT","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?k(t.prefix):t.prefix,t.name,t.project??"all",t.revokedAt?"revoked":t.live?"live":"expired",pe(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
601
- `)})}async function fs(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await _("cloud-token-revoke",t);g(n,()=>{c(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}J();ne();q();async function ps(e){let t=await te(e);g({ref:t.ref,anonKey:t.anonKey,serviceRoleKey:t.serviceRoleKey,issuedAt:t.keysIssuedAt,expiresAt:t.keysExpireAt},()=>{if(!t.anonKey||!t.serviceRoleKey){c(`${e} has no API keys yet. Ask again in a moment.`);return}process.stdout.write(`${hs(t.anonKey,t.serviceRoleKey)}
602
- `),Nl(t.keysIssuedAt)})}async function gs(e,t){if(!t.force)throw c(`Rotating the keys for ${e} stops every anon and service_role key already pasted anywhere.`),c("Anything holding one (a deployed app, a CI secret, a teammate) breaks until it is updated."),new h("keys rotate needs --force, because this cannot be undone");let n=await _("cloud-project-rotate-jwt",{ref:e});g(n,()=>{process.stdout.write(`${hs(n.anonKey,n.serviceRoleKey)}
603
- `),c(""),c(n.breaks),c(b(" snoutdata keys --json to read them again later"))})}function hs(e,t){return F([["KEY","VALUE"],["anon",e],["service_role",t]])}function Nl(e){c(""),c(`${k("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),c(`${k("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&c(b(` issued ${e.slice(0,10)}`))}J();ne();q();import{readdir as Il,readFile as Cl,stat as Ml}from"node:fs/promises";import{join as Wn,relative as jl,sep as Ll}from"node:path";var ys=["functions"];async function Kn(e){try{return(await Ml(e)).isDirectory()}catch{return!1}}async function Ul(e,t){if(t){if(!await Kn(t))throw new h(`${t} is not a directory`);return t}for(let n of ys){let r=Wn(n,e);if(await Kn(r))return r}throw new h(`no folder found for ${e} at ${ys[0]}/${e}; use --dir to say where it is.`)}async function ws(e){let t=[];async function n(r){for(let o of await Il(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=Wn(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await Cl(s),l=a.toString("utf8");if(Buffer.compare(Buffer.from(l,"utf8"),a)!==0)throw new h(`${s} is not text, and a function bundle is source code. Put binary assets in Storage.`);t.push({path:jl(e,s).split(Ll).join("/"),text:l})}}return await n(e),t}async function Fl(e,t,n){let r=await ws(t);if(r.length===0)throw new h(`${t} has no files in it`);let o=n??(r.some(l=>l.path==="index.ts")?"index.ts":"");if(!o)throw new h(`${t} has no index.ts, so there is nothing to start. Name the file with --entrypoint.`);let s=Wn(t,"..","_shared");if(!await Kn(s))return{entrypoint:o,files:r};let a=(await ws(s)).map(l=>({path:`_shared/${l.path}`,text:l.text}));return{entrypoint:`${e}/${o}`,files:[...r.map(l=>({path:`${e}/${l.path}`,text:l.text})),...a]}}async function Jn(e,t,n){Te(t);let r=await Ul(t,n.dir),{entrypoint:o,files:s}=await Fl(t,r,n.entrypoint),a=Wo({entrypoint:o,files:s});return{answer:await _("cloud-project-functions",{ref:e,action:"deploy",name:t,verifyJwt:n.verifyJwt,bundle:a}),directory:r,files:s.length,bytes:JSON.stringify(a).length}}async function bs(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await Jn(e,t,n),l=r.functions.find(i=>i.name===t);g(r,()=>{let i=Math.max(1,Math.round(a/1024));c(`${k(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${i} KB)`),l&&c(` ${l.url}`),n.verifyJwt||(c(""),c(k(" Anybody who knows this URL can run it. No API key is required.")),c(b(" That is what a webhook receiver needs. Check the sender signature inside the function."))),r.limit.maxFunctions!==null&&c(b(` ${r.limit.deployed} of ${r.limit.maxFunctions} functions on this plan`))})}function zn(e){return _("cloud-project-functions",{ref:e})}function Vn(e,t){return Te(t),_("cloud-project-functions",{ref:e,action:"delete",name:t})}function Yn(e,t,n){if(Te(t),!n.reset&&n.memoryMb===void 0&&n.concurrency===void 0)throw new h("functions size needs --memory MB, --concurrency N, or --reset");return _("cloud-project-functions",{ref:e,action:"size",name:t,...n.reset?{reset:!0}:{memoryMb:n.memoryMb??null,concurrency:n.concurrency??null}})}function Bl(e,t){if(e.memoryMb==null||e.concurrency==null)return null;let n=e.memoryMb*e.concurrency,r=t.podMemoryMb?` of ${t.podMemoryMb} MB`:"";return`${e.memoryMb} MB x ${e.concurrency} worker${e.concurrency===1?"":"s"} = ${n} MB${r}`}function Ss(e){return e.functionMemoryMb==null||e.concurrencyMax==null||e.podMemoryMb==null?null:`${e.tier??"this"} plan: up to ${e.functionMemoryMb} MB a worker and ${e.concurrencyMax} workers a function, memory x workers up to ${e.podMemoryMb} MB`}async function _s(e,t,n){let r=await Yn(e,t,n),o=r.functions.find(s=>s.name===t);g(r,()=>{let s=o?Bl(o,r.limit):null;c(`${k(t)} ${n.reset?"is back to the plan default":"is resized"}${s?`: ${s}`:""}`),c(b(" The next request runs at it; workers already running finish what they hold."));let a=Ss(r.limit);a&&c(b(` ${a}`))})}async function xs(e){let t=await zn(e);g(t,()=>{if(t.functions.length===0){c(`${e} has no functions.`),c(b(" snoutdata functions deploy <name> from functions/<name>"));return}process.stdout.write(`${F([["NAME","KEY","SIZE","MEMORY","WORKERS","URL"],...t.functions.map(r=>[r.name,r.verifyJwt?"required":k("open"),r.bytes===null?"-":`${Math.max(1,Math.round(r.bytes/1024))} KB`,r.memoryMb==null?"-":`${r.memoryMb} MB`,r.concurrency==null?"-":String(r.concurrency),r.url])])}
604
- `),t.limit.maxFunctions!==null&&c(b(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`));let n=Ss(t.limit);n&&c(b(`${n}. Change one with: snoutdata functions size <name> --memory MB --concurrency N`))})}async function vs(e,t){let n=await Vn(e,t);g(n,()=>{c(n.live?`${t} is gone.`:`${t} is gone. It stops answering within a few seconds.`)})}J();ne();q();function ks(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function Es(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function $s(e,t){if(e.length===0)throw new h("secrets set needs NAME=value, or NAME with --stdin");if(t.stdin){if(e.length!==1)throw new h("--stdin sets one secret, so it takes one NAME");return[{name:e[0],value:await Es()}]}return e.map(n=>{let r=ks(n);if(!r)throw new h(`${n} is not NAME=value. Use --stdin to set a value from a pipe.`);return r})}async function As(e,t,n){if(t.length===0)throw new h("secrets set needs NAME=value, or NAME with --stdin");let r=[];if(n.stdin){if(t.length!==1)throw new h("--stdin sets one secret, so it takes one NAME");r.push({name:t[0],value:await Es()})}else for(let s of t){let a=ks(s);if(!a)throw new h(`${s} is not NAME=value. Use --stdin to set a value from a pipe.`);r.push(a)}for(let s of r){let a=lt(s.name);if(a)throw new h(a)}let o=null;for(let s of r)o=await _("cloud-project-secrets",{ref:e,action:"set",name:s.name,value:s.value});g(o,()=>{for(let s of r)c(`${k(s.name)} set`);c(b(" Functions pick it up within a few seconds. Redeploying is not needed.")),n.stdin||c(b(" A value typed here is in your shell history. In CI, use: \u2026 | snoutdata secrets set NAME --stdin"))})}async function Ps(e,t){let n=lt(t);if(n)throw new h(n);let r=await _("cloud-project-secrets",{ref:e,action:"unset",name:t});g(r,()=>{c(`${t} is gone.`)})}async function Rs(e){let t=await _("cloud-project-secrets",{ref:e});g(t,()=>{if(t.secrets.length===0){c(`${e} has no function secrets.`),c(b(" snoutdata secrets set STRIPE_KEY=sk_..."));return}process.stdout.write(`${F([["NAME","SIZE","SET"],...t.secrets.map(n=>[n.name,n.bytes===null?"-":`${n.bytes} B`,pe(n.updatedAt)])])}
605
- `),c(b("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as mt}from"node:child_process";import{appendFileSync as Ws,existsSync as en,readFileSync as Js,statSync as yd,unlinkSync as wd,writeFileSync as Ge}from"node:fs";import{dirname as bd,join as tn}from"node:path";import{tmpdir as Sd}from"node:os";import{open as zs}from"node:fs/promises";function Ts(e){let t=(r,o)=>String.fromCharCode(...Array.from(e.slice(r,r+o)));return t(0,5)==="PGDMP"?"custom":e.length>=262&&t(257,5)==="ustar"?"tar":Array.from(e.slice(0,256)).every(r=>r===9||r===10||r===13||r>=32&&r<127||r>=128)&&e.length>0?"plain":"unknown"}function Os(e,t,n={}){return t.readOnly?{ok:!1,code:"read-only",reason:"this project is over its plan's storage limit, so it refuses writes. A restore would fail part way. Delete some rows or change plan first."}:e==="unknown"?{ok:!1,code:"unknown-format",reason:"that file is not a pg_dump archive and does not look like SQL. `snoutdata db export` writes the custom format; psql reads plain .sql."}:!n.force&&t.existingTables>0?{ok:!1,code:"not-empty",reason:`this database already has ${t.existingTables} table${t.existingTables===1?"":"s"}. Restoring into it would leave some objects created and some not. Check --ref is the project you meant, or pass --force.`}:{ok:!0,tool:e==="plain"?"psql":"pg_restore",format:e}}function Ns(e){let t=e.split(`
606
- `).filter(r=>/^pg_restore: error:/.test(r.trim())),n=t.filter(r=>!/must be owner of extension/.test(r));return{total:t.length,harmless:t.length-n.length,fatal:n}}var Xn={auth:"snout_auth_admin",storage:"snout_storage_admin",push:"snout_push_admin"},ql=/^(snoutpod_admin|snout_[a-z]+_admin)$/,Vt=new Set(["auth","storage","push","realtime","snout_realtime","_realtime","graphql","graphql_public","extensions","net","cron","pgbouncer","vault","pgsodium"]),Kl=/^(pgrst_ddl_watch|pgrst_drop_watch|snoutpod_[a-z_]+|snoutdata_realtime|snout_realtime_messages)(\(|$)/,Wl={auth:"auth",storage:"storage",push:"push"},Jl=new Set(["push.settings"]),zl=["TABLE DATA","SEQUENCE SET","SEQUENCE OWNED BY","FK CONSTRAINT","CHECK CONSTRAINT","DEFAULT ACL","EVENT TRIGGER","ROW SECURITY","INDEX ATTACH","TABLE ATTACH","MATERIALIZED VIEW DATA","MATERIALIZED VIEW","PUBLICATION TABLES IN SCHEMA","PUBLICATION TABLE","FOREIGN TABLE","FOREIGN DATA WRAPPER","FOREIGN SERVER","USER MAPPING","TEXT SEARCH CONFIGURATION","TEXT SEARCH DICTIONARY","TEXT SEARCH PARSER","TEXT SEARCH TEMPLATE","LARGE OBJECT","PROCEDURAL LANGUAGE","OPERATOR CLASS","OPERATOR FAMILY","ACCESS METHOD","EXTENDED STATISTICS","DATABASE PROPERTIES"];function Vl(e){let t=/^\d+; \d+ \d+ (.+)$/.exec(e.trim());if(!t)return null;let n=t[1],r=zl.find(i=>n.startsWith(`${i} `))??n.split(" ")[0],o=n.slice(r.length).trim().split(" "),s=o[0]??"-",a=o.length>2?o[o.length-1]:"",l=o.slice(1,o.length>2?-1:void 0).join(" ");return{line:e.trim(),type:r,schema:s,name:l,owner:a}}function Yl(e){let t=new Map,n=o=>o.replace(/"/g,""),r=/ALTER TABLE (?:ONLY )?([\w."$]+)\s+ADD CONSTRAINT [^;]*?FOREIGN KEY \([^)]*\) REFERENCES ([\w."$]+)\(/g;for(let o of e.matchAll(r)){let s=n(o[1]),a=n(o[2]);if(s!==a){let l=t.get(s)??new Set;l.add(a),t.set(s,l)}}return t}function Ds(e,t){let n=new Map,r=[],o=new Set,s=0;for(let i of e.split(`
607
- `)){let u=Vl(i);if(!u)continue;let{type:d,schema:f,name:y,owner:m}=u;if(d==="TABLE DATA"||d==="SEQUENCE SET"){let S=/^(schema_migrations|migrations)$/.test(y)&&Vt.has(f);if(/realtime/.test(f)||S||f==="public"&&y==="spatial_ref_sys"){s++;continue}if(f==="cron"){s++,o.add("Scheduled jobs (pg_cron) are not restored: each names the project it came from. Schedule them again with cron.schedule.");continue}if(f==="push"&&y==="credentials"){s++,o.add("Push credentials are not restored: they are sealed for the project they came from. Set them again with `snoutdata push credentials set`.");continue}let w=Wl[f];if(w){w==="storage"&&y==="objects"&&o.add("Storage objects are restored as rows; the files themselves are not part of an export. Upload them again into this project."),n.set(w,[...n.get(w)??[],u]);continue}if(Vt.has(f)){s++;continue}r.push(u.line);continue}if(d==="COMMENT"&&f==="-"&&/^EXTENSION( |$)/.test(y)||(d==="SCHEMA"?Vt.has(y):Vt.has(f))||Kl.test(y)||ql.test(m)&&d!=="SCHEMA"){s++;continue}r.push(u.line)}let a=Yl(t),l=["auth","storage","push"];return{platformData:l.filter(i=>n.has(i)).map(i=>{let u=n.get(i),d=u.filter(y=>y.type==="TABLE DATA"&&Jl.has(`${y.schema}.${y.name}`)).map(y=>`${y.schema}.${y.name}`),f=u.filter(y=>y.type==="TABLE DATA").map(y=>`${y.schema}.${y.name}`);return{product:i,clear:d,tables:f,lines:Xl(u,a)}}),rest:r,products:l.filter(i=>n.has(i)),skipped:s,notes:[...o]}}function Xl(e,t){let n=l=>`${l.schema}.${l.name}`,r=new Set(e.map(n)),o=new Set,s=[],a=[...e];for(;a.length>0;){let i=a.find(u=>[...t.get(n(u))??[]].every(d=>!r.has(d)||o.has(d)||d===n(u)))??a[0];o.add(n(i)),s.push(i.line),a=a.filter(u=>u!==i)}return s}J();ne();q();var Zl="2024-11-05";function H(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var I={type:"string"},Yt=[{name:"whoami",description:"Who this CLI is signed in as, and how (a session or a long-lived access token). Use it to check a credential before doing anything else.",inputSchema:H({})},{name:"list_projects",description:"Every hosted Postgres database on this account, with its ref, name, region, state (ready, paused, error), size, and whether it is read-only because it is over its plan's storage limit. Under `local`, the self-hosted projects on this machine (SnoutData Studio's, or linked with `snoutdata link --local`), whose refs work with get_connection_url, push_migrations, get_project and the function tools.",inputSchema:H({})},{name:"create_project",description:"Make a new hosted Postgres database and wait for it to be ready. Costs a project against the plan allowance; a free plan includes two. Returns its ref, which every other tool takes.",inputSchema:H({name:I,region:I,teamId:I},["name"])},{name:"get_connection_url",description:"The postgres:// URL for a project, ready to put in DATABASE_URL. IT CONTAINS A LIVE PASSWORD, so treat it as a credential and do not print it into a log or a file that is committed. Connecting to a paused project wakes it, which takes a few seconds.",inputSchema:H({ref:I},["ref"])},{name:"pause_project",description:"Stop a project, and wait until it is paused. Its data is kept and the next connection wakes it. A production project refuses this.",inputSchema:H({ref:I},["ref"])},{name:"resume_project",description:"Start a paused project without waiting for a connection to wake it, and wait until it is ready.",inputSchema:H({ref:I},["ref"])},{name:"push_migrations",description:"Run the .sql files in a folder against a project, in name order, once each, recording what ran in a _snoutdata_migrations table. Refuses a file that changed after it ran, a file that has gone, and a new file that sorts before one already applied. Use dryRun first to see what would happen. Needs psql on this machine.",inputSchema:H({ref:I,dir:I,dryRun:{type:"boolean"}},["ref"])},{name:"usage",description:"How much storage and compute a project has used against the limit on this plan, and whether it is near it. READ THIS BEFORE a migration or a bulk insert: a project over its storage limit is made read-only, and `list_projects` will say readOnly afterwards without ever saying how much headroom was left. Takes an optional number of days of history (default 30, maximum 365).",inputSchema:H({ref:I,days:{type:"number"}},["ref"])},{name:"export_status",description:"Whether a copy of this database is being taken, when the last one finished, and a download link if there is a live one. Costs nothing and starts nothing.",inputSchema:H({ref:I},["ref"])},{name:"start_export",description:"Take a copy of the whole database. Runs a pg_dump on the server and can take minutes, so this STARTS it and returns; poll export_status for the link. Worth doing before a migration you are unsure of. It does not block the database.",inputSchema:H({ref:I},["ref"])},{name:"reset_password",description:"Rotate the project's database password. THE OLD ONE STOPS WORKING within a few seconds, including any DATABASE_URL already written into a file or a running process; connections already open keep working. Returns the new connection URL, which works once the change applies (wait about five seconds before connecting). Use it when a credential has leaked, not routinely.",inputSchema:H({ref:I},["ref"])},{name:"list_teams",description:"Teams this account belongs to, with the id create_project takes as teamId. Without this there is no way to discover that id.",inputSchema:H({})},{name:"list_tokens",description:"The long-lived sdt_ access tokens on this account: id, name, prefix, when each was made and last used. Never the token itself, which is shown once when it is made.",inputSchema:H({})},{name:"create_token",description:"Mint a long-lived sdt_ access token, for a CI job or another machine. RETURNS THE TOKEN ONCE AND NEVER AGAIN. Without `project` it is as capable as this account, so hand it over deliberately; with a project ref it reaches that one project and nothing else, which is what a CI job for one project should get. Optionally expires, as an ISO date.",inputSchema:H({name:I,expires:I,project:I},["name"])},{name:"revoke_token",description:"Revoke an access token by its id. Anything using it stops working at once. Safe to do to a token you think has leaked.",inputSchema:H({id:I},["id"])},{name:"deploy_function",description:"Put a folder of TypeScript on the edge as an HTTP endpoint at https://<ref>.api.snoutdata.com/functions/v1/<name>. Reads functions/<name> unless dir says otherwise. Deno, so no build step and no node_modules. Set openToAnyone ONLY for a webhook receiver whose sender cannot send an API key: it makes the URL callable by anybody who knows it, and the function must then check the sender's signature itself.",inputSchema:H({ref:I,name:I,dir:I,entrypoint:I,openToAnyone:{type:"boolean"}},["ref","name"])},{name:"list_functions",description:"The functions deployed to a project, their URLs, bundle sizes, whether each one needs an API key, and each one's memory and concurrency with the plan's limits for both. Costs nothing.",inputSchema:H({ref:I},["ref"])},{name:"size_function",description:"Set one function's memory (MB one worker may use) and concurrency (how many workers it may run at once), within the project's plan. memoryMb x concurrency may not exceed the project's memory; list_functions shows each function's size and the plan's limits (limit.functionMemoryMb, limit.concurrencyMax, limit.podMemoryMb). A size that does not fit is refused with a sentence saying why. Omit one to keep it as it runs now; reset: true goes back to the plan's default. A second worker only helps CPU-bound work: requests that wait on the network share one.",inputSchema:H({ref:I,name:I,memoryMb:{type:"integer"},concurrency:{type:"integer"},reset:{type:"boolean"}},["ref","name"])},{name:"delete_function",description:"Remove one function from a project. It stops answering within a few seconds. It does not touch the database.",inputSchema:H({ref:I,name:I},["ref","name"])},{name:"list_function_secrets",description:"The NAMES of the environment variables a project's functions run with, their sizes and when each was last set. Never the values, which are not readable by anything but the runtime. THERE IS NO TOOL THAT SETS ONE: a secret in a tool call is a secret in an audit log, so a person sets it with `snoutdata secrets set`. Use this to check whether one a function needs is there.",inputSchema:H({ref:I},["ref"])},{name:"get_project",description:"Everything about one project in one call: its state, region and size, which products are on (auth, storage, data-api), the names of its deployed functions and function secrets, and its custom domains. Never a password, a key or a download link (export_status has the link to the last export).",inputSchema:H({ref:I},["ref"])},{name:"list_products",description:"Whether a project's auth (user sign-up and sign-in), storage (files) and data API (REST and GraphQL over its tables) are on, and whether the plan allows the data API.",inputSchema:H({ref:I},["ref"])},{name:"set_product",description:"Turn auth, storage or data-api on or off for a project. It starts within about a minute. All three are on every plan, including free.",inputSchema:H({ref:I,product:{type:"string",enum:["auth","storage","data-api"]},enabled:{type:"boolean"}},["ref","product","enabled"])},{name:"list_domains",description:"The custom domains in front of a project's API, whether each is verified, and the DNS records each needs.",inputSchema:H({ref:I},["ref"])},{name:"add_domain",description:"Serve a project's API at the user's own hostname (paid plans). Returns the DNS records the user must publish before verify_domain can succeed.",inputSchema:H({ref:I,hostname:I},["ref","hostname"])},{name:"verify_domain",description:"Check a custom domain's DNS records and verify it once they are published.",inputSchema:H({ref:I,hostname:I},["ref","hostname"])},{name:"remove_domain",description:"Stop serving a project at a custom domain.",inputSchema:H({ref:I,hostname:I},["ref","hostname"])},{name:"restore_window",description:"How far back a point-in-time restore of a project can go, or why it cannot (the plan, or no backup yet). Starts nothing.",inputSchema:H({ref:I},["ref"])},{name:"restore_to_point",description:"Rewind a project to a moment (ISO 8601, inside restore_window) into a NEW project beside it, never over it. Costs a project slot on the plan. The original is untouched.",inputSchema:H({ref:I,at:I,name:I},["ref","at"])},{name:"delete_project",description:"Delete a project. THIS DESTROYS A DATABASE. Off unless the person who started this server passed --allow-delete, in which case it still only marks the project deleted and its data is erasable for a grace period.",inputSchema:H({ref:I},["ref"])}],Ql=new Set(["whoami","list_projects","get_connection_url","usage","export_status","list_teams","list_tokens","list_functions","list_function_secrets","get_project","list_products","list_domains","restore_window"]),ed=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]),Is=["get_connection_url","push_migrations","get_project","list_functions","deploy_function","delete_function","list_function_secrets"];function Zn(e){if(!e||typeof e!="object")return e;let t=e;if(Array.isArray(t.projects))return{...t,projects:t.projects.map(Zn)};let n=t.export;if(n&&typeof n=="object"){let{url:r,rolesSql:o,...s}=n;return{...t,export:{...s,hasLink:typeof r=="string"&&r.length>0}}}return e}function er(e,t){return t.available?null:t.pitrEnabled?`${e} has no backup to restore from yet.`:`Point-in-time restore is part of the Pro and Business plans, and this project is on ${t.tier??"another plan"}.`}function td(e){return{...e,annotations:Ql.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:ed.has(e.name)}}}function nd(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function ut(e,t){return{jsonrpc:"2.0",id:e,result:t}}function Qn(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function R(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return ut(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function rd(e,t=new Date){let n=Date.parse(e);return!Number.isFinite(n)||n<=t.getTime()?null:Math.ceil((n-t.getTime())/864e5)}function W(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function Cs(e,t,n){let r=e.id??null,o=e.method??"",s=e.params??{};if(e.id===void 0||e.id===null)return null;switch(o){case"initialize":return ut(r,{protocolVersion:Zl,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return ut(r,{});case"tools/list":return ut(r,{tools:nd([...Yt.map(td),...n.borrowed??[]])});case"tools/call":return od(r,s,t,n);default:return Qn(r,-32601,`unknown method: ${o}`)}}async function od(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(Yt.some(d=>d.name===o)||(r.borrowed?.some(d=>d.name===o)??!1)))return Qn(e,-32602,`unknown tool: ${o}`);let l=["get_connection_url","pause_project","resume_project","delete_project","push_migrations","usage","export_status","start_export","reset_password","get_project","list_products","set_product","list_domains","add_domain","verify_domain","remove_domain","restore_window","restore_to_point"],i=W(s,"ref");if(l.includes(o)&&!i)return R(e,`${o} needs a project ref. Call list_projects to see them.`,!0);let u=i&&n.localProject?n.localProject(i):null;if(u&&!Is.includes(o))return R(e,`${i} is the self-hosted project in ${u.folder}, and ${o} is about SnoutData Cloud. On a local project these tools work: ${Is.join(", ")}.`,!0);try{switch(o){case"whoami":return R(e,await n.whoami());case"list_projects":return R(e,Zn(await n.listProjects()));case"create_project":{let d=W(s,"name");return d?R(e,await n.createProject({name:d,region:W(s,"region")??void 0,teamId:W(s,"teamId")??void 0})):R(e,"create_project needs a name.",!0)}case"get_connection_url":{let d=await n.connection(i);return R(e,{url:d.uri,state:d.state,wakesInstantly:d.wakesInstantly,warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"pause_project":return R(e,await n.setProjectState("pause",i));case"resume_project":return R(e,await n.setProjectState("resume",i));case"usage":{let d=typeof s.days=="number"?s.days:void 0;return R(e,await n.usage(i,d))}case"export_status":return R(e,await n.exportStatus(i));case"start_export":return R(e,await n.requestExport(i));case"reset_password":{let d=await n.resetPassword(i),f=await n.connection(i);return R(e,{url:f.uri,appliesIn:d.appliesIn??"within a few seconds",next:"Wait about five seconds before connecting with this URL: until the change applies the new password is refused and the old one still works. Then update DATABASE_URL wherever it is set.",warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"list_teams":return R(e,await n.listTeams());case"get_project":return R(e,Zn(await n.getProject(i)));case"list_products":return R(e,await n.getProducts(i));case"set_product":{let d=W(s,"product");return d!=="auth"&&d!=="storage"&&d!=="data-api"?R(e,"set_product needs product: auth, storage or data-api.",!0):typeof s.enabled!="boolean"?R(e,"set_product needs enabled: true or false.",!0):R(e,await n.setProduct(i,d,s.enabled))}case"list_domains":return R(e,await n.listDomains(i));case"add_domain":case"verify_domain":case"remove_domain":{let d=W(s,"hostname");return d?R(e,await n.domainAction(i,o.split("_")[0],d)):R(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":{let d=await n.restoreWindow(i),f=d&&d.restore?er(i,d.restore):null;return R(e,f?{...d,reason:f}:d)}case"restore_to_point":{let d=W(s,"at");return d?R(e,await n.restoreTo(i,d,W(s,"name")??void 0)):R(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let d=W(s,"ref"),f=W(s,"name");return!d||!f?R(e,"deploy_function needs a project ref and a function name.",!0):R(e,await n.deployFunction(d,f,{dir:W(s,"dir")??void 0,entrypoint:W(s,"entrypoint")??void 0,verifyJwt:s&&typeof s=="object"&&"openToAnyone"in s?s.openToAnyone!==!0:!0}))}case"list_functions":{let d=W(s,"ref");return d?R(e,await n.listFunctions(d)):R(e,"list_functions needs a project ref.",!0)}case"size_function":{let d=W(s,"ref"),f=W(s,"name");if(!d||!f)return R(e,"size_function needs a project ref and a function name.",!0);let y=typeof s.memoryMb=="number"?s.memoryMb:void 0,m=typeof s.concurrency=="number"?s.concurrency:void 0,p=s.reset===!0;return!p&&y===void 0&&m===void 0?R(e,"size_function needs memoryMb, concurrency, or reset: true.",!0):R(e,await n.sizeFunction(d,f,{memoryMb:y,concurrency:m,reset:p}))}case"delete_function":{let d=W(s,"ref"),f=W(s,"name");return!d||!f?R(e,"delete_function needs a project ref and a function name.",!0):R(e,await n.deleteFunction(d,f))}case"list_function_secrets":{let d=W(s,"ref");return d?R(e,await n.listFunctionSecrets(d)):R(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return R(e,await n.listTokens());case"create_token":{let d=W(s,"name");if(!d)return R(e,"create_token needs a name, so somebody can tell later what it was for.",!0);let f=W(s,"expires"),y=f?rd(f):void 0;return y===null?R(e,`create_token: \`expires\` must be a date in the future, like 2026-12-31. Got "${f}".`,!0):R(e,await n.createToken(d,y,W(s,"project")??void 0))}case"revoke_token":{let d=W(s,"id");return d?R(e,await n.revokeToken(d)):R(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return R(e,await n.pushMigrations(i,{dir:W(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?R(e,await n.setProjectState("delete",i)):R(e,"delete_project is off. The person running this server started it without --allow-delete, so deleting a database is theirs to do. Tell them that is what you wanted.",!0);default:return r.borrowed?.some(d=>d.name===o)?n.callBorrowed?ut(e,await n.callBorrowed(o,s)):R(e,`${o} needs SnoutData Studio, which is no longer answering.`,!0):Qn(e,-32602,`unknown tool: ${o}`)}}catch(d){return R(e,d instanceof Error?d.message:String(d),!0)}}function Ms(e){let t=e.split(`
608
- `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}var Xt=["auth","storage","data-api","push"];function Oe(e){return{error:e instanceof Error?e.message:String(e)}}async function He(e){let[t,n,r,o]=await Promise.all([_("cloud-project-auth",{ref:e,action:"status"}).then(s=>({enabled:s.enabled===!0,google:s.google?.enabled===!0,saml:s.saml?.enabled===!0}),Oe),_("cloud-project-storage",{ref:e}).then(s=>({enabled:s.storage?.enabled===!0,pending:s.storage?.pending===!0,bytes:s.storage?.bytes??null,files:s.storage?.files??null}),Oe),_("cloud-project-data-api",{ref:e}).then(s=>({enabled:s.dataApi?.enabled===!0,allowedOnPlan:s.dataApi?.allowed===!0}),Oe),_("cloud-project-push",{ref:e}).then(s=>({enabled:s.push?.enabled===!0,scheduling:s.push?.scheduling===!0}),Oe)]);return{ref:e,auth:t,storage:n,dataApi:r,push:o,realtime:sd}}var sd={enabled:!0,broadcast:!0,presence:!0,tableChanges:"Plus and Pro plans"};function js(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!Xt.includes(t))throw new h(`say which product: ${Xt.join(", ")}`);return t}async function tr(e,t,n){if(t==="auth"){let r=await _("cloud-project-auth",{ref:e,action:n?"enable":"disable"});return{ref:e,product:t,enabled:n,note:r.note??(n?"Auth starts within a minute.":"Auth stops. Your users stay in the auth schema.")}}return t==="push"?(await _("cloud-project-push",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?"Push starts within a minute. It runs beside the database, so the database restarts once. Set keys with: snoutdata push credentials":"Push is off. Devices, the log and the keys stay in the push schema."}):(await _(t==="storage"?"cloud-project-storage":"cloud-project-data-api",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?`${t} starts within a minute, when the host picks up the change.`:`${t} is off.`})}async function Ls(e){let t=await He(e);g(t,()=>{let n=(l,i,u="")=>[l,i&&typeof i=="object"&&"error"in i?`could not read: ${i.error}`:i.enabled?k("on"):"off",u],r=t.storage,o=t.dataApi;process.stdout.write(`${F([["PRODUCT","STATE",""],n("auth",t.auth),n("storage",r,"pending"in r&&r.pending?"waiting for the host":""),n("data-api",o,"allowedOnPlan"in o&&!o.allowedOnPlan?"not on this plan":""),n("push",t.push,"scheduling"in t.push&&t.push.enabled&&!t.push.scheduling?"scheduled sends on paid plans":""),n("realtime",t.realtime,"always on: broadcast, presence; table changes on Plus and Pro")])}
609
- `);let s=l=>!!(l&&typeof l=="object"&&"enabled"in l&&!l.enabled),a=["auth","storage","data-api","push"].find(l=>s(l==="data-api"?o:t[l]));a&&c(b(` snoutdata products enable ${a} --ref ${e}`))})}async function Us(e,t,n){let r=await tr(e,t,n);g(r,()=>c(r.note))}async function Zt(e){let t=await _("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function nr(e,t,n){if(!n)throw new h(`domains ${t} needs a hostname, e.g. api.example.com`);let r=await _("cloud-project-domain",{ref:e,action:t,hostname:n});return{ref:e,domain:r.domain??null,next:r.next??null}}function ad(e,t){let n=[["TYPE","NAME","VALUE"]];e.record&&n.push([e.record.type,e.record.name,e.record.value]),e.certificateRecord&&n.push([e.certificateRecord.type,e.certificateRecord.name,e.certificateRecord.value]),n.push(["CNAME",e.hostname,`${t}.api.snoutdata.com`]),process.stdout.write(`${F(n)}
610
- `)}async function Fs(e,t,n){if(t===void 0||t==="list"){let o=await Zt(e);g(o,()=>{if(o.domains.length===0){c(`No domains on ${e}. \`snoutdata domains add api.example.com\`.`);return}process.stdout.write(`${F([["HOSTNAME","VERIFIED","CERTIFICATE"],...o.domains.map(s=>[s.hostname,s.verified?"yes":k("no"),s.certificate?.state??""])])}
611
- `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new h(`unknown command: domains ${t}`);let r=await nr(e,t,n??"");g(r,()=>{if(t==="remove"){c(`${n} removed.`);return}r.domain&&(c(r.domain.verified?`${n} is verified.`:r.domain.verificationError??`${n} is not verified yet. Publish these records, then \`snoutdata domains verify ${n}\`:`),r.domain.verified||ad(r.domain,e)),r.next&&c(r.next)})}async function rr(e){return _("cloud-project-restore",{ref:e},"GET")}async function or(e,t,n){if(Number.isNaN(Date.parse(t)))throw new h(`--at needs a moment, e.g. 2026-09-20T14:30:00Z (got "${t}")`);return _("cloud-project-restore",{ref:e,at:new Date(t).toISOString(),...n?{name:n}:{}})}async function Bs(e){let t=await rr(e);g(t,()=>{let n=t.restore,r=er(e,n);if(r){c(r);return}c(`${e} can be restored to any moment from ${n.earliest} to ${n.latest}.`),c(b(` snoutdata db restore --at ${n.latest} --ref ${e}`))})}async function Hs(e,t,n){let r=await or(e,t,n);g(r,()=>{c(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${k(r.project.name)} (${r.project.ref}).`),c(b(" snoutdata projects list to see when it is ready"))})}async function sr(e){let{projects:t}=await re(),n=t.find(l=>l.ref===e);if(!n)throw new h(`no project ${e} on this account. \`snoutdata projects list\`.`);let[r,o,s,a]=await Promise.all([He(e),_("cloud-project-functions",{ref:e}).then(l=>(l.functions??[]).map(i=>i.name),Oe),_("cloud-project-secrets",{ref:e}).then(l=>(l.secrets??[]).map(i=>i.name),Oe),Zt(e).then(l=>l.domains.map(i=>({hostname:i.hostname,verified:i.verified})),Oe)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function Gs(e){let t=await sr(e);g(t,()=>{let n=t,r=s=>s&&typeof s=="object"&&"enabled"in s?s.enabled?"on":"off":"?",o=s=>Array.isArray(s)?s.length?s.map(a=>typeof a=="string"?a:a.hostname).join(", "):"none":"could not read";process.stdout.write(`${F([["name",n.name],["ref",n.ref],["state",n.state],["region",n.region],["auth / storage / data-api / push",`${r(n.products.auth)} / ${r(n.products.storage)} / ${r(n.products.dataApi)} / ${r(n.products.push)}`],["realtime","on (broadcast and presence; table changes on Plus and Pro)"],["functions",o(n.functions)],["secrets",o(n.secretNames)],["domains",o(n.domains)]])}
612
- `)})}J();ee();Le();J();q();import{existsSync as id,mkdirSync as cd,readFileSync as ld,renameSync as dd,writeFileSync as ud}from"node:fs";import{homedir as ar}from"node:os";import{dirname as md,join as fd}from"node:path";import{rootCertificates as pd}from"node:tls";var gd="SNOUTDATA_DB_SSLMODE";function hd(e=ar()){let t=fd(e,".snoutdata","ca-roots.pem"),n=`${pd.join(`
594
+ `:""))}async function ss(e,t){for(let r of t){let o=lt(r.name);if(o)throw new y(o)}let n=Kn(e);for(let r of t)n.set(r.name,r.value);os(e,n),await Jt(e),g({ref:e.ref,set:t.map(r=>r.name)},()=>{for(let r of t)c(`${x(r.name)} set in ${zt(e)}`)})}function Vt(e){return{ref:e.ref,local:e.folder,secrets:[...Kn(e).keys()].sort().map(t=>({name:t}))}}function as(e){let t=Vt(e),n=t.secrets.map(r=>r.name);g(t,()=>{c(n.length?n.join(`
595
+ `):`No function secrets in ${zt(e)}.`)})}async function is(e,t){let n=Kn(e);if(!n.delete(t))throw new C("not-found",`${t} is not set for this local project`);os(e,n),await Jt(e),g({ref:e.ref,unset:t},()=>c(`${t} unset.`))}te();Ge();ke();In();It();K();Gt();z();z();te();ke();K();async function cs(){let{projects:e,allowance:t}=await oe();g({projects:e,allowance:t},()=>{if(e.length===0){c('No projects yet. `snoutdata projects create --name "my project"`.');return}process.stdout.write(`${F(Il(e))}
596
+ `)})}function Il(e){let t=e.some(n=>typeof n.postgresVersion=="number");return[["REF","NAME","STATE","REGION",...t?["POSTGRES"]:[],"LAST CONNECTION"],...e.map(n=>[n.ref,n.name,n.readOnly?x("read-only"):n.state==="ready"?n.state:x(n.state),n.region,...t?[typeof n.postgresVersion=="number"?String(n.postgresVersion):"-"]:[],he(n.lastConnectionAt)])]}async function Jn(e){let t=await S("cloud-project-create",{name:e.name,region:e.region,teamId:e.teamId}),n=t.project.ref;c(`Created ${n}.`);let r=t.project;if(e.wait){c("Waiting for it to start\u2026");let s="";r=await ct(n,{timeoutMs:e.timeoutMs,onTick:a=>{a!==s&&(s=a,c(b(` ${a}`)))}})}e.link&&Ue(e.link,{ref:n,name:r.name});let o=`postgres://${r.user}:${encodeURIComponent(t.password)}@${r.host}:5432/${r.database}?sslmode=require`;g({project:r,ref:n,host:r.host,database:r.database,user:r.user,...e.showUrl?{password:t.password,uri:o}:{connectionString:"snoutdata db url"}},()=>{process.stdout.write(`${e.showUrl?o:n}
597
+ `),c(""),c(`Ready. ${x(n)} in ${r.region}, at ${r.host}.`),c(e.showUrl?"The password is stored for you: `snoutdata db url` prints this again.":"`snoutdata db url` prints the connection string (it holds the password, so it is not shown here).")})}async function ls(e,t,n={}){let r=`cloud-project-${e}`,o=await S(r,{ref:t}),s=e==="delete"?"deleted":`${e}d`;if(n.wait===!1){g({...o,settled:!1},()=>c(`${t}: asked to be ${s}. \`snoutdata projects list\` shows when it is.`));return}let a=o.project?.state??"",d=await it(t,e,{timeoutMs:n.timeoutMs,onTick:u=>{u!==a&&(a=u,c(b(` ${u}`)))}}),i=d?.state??"deleted";if(i==="error")throw new C("failed",`${t} went to error instead of ${s}${d?.stateDetail?`: ${d.stateDetail}`:""}.`,{ref:t,state:i});g({...o,...d?{project:d}:{},settled:!0},()=>{c(o.changed?`${t}: ${s}.`:`${t} was already ${s}.`)})}async function ds(e){let t=await S("cloud-project-reset-password",{ref:e});g(t,()=>{process.stdout.write(`${t.password}
598
+ `),c(`New password for ${t.user}. It applies ${t.appliesIn}.`)})}async function us(){let{teams:e}=await qt();g({teams:e},()=>{if(e.length===0){c("You are not on any teams.");return}process.stdout.write(`${F([["NAME","ID","SHARE"],...e.map(t=>[t.name,t.id,t.mayShare?"yes":t.why??"no"])])}
599
+ `)})}async function ms(e){let{teams:t}=await qt(),n=t.find(s=>s.id===e),r=n?[n]:t.filter(s=>s.name.toLowerCase()===e.toLowerCase());if(r.length===0)throw new Error(t.length===0?`You are not on any teams, so there is no "${e}" to share with.`:`No team called "${e}". You are on: ${t.map(s=>s.name).join(", ")}.`);if(r.length>1)throw new Error(`More than one team is called "${e}". Use the id: ${r.map(s=>s.id).join(", ")}.`);let o=r[0];if(!o.mayShare)throw new Error(`${o.name}: ${o.why??"you cannot share a project into this team"}.`);return o.id}z();K();async function fs(e){let t=await S("cloud-token-create",{name:e.name,expiresInDays:e.expiresInDays,...e.project?{project:e.project}:{}});g(t,()=>{process.stdout.write(`${t.token}
600
+ `),c(""),c(`${x(t.name)} \u2014 ${t.expiresAt?`expires ${t.expiresAt.slice(0,10)}`:"does not expire"}.`),c(t.project?`Reaches project ${t.project} only.`:"Reaches every project on this account."),c("This is the only time it is shown: only its hash is stored."),c(b(" export SNOUTDATA_ACCESS_TOKEN=<that token>"))})}async function ps(){let{tokens:e}=await S("cloud-token-list");g({tokens:e},()=>{if(e.length===0){c("No access tokens. `snoutdata tokens create --name ci`.");return}process.stdout.write(`${F([["PREFIX","NAME","PROJECT","STATE","LAST USED","EXPIRES"],...e.map(t=>[t.current?x(t.prefix):t.prefix,t.name,t.project??"all",t.revokedAt?"revoked":t.live?"live":"expired",he(t.lastUsedAt),t.expiresAt?t.expiresAt.slice(0,10):"never"])])}
601
+ `)})}async function gs(e){let t=e.startsWith("sdt_")?{prefix:e}:{id:e},n=await S("cloud-token-revoke",t);g(n,()=>{c(n.changed?`Revoked ${n.token.prefix} (${n.token.name}).`:`${n.token.prefix} was already revoked.`)})}z();re();K();async function hs(e){let t=await ne(e);g({ref:t.ref,anonKey:t.anonKey,serviceRoleKey:t.serviceRoleKey,issuedAt:t.keysIssuedAt,expiresAt:t.keysExpireAt},()=>{if(!t.anonKey||!t.serviceRoleKey){c(`${e} has no API keys yet. Ask again in a moment.`);return}process.stdout.write(`${ws(t.anonKey,t.serviceRoleKey)}
602
+ `),jl(t.keysIssuedAt)})}async function ys(e,t){if(!t.force)throw c(`Rotating the keys for ${e} stops every anon and service_role key already pasted anywhere.`),c("Anything holding one (a deployed app, a CI secret, a teammate) breaks until it is updated."),new y("keys rotate needs --force, because this cannot be undone");let n=await S("cloud-project-rotate-jwt",{ref:e});g(n,()=>{process.stdout.write(`${ws(n.anonKey,n.serviceRoleKey)}
603
+ `),c(""),c(n.breaks),c(b(" snoutdata keys --json to read them again later"))})}function ws(e,t){return F([["KEY","VALUE"],["anon",e],["service_role",t]])}function jl(e){c(""),c(`${x("anon")} is the public one: it goes in a browser, and row-level security decides what it reads.`),c(`${x("service_role")} bypasses row-level security. Server side only, never in a client bundle.`),e&&c(b(` issued ${e.slice(0,10)}`))}z();re();K();import{readdir as Ul,readFile as Fl,stat as Bl}from"node:fs/promises";import{join as zn,relative as Hl,sep as Gl}from"node:path";var bs=["functions"];async function Wn(e){try{return(await Bl(e)).isDirectory()}catch{return!1}}async function ql(e,t){if(t){if(!await Wn(t))throw new y(`${t} is not a directory`);return t}for(let n of bs){let r=zn(n,e);if(await Wn(r))return r}throw new y(`no folder found for ${e} at ${bs[0]}/${e}; use --dir to say where it is.`)}async function Ss(e){let t=[];async function n(r){for(let o of await Ul(r,{withFileTypes:!0})){if(o.name.startsWith(".")||o.name==="node_modules")continue;let s=zn(r,o.name);if(o.isDirectory()){await n(s);continue}if(!o.isFile())continue;let a=await Fl(s),d=a.toString("utf8");if(Buffer.compare(Buffer.from(d,"utf8"),a)!==0)throw new y(`${s} is not text, and a function bundle is source code. Put binary assets in Storage.`);t.push({path:Hl(e,s).split(Gl).join("/"),text:d})}}return await n(e),t}async function Kl(e,t,n){let r=await Ss(t);if(r.length===0)throw new y(`${t} has no files in it`);let o=n??(r.some(d=>d.path==="index.ts")?"index.ts":"");if(!o)throw new y(`${t} has no index.ts, so there is nothing to start. Name the file with --entrypoint.`);let s=zn(t,"..","_shared");if(!await Wn(s))return{entrypoint:o,files:r};let a=(await Ss(s)).map(d=>({path:`_shared/${d.path}`,text:d.text}));return{entrypoint:`${e}/${o}`,files:[...r.map(d=>({path:`${e}/${d.path}`,text:d.text})),...a]}}async function Vn(e,t,n){De(t);let r=await ql(t,n.dir),{entrypoint:o,files:s}=await Kl(t,r,n.entrypoint),a=zo({entrypoint:o,files:s});return{answer:await S("cloud-project-functions",{ref:e,action:"deploy",name:t,verifyJwt:n.verifyJwt,bundle:a}),directory:r,files:s.length,bytes:JSON.stringify(a).length}}async function _s(e,t,n){let{answer:r,directory:o,files:s,bytes:a}=await Vn(e,t,n),d=r.functions.find(i=>i.name===t);g(r,()=>{let i=Math.max(1,Math.round(a/1024));c(`${x(t)} deployed from ${o} (${s} file${s===1?"":"s"}, ${i} KB)`),d&&c(` ${d.url}`),n.verifyJwt||(c(""),c(x(" Anybody who knows this URL can run it. No API key is required.")),c(b(" That is what a webhook receiver needs. Check the sender signature inside the function."))),r.limit.maxFunctions!==null&&c(b(` ${r.limit.deployed} of ${r.limit.maxFunctions} functions on this plan`))})}function Yn(e){return S("cloud-project-functions",{ref:e})}function Xn(e,t){return De(t),S("cloud-project-functions",{ref:e,action:"delete",name:t})}function Zn(e,t,n){if(De(t),!n.reset&&n.memoryMb===void 0&&n.concurrency===void 0)throw new y("functions size needs --memory MB, --concurrency N, or --reset");return S("cloud-project-functions",{ref:e,action:"size",name:t,...n.reset?{reset:!0}:{memoryMb:n.memoryMb??null,concurrency:n.concurrency??null}})}function Jl(e,t){if(e.memoryMb==null||e.concurrency==null)return null;let n=e.memoryMb*e.concurrency,r=t.podMemoryMb?` of ${t.podMemoryMb} MB`:"";return`${e.memoryMb} MB x ${e.concurrency} worker${e.concurrency===1?"":"s"} = ${n} MB${r}`}function xs(e){return e.functionMemoryMb==null||e.concurrencyMax==null||e.podMemoryMb==null?null:`${e.tier??"this"} plan: up to ${e.functionMemoryMb} MB a worker and ${e.concurrencyMax} workers a function, memory x workers up to ${e.podMemoryMb} MB`}async function ks(e,t,n){let r=await Zn(e,t,n),o=r.functions.find(s=>s.name===t);g(r,()=>{let s=o?Jl(o,r.limit):null;c(`${x(t)} ${n.reset?"is back to the plan default":"is resized"}${s?`: ${s}`:""}`),c(b(" The next request runs at it; workers already running finish what they hold."));let a=xs(r.limit);a&&c(b(` ${a}`))})}async function vs(e){let t=await Yn(e);g(t,()=>{if(t.functions.length===0){c(`${e} has no functions.`),c(b(" snoutdata functions deploy <name> from functions/<name>"));return}process.stdout.write(`${F([["NAME","KEY","SIZE","MEMORY","WORKERS","URL"],...t.functions.map(r=>[r.name,r.verifyJwt?"required":x("open"),r.bytes===null?"-":`${Math.max(1,Math.round(r.bytes/1024))} KB`,r.memoryMb==null?"-":`${r.memoryMb} MB`,r.concurrency==null?"-":String(r.concurrency),r.url])])}
604
+ `),t.limit.maxFunctions!==null&&c(b(`${t.limit.deployed} of ${t.limit.maxFunctions} on this plan`));let n=xs(t.limit);n&&c(b(`${n}. Change one with: snoutdata functions size <name> --memory MB --concurrency N`))})}async function Es(e,t){let n=await Xn(e,t);g(n,()=>{c(n.live?`${t} is gone.`:`${t} is gone. It stops answering within a few seconds.`)})}z();re();K();function $s(e){let t=e.indexOf("=");return t<=0?null:{name:e.slice(0,t),value:e.slice(t+1)}}async function Ps(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\n$/,"")}async function As(e,t){if(e.length===0)throw new y("secrets set needs NAME=value, or NAME with --stdin");if(t.stdin){if(e.length!==1)throw new y("--stdin sets one secret, so it takes one NAME");return[{name:e[0],value:await Ps()}]}return e.map(n=>{let r=$s(n);if(!r)throw new y(`${n} is not NAME=value. Use --stdin to set a value from a pipe.`);return r})}async function Ts(e,t,n){if(t.length===0)throw new y("secrets set needs NAME=value, or NAME with --stdin");let r=[];if(n.stdin){if(t.length!==1)throw new y("--stdin sets one secret, so it takes one NAME");r.push({name:t[0],value:await Ps()})}else for(let s of t){let a=$s(s);if(!a)throw new y(`${s} is not NAME=value. Use --stdin to set a value from a pipe.`);r.push(a)}for(let s of r){let a=lt(s.name);if(a)throw new y(a)}let o=null;for(let s of r)o=await S("cloud-project-secrets",{ref:e,action:"set",name:s.name,value:s.value});g(o,()=>{for(let s of r)c(`${x(s.name)} set`);c(b(" Functions pick it up within a few seconds. Redeploying is not needed.")),n.stdin||c(b(" A value typed here is in your shell history. In CI, use: \u2026 | snoutdata secrets set NAME --stdin"))})}async function Rs(e,t){let n=lt(t);if(n)throw new y(n);let r=await S("cloud-project-secrets",{ref:e,action:"unset",name:t});g(r,()=>{c(`${t} is gone.`)})}async function Os(e){let t=await S("cloud-project-secrets",{ref:e});g(t,()=>{if(t.secrets.length===0){c(`${e} has no function secrets.`),c(b(" snoutdata secrets set STRIPE_KEY=sk_..."));return}process.stdout.write(`${F([["NAME","SIZE","SET"],...t.secrets.map(n=>[n.name,n.bytes===null?"-":`${n.bytes} B`,he(n.updatedAt)])])}
605
+ `),c(b("Values are not readable, here or anywhere. Set one again to change it."))})}import{spawn as mt}from"node:child_process";import{appendFileSync as zs,existsSync as tn,readFileSync as Vs,statSync as xd,unlinkSync as kd,writeFileSync as ze}from"node:fs";import{dirname as vd,join as nn}from"node:path";import{tmpdir as Ed}from"node:os";import{open as Ys}from"node:fs/promises";function Ns(e){let t=(r,o)=>String.fromCharCode(...Array.from(e.slice(r,r+o)));return t(0,5)==="PGDMP"?"custom":e.length>=262&&t(257,5)==="ustar"?"tar":Array.from(e.slice(0,256)).every(r=>r===9||r===10||r===13||r>=32&&r<127||r>=128)&&e.length>0?"plain":"unknown"}function Ds(e,t,n={}){return t.readOnly?{ok:!1,code:"read-only",reason:"this project is over its plan's storage limit, so it refuses writes. A restore would fail part way. Delete some rows or change plan first."}:e==="unknown"?{ok:!1,code:"unknown-format",reason:"that file is not a pg_dump archive and does not look like SQL. `snoutdata db export` writes the custom format; psql reads plain .sql."}:!n.force&&t.existingTables>0?{ok:!1,code:"not-empty",reason:`this database already has ${t.existingTables} table${t.existingTables===1?"":"s"}. Restoring into it would leave some objects created and some not. Check --ref is the project you meant, or pass --force.`}:{ok:!0,tool:e==="plain"?"psql":"pg_restore",format:e}}function Is(e){let t=e.split(`
606
+ `).filter(r=>/^pg_restore: error:/.test(r.trim())),n=t.filter(r=>!/must be owner of extension/.test(r));return{total:t.length,harmless:t.length-n.length,fatal:n}}var Qn={auth:"snout_auth_admin",storage:"snout_storage_admin",push:"snout_push_admin"},Vl=/^(snoutpod_admin|snout_[a-z]+_admin)$/,Yt=new Set(["auth","storage","push","realtime","snout_realtime","_realtime","graphql","graphql_public","extensions","net","cron","pgbouncer","vault","pgsodium"]),Yl=/^(pgrst_ddl_watch|pgrst_drop_watch|snoutpod_[a-z_]+|snoutdata_realtime|snout_realtime_messages)(\(|$)/,Xl={auth:"auth",storage:"storage",push:"push"},Zl=new Set(["push.settings"]),Ql=["TABLE DATA","SEQUENCE SET","SEQUENCE OWNED BY","FK CONSTRAINT","CHECK CONSTRAINT","DEFAULT ACL","EVENT TRIGGER","ROW SECURITY","INDEX ATTACH","TABLE ATTACH","MATERIALIZED VIEW DATA","MATERIALIZED VIEW","PUBLICATION TABLES IN SCHEMA","PUBLICATION TABLE","FOREIGN TABLE","FOREIGN DATA WRAPPER","FOREIGN SERVER","USER MAPPING","TEXT SEARCH CONFIGURATION","TEXT SEARCH DICTIONARY","TEXT SEARCH PARSER","TEXT SEARCH TEMPLATE","LARGE OBJECT","PROCEDURAL LANGUAGE","OPERATOR CLASS","OPERATOR FAMILY","ACCESS METHOD","EXTENDED STATISTICS","DATABASE PROPERTIES"];function ed(e){let t=/^\d+; \d+ \d+ (.+)$/.exec(e.trim());if(!t)return null;let n=t[1],r=Ql.find(i=>n.startsWith(`${i} `))??n.split(" ")[0],o=n.slice(r.length).trim().split(" "),s=o[0]??"-",a=o.length>2?o[o.length-1]:"",d=o.slice(1,o.length>2?-1:void 0).join(" ");return{line:e.trim(),type:r,schema:s,name:d,owner:a}}function td(e){let t=new Map,n=o=>o.replace(/"/g,""),r=/ALTER TABLE (?:ONLY )?([\w."$]+)\s+ADD CONSTRAINT [^;]*?FOREIGN KEY \([^)]*\) REFERENCES ([\w."$]+)\(/g;for(let o of e.matchAll(r)){let s=n(o[1]),a=n(o[2]);if(s!==a){let d=t.get(s)??new Set;d.add(a),t.set(s,d)}}return t}function Cs(e,t){let n=new Map,r=[],o=new Set,s=0;for(let i of e.split(`
607
+ `)){let u=ed(i);if(!u)continue;let{type:l,schema:f,name:w,owner:R}=u;if(l==="TABLE DATA"||l==="SEQUENCE SET"){let p=/^(schema_migrations|migrations)$/.test(w)&&Yt.has(f);if(/realtime/.test(f)||p||f==="public"&&w==="spatial_ref_sys"){s++;continue}if(f==="cron"){s++,o.add("Scheduled jobs (pg_cron) are not restored: each names the project it came from. Schedule them again with cron.schedule.");continue}if(f==="push"&&w==="credentials"){s++,o.add("Push credentials are not restored: they are sealed for the project they came from. Set them again with `snoutdata push credentials set`.");continue}let h=Xl[f];if(h){h==="storage"&&w==="objects"&&o.add("Storage objects are restored as rows; the files themselves are not part of an export. Upload them again into this project."),n.set(h,[...n.get(h)??[],u]);continue}if(Yt.has(f)){s++;continue}r.push(u.line);continue}if(l==="COMMENT"&&f==="-"&&/^EXTENSION( |$)/.test(w)||(l==="SCHEMA"?Yt.has(w):Yt.has(f))||Yl.test(w)||Vl.test(R)&&l!=="SCHEMA"){s++;continue}r.push(u.line)}let a=td(t),d=["auth","storage","push"];return{platformData:d.filter(i=>n.has(i)).map(i=>{let u=n.get(i),l=u.filter(w=>w.type==="TABLE DATA"&&Zl.has(`${w.schema}.${w.name}`)).map(w=>`${w.schema}.${w.name}`),f=u.filter(w=>w.type==="TABLE DATA").map(w=>`${w.schema}.${w.name}`);return{product:i,clear:l,tables:f,lines:nd(u,a)}}),rest:r,products:d.filter(i=>n.has(i)),skipped:s,notes:[...o]}}function nd(e,t){let n=d=>`${d.schema}.${d.name}`,r=new Set(e.map(n)),o=new Set,s=[],a=[...e];for(;a.length>0;){let i=a.find(u=>[...t.get(n(u))??[]].every(l=>!r.has(l)||o.has(l)||l===n(u)))??a[0];o.add(n(i)),s.push(i.line),a=a.filter(u=>u!==i)}return s}z();re();K();var rd="2024-11-05";function G(e,t=[]){return{type:"object",properties:e,additionalProperties:!1,...t.length?{required:t}:{}}}var M={type:"string"},Xt=[{name:"whoami",description:"Who this CLI is signed in as, and how (a session or a long-lived access token). Use it to check a credential before doing anything else.",inputSchema:G({})},{name:"list_projects",description:"Every hosted Postgres database on this account, with its ref, name, region, state (ready, paused, error), size, and whether it is read-only because it is over its plan's storage limit. Under `local`, the self-hosted projects on this machine (SnoutData Studio's, or linked with `snoutdata link --local`), whose refs work with get_connection_url, push_migrations, get_project and the function tools.",inputSchema:G({})},{name:"create_project",description:"Make a new hosted Postgres database and wait for it to be ready. Costs a project against the plan allowance; a free plan includes two. Returns its ref, which every other tool takes.",inputSchema:G({name:M,region:M,teamId:M},["name"])},{name:"get_connection_url",description:"The postgres:// URL for a project, ready to put in DATABASE_URL. IT CONTAINS A LIVE PASSWORD, so treat it as a credential and do not print it into a log or a file that is committed. Connecting to a paused project wakes it, which takes a few seconds.",inputSchema:G({ref:M},["ref"])},{name:"pause_project",description:"Stop a project, and wait until it is paused. Its data is kept and the next connection wakes it. A production project refuses this.",inputSchema:G({ref:M},["ref"])},{name:"resume_project",description:"Start a paused project without waiting for a connection to wake it, and wait until it is ready.",inputSchema:G({ref:M},["ref"])},{name:"push_migrations",description:"Run the .sql files in a folder against a project, in name order, once each, recording what ran in a _snoutdata_migrations table. Refuses a file that changed after it ran, a file that has gone, and a new file that sorts before one already applied. Use dryRun first to see what would happen. Needs psql on this machine.",inputSchema:G({ref:M,dir:M,dryRun:{type:"boolean"}},["ref"])},{name:"usage",description:"How much storage and compute a project has used against the limit on this plan, and whether it is near it. READ THIS BEFORE a migration or a bulk insert: a project over its storage limit is made read-only, and `list_projects` will say readOnly afterwards without ever saying how much headroom was left. Takes an optional number of days of history (default 30, maximum 365).",inputSchema:G({ref:M,days:{type:"number"}},["ref"])},{name:"export_status",description:"Whether a copy of this database is being taken, when the last one finished, and a download link if there is a live one. Costs nothing and starts nothing.",inputSchema:G({ref:M},["ref"])},{name:"start_export",description:"Take a copy of the whole database. Runs a pg_dump on the server and can take minutes, so this STARTS it and returns; poll export_status for the link. Worth doing before a migration you are unsure of. It does not block the database.",inputSchema:G({ref:M},["ref"])},{name:"reset_password",description:"Rotate the project's database password. THE OLD ONE STOPS WORKING within a few seconds, including any DATABASE_URL already written into a file or a running process; connections already open keep working. Returns the new connection URL, which works once the change applies (wait about five seconds before connecting). Use it when a credential has leaked, not routinely.",inputSchema:G({ref:M},["ref"])},{name:"list_teams",description:"Teams this account belongs to, with the id create_project takes as teamId. Without this there is no way to discover that id.",inputSchema:G({})},{name:"list_tokens",description:"The long-lived sdt_ access tokens on this account: id, name, prefix, when each was made and last used. Never the token itself, which is shown once when it is made.",inputSchema:G({})},{name:"create_token",description:"Mint a long-lived sdt_ access token, for a CI job or another machine. RETURNS THE TOKEN ONCE AND NEVER AGAIN. Without `project` it is as capable as this account, so hand it over deliberately; with a project ref it reaches that one project and nothing else, which is what a CI job for one project should get. Optionally expires, as an ISO date.",inputSchema:G({name:M,expires:M,project:M},["name"])},{name:"revoke_token",description:"Revoke an access token by its id. Anything using it stops working at once. Safe to do to a token you think has leaked.",inputSchema:G({id:M},["id"])},{name:"deploy_function",description:"Put a folder of TypeScript on the edge as an HTTP endpoint at https://<ref>.api.snoutdata.com/functions/v1/<name>. Reads functions/<name> unless dir says otherwise. Deno, so no build step and no node_modules. Set openToAnyone ONLY for a webhook receiver whose sender cannot send an API key: it makes the URL callable by anybody who knows it, and the function must then check the sender's signature itself.",inputSchema:G({ref:M,name:M,dir:M,entrypoint:M,openToAnyone:{type:"boolean"}},["ref","name"])},{name:"list_functions",description:"The functions deployed to a project, their URLs, bundle sizes, whether each one needs an API key, and each one's memory and concurrency with the plan's limits for both. Costs nothing.",inputSchema:G({ref:M},["ref"])},{name:"size_function",description:"Set one function's memory (MB one worker may use) and concurrency (how many workers it may run at once), within the project's plan. memoryMb x concurrency may not exceed the project's memory; list_functions shows each function's size and the plan's limits (limit.functionMemoryMb, limit.concurrencyMax, limit.podMemoryMb). A size that does not fit is refused with a sentence saying why. Omit one to keep it as it runs now; reset: true goes back to the plan's default. A second worker only helps CPU-bound work: requests that wait on the network share one.",inputSchema:G({ref:M,name:M,memoryMb:{type:"integer"},concurrency:{type:"integer"},reset:{type:"boolean"}},["ref","name"])},{name:"delete_function",description:"Remove one function from a project. It stops answering within a few seconds. It does not touch the database.",inputSchema:G({ref:M,name:M},["ref","name"])},{name:"list_function_secrets",description:"The NAMES of the environment variables a project's functions run with, their sizes and when each was last set. Never the values, which are not readable by anything but the runtime. THERE IS NO TOOL THAT SETS ONE: a secret in a tool call is a secret in an audit log, so a person sets it with `snoutdata secrets set`. Use this to check whether one a function needs is there.",inputSchema:G({ref:M},["ref"])},{name:"get_project",description:"Everything about one project in one call: its state, region and size, which products are on (auth, storage, data-api), the names of its deployed functions and function secrets, and its custom domains. Never a password, a key or a download link (export_status has the link to the last export).",inputSchema:G({ref:M},["ref"])},{name:"list_products",description:"Whether a project's auth (user sign-up and sign-in), storage (files) and data API (REST and GraphQL over its tables) are on, and whether the plan allows the data API.",inputSchema:G({ref:M},["ref"])},{name:"set_product",description:"Turn auth, storage or data-api on or off for a project. It starts within about a minute. All three are on every plan, including free.",inputSchema:G({ref:M,product:{type:"string",enum:["auth","storage","data-api"]},enabled:{type:"boolean"}},["ref","product","enabled"])},{name:"list_domains",description:"The custom domains in front of a project's API, whether each is verified, and the DNS records each needs.",inputSchema:G({ref:M},["ref"])},{name:"add_domain",description:"Serve a project's API at the user's own hostname (paid plans). Returns the DNS records the user must publish before verify_domain can succeed.",inputSchema:G({ref:M,hostname:M},["ref","hostname"])},{name:"verify_domain",description:"Check a custom domain's DNS records and verify it once they are published.",inputSchema:G({ref:M,hostname:M},["ref","hostname"])},{name:"remove_domain",description:"Stop serving a project at a custom domain.",inputSchema:G({ref:M,hostname:M},["ref","hostname"])},{name:"restore_window",description:"How far back a point-in-time restore of a project can go, or why it cannot (the plan, or no backup yet). Starts nothing.",inputSchema:G({ref:M},["ref"])},{name:"restore_to_point",description:"Rewind a project to a moment (ISO 8601, inside restore_window) into a NEW project beside it, never over it. Costs a project slot on the plan. The original is untouched.",inputSchema:G({ref:M,at:M,name:M},["ref","at"])},{name:"delete_project",description:"Delete a project. THIS DESTROYS A DATABASE. Off unless the person who started this server passed --allow-delete, in which case it still only marks the project deleted and its data is erasable for a grace period.",inputSchema:G({ref:M},["ref"])}],od=new Set(["whoami","list_projects","get_connection_url","usage","export_status","list_teams","list_tokens","list_functions","list_function_secrets","get_project","list_products","list_domains","restore_window"]),sd=new Set(["delete_project","delete_function","reset_password","revoke_token","remove_domain"]),Ms=["get_connection_url","push_migrations","get_project","list_functions","deploy_function","delete_function","list_function_secrets"];function er(e){if(!e||typeof e!="object")return e;let t=e;if(Array.isArray(t.projects))return{...t,projects:t.projects.map(er)};let n=t.export;if(n&&typeof n=="object"){let{url:r,rolesSql:o,...s}=n;return{...t,export:{...s,hasLink:typeof r=="string"&&r.length>0}}}return e}function nr(e,t){return t.available?null:t.pitrEnabled?`${e} has no backup to restore from yet.`:`Point-in-time restore is part of the Pro and Business plans, and this project is on ${t.tier??"another plan"}.`}function ad(e){return{...e,annotations:od.has(e.name)?{readOnlyHint:!0}:{readOnlyHint:!1,destructiveHint:sd.has(e.name)}}}function id(e){let t=new Set;return e.filter(n=>!t.has(n.name)&&t.add(n.name))}function ut(e,t){return{jsonrpc:"2.0",id:e,result:t}}function tr(e,t,n){return{jsonrpc:"2.0",id:e,error:{code:t,message:n}}}function T(e,t,n=!1){let r=typeof t=="string"?t:JSON.stringify(t,null,2);return ut(e,{content:[{type:"text",text:r}],...n?{isError:!0}:{}})}function cd(e,t=new Date){let n=Date.parse(e);return!Number.isFinite(n)||n<=t.getTime()?null:Math.ceil((n-t.getTime())/864e5)}function W(e,t){let n=e[t];return typeof n=="string"&&n.trim().length>0?n.trim():null}async function js(e,t,n){let r=e.id??null,o=e.method??"",s=e.params??{};if(e.id===void 0||e.id===null)return null;switch(o){case"initialize":return ut(r,{protocolVersion:rd,capabilities:{tools:{listChanged:!1}},serverInfo:{name:"snoutdata",version:n.version}});case"ping":return ut(r,{});case"tools/list":return ut(r,{tools:id([...Xt.map(ad),...n.borrowed??[]])});case"tools/call":return ld(r,s,t,n);default:return tr(r,-32601,`unknown method: ${o}`)}}async function ld(e,t,n,r){let o=typeof t.name=="string"?t.name:"",s=t.arguments??{};if(!(Xt.some(l=>l.name===o)||(r.borrowed?.some(l=>l.name===o)??!1)))return tr(e,-32602,`unknown tool: ${o}`);let d=["get_connection_url","pause_project","resume_project","delete_project","push_migrations","usage","export_status","start_export","reset_password","get_project","list_products","set_product","list_domains","add_domain","verify_domain","remove_domain","restore_window","restore_to_point"],i=W(s,"ref");if(d.includes(o)&&!i)return T(e,`${o} needs a project ref. Call list_projects to see them.`,!0);let u=i&&n.localProject?n.localProject(i):null;if(u&&!Ms.includes(o))return T(e,`${i} is the self-hosted project in ${u.folder}, and ${o} is about SnoutData Cloud. On a local project these tools work: ${Ms.join(", ")}.`,!0);try{switch(o){case"whoami":return T(e,await n.whoami());case"list_projects":return T(e,er(await n.listProjects()));case"create_project":{let l=W(s,"name");return l?T(e,await n.createProject({name:l,region:W(s,"region")??void 0,teamId:W(s,"teamId")??void 0})):T(e,"create_project needs a name.",!0)}case"get_connection_url":{let l=await n.connection(i);return T(e,{url:l.uri,state:l.state,wakesInstantly:l.wakesInstantly,warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"pause_project":return T(e,await n.setProjectState("pause",i));case"resume_project":return T(e,await n.setProjectState("resume",i));case"usage":{let l=typeof s.days=="number"?s.days:void 0;return T(e,await n.usage(i,l))}case"export_status":return T(e,await n.exportStatus(i));case"start_export":return T(e,await n.requestExport(i));case"reset_password":{let l=await n.resetPassword(i),f=await n.connection(i);return T(e,{url:f.uri,appliesIn:l.appliesIn??"within a few seconds",next:"Wait about five seconds before connecting with this URL: until the change applies the new password is refused and the old one still works. Then update DATABASE_URL wherever it is set.",warning:"This URL contains a live database password. Put it in an environment variable, not in a file that is committed."})}case"list_teams":return T(e,await n.listTeams());case"get_project":return T(e,er(await n.getProject(i)));case"list_products":return T(e,await n.getProducts(i));case"set_product":{let l=W(s,"product");return l!=="auth"&&l!=="storage"&&l!=="data-api"?T(e,"set_product needs product: auth, storage or data-api.",!0):typeof s.enabled!="boolean"?T(e,"set_product needs enabled: true or false.",!0):T(e,await n.setProduct(i,l,s.enabled))}case"list_domains":return T(e,await n.listDomains(i));case"add_domain":case"verify_domain":case"remove_domain":{let l=W(s,"hostname");return l?T(e,await n.domainAction(i,o.split("_")[0],l)):T(e,`${o} needs a hostname, e.g. api.example.com.`,!0)}case"restore_window":{let l=await n.restoreWindow(i),f=l&&l.restore?nr(i,l.restore):null;return T(e,f?{...l,reason:f}:l)}case"restore_to_point":{let l=W(s,"at");return l?T(e,await n.restoreTo(i,l,W(s,"name")??void 0)):T(e,"restore_to_point needs at: an ISO 8601 moment inside restore_window.",!0)}case"deploy_function":{let l=W(s,"ref"),f=W(s,"name");return!l||!f?T(e,"deploy_function needs a project ref and a function name.",!0):T(e,await n.deployFunction(l,f,{dir:W(s,"dir")??void 0,entrypoint:W(s,"entrypoint")??void 0,verifyJwt:s&&typeof s=="object"&&"openToAnyone"in s?s.openToAnyone!==!0:!0}))}case"list_functions":{let l=W(s,"ref");return l?T(e,await n.listFunctions(l)):T(e,"list_functions needs a project ref.",!0)}case"size_function":{let l=W(s,"ref"),f=W(s,"name");if(!l||!f)return T(e,"size_function needs a project ref and a function name.",!0);let w=typeof s.memoryMb=="number"?s.memoryMb:void 0,R=typeof s.concurrency=="number"?s.concurrency:void 0,m=s.reset===!0;return!m&&w===void 0&&R===void 0?T(e,"size_function needs memoryMb, concurrency, or reset: true.",!0):T(e,await n.sizeFunction(l,f,{memoryMb:w,concurrency:R,reset:m}))}case"delete_function":{let l=W(s,"ref"),f=W(s,"name");return!l||!f?T(e,"delete_function needs a project ref and a function name.",!0):T(e,await n.deleteFunction(l,f))}case"list_function_secrets":{let l=W(s,"ref");return l?T(e,await n.listFunctionSecrets(l)):T(e,"list_function_secrets needs a project ref.",!0)}case"list_tokens":return T(e,await n.listTokens());case"create_token":{let l=W(s,"name");if(!l)return T(e,"create_token needs a name, so somebody can tell later what it was for.",!0);let f=W(s,"expires"),w=f?cd(f):void 0;return w===null?T(e,`create_token: \`expires\` must be a date in the future, like 2026-12-31. Got "${f}".`,!0):T(e,await n.createToken(l,w,W(s,"project")??void 0))}case"revoke_token":{let l=W(s,"id");return l?T(e,await n.revokeToken(l)):T(e,"revoke_token needs the id of a token. list_tokens has them.",!0)}case"push_migrations":return T(e,await n.pushMigrations(i,{dir:W(s,"dir")??void 0,dryRun:s.dryRun===!0}));case"delete_project":return r.allowDelete?T(e,await n.setProjectState("delete",i)):T(e,"delete_project is off. The person running this server started it without --allow-delete, so deleting a database is theirs to do. Tell them that is what you wanted.",!0);default:return r.borrowed?.some(l=>l.name===o)?n.callBorrowed?ut(e,await n.callBorrowed(o,s)):T(e,`${o} needs SnoutData Studio, which is no longer answering.`,!0):tr(e,-32602,`unknown tool: ${o}`)}}catch(l){return T(e,l instanceof Error?l.message:String(l),!0)}}function Ls(e){let t=e.split(`
608
+ `),n=t.pop()??"";return{messages:t.map(r=>r.trim()).filter(r=>r.length>0),rest:n}}var Zt=["auth","storage","data-api","push"];function Ie(e){return{error:e instanceof Error?e.message:String(e)}}async function We(e){let[t,n,r,o]=await Promise.all([S("cloud-project-auth",{ref:e,action:"status"}).then(s=>({enabled:s.enabled===!0,google:s.google?.enabled===!0,saml:s.saml?.enabled===!0}),Ie),S("cloud-project-storage",{ref:e}).then(s=>({enabled:s.storage?.enabled===!0,pending:s.storage?.pending===!0,bytes:s.storage?.bytes??null,files:s.storage?.files??null}),Ie),S("cloud-project-data-api",{ref:e}).then(s=>({enabled:s.dataApi?.enabled===!0,allowedOnPlan:s.dataApi?.allowed===!0}),Ie),S("cloud-project-push",{ref:e}).then(s=>({enabled:s.push?.enabled===!0,scheduling:s.push?.scheduling===!0}),Ie)]);return{ref:e,auth:t,storage:n,dataApi:r,push:o,realtime:dd}}var dd={enabled:!0,broadcast:!0,presence:!0,tableChanges:"Plus and Pro plans"};function Us(e){let t=(e??"").toLowerCase().replace("_","-").replace(/^dataapi$/,"data-api");if(!Zt.includes(t))throw new y(`say which product: ${Zt.join(", ")}`);return t}async function rr(e,t,n){if(t==="auth"){let r=await S("cloud-project-auth",{ref:e,action:n?"enable":"disable"});return{ref:e,product:t,enabled:n,note:r.note??(n?"Auth starts within a minute.":"Auth stops. Your users stay in the auth schema.")}}return t==="push"?(await S("cloud-project-push",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?"Push starts within a minute. It runs beside the database, so the database restarts once. Set keys with: snoutdata push credentials":"Push is off. Devices, the log and the keys stay in the push schema."}):(await S(t==="storage"?"cloud-project-storage":"cloud-project-data-api",{ref:e,enable:n}),{ref:e,product:t,enabled:n,note:n?`${t} starts within a minute, when the host picks up the change.`:`${t} is off.`})}async function Fs(e){let t=await We(e);g(t,()=>{let n=(d,i,u="")=>[d,i&&typeof i=="object"&&"error"in i?`could not read: ${i.error}`:i.enabled?x("on"):"off",u],r=t.storage,o=t.dataApi;process.stdout.write(`${F([["PRODUCT","STATE",""],n("auth",t.auth),n("storage",r,"pending"in r&&r.pending?"waiting for the host":""),n("data-api",o,"allowedOnPlan"in o&&!o.allowedOnPlan?"not on this plan":""),n("push",t.push,"scheduling"in t.push&&t.push.enabled&&!t.push.scheduling?"scheduled sends on paid plans":""),n("realtime",t.realtime,"always on: broadcast, presence; table changes on Plus and Pro")])}
609
+ `);let s=d=>!!(d&&typeof d=="object"&&"enabled"in d&&!d.enabled),a=["auth","storage","data-api","push"].find(d=>s(d==="data-api"?o:t[d]));a&&c(b(` snoutdata products enable ${a} --ref ${e}`))})}async function Bs(e,t,n){let r=await rr(e,t,n);g(r,()=>c(r.note))}async function Qt(e){let t=await S("cloud-project-domain",{ref:e,action:"list"});return{ref:e,domains:t.domains??[]}}async function or(e,t,n){if(!n)throw new y(`domains ${t} needs a hostname, e.g. api.example.com`);let r=await S("cloud-project-domain",{ref:e,action:t,hostname:n});return{ref:e,domain:r.domain??null,next:r.next??null}}function ud(e,t){let n=[["TYPE","NAME","VALUE"]];e.record&&n.push([e.record.type,e.record.name,e.record.value]),e.certificateRecord&&n.push([e.certificateRecord.type,e.certificateRecord.name,e.certificateRecord.value]),n.push(["CNAME",e.hostname,`${t}.api.snoutdata.com`]),process.stdout.write(`${F(n)}
610
+ `)}async function Hs(e,t,n){if(t===void 0||t==="list"){let o=await Qt(e);g(o,()=>{if(o.domains.length===0){c(`No domains on ${e}. \`snoutdata domains add api.example.com\`.`);return}process.stdout.write(`${F([["HOSTNAME","VERIFIED","CERTIFICATE"],...o.domains.map(s=>[s.hostname,s.verified?"yes":x("no"),s.certificate?.state??""])])}
611
+ `)});return}if(t!=="add"&&t!=="verify"&&t!=="remove")throw new y(`unknown command: domains ${t}`);let r=await or(e,t,n??"");g(r,()=>{if(t==="remove"){c(`${n} removed.`);return}r.domain&&(c(r.domain.verified?`${n} is verified.`:r.domain.verificationError??`${n} is not verified yet. Publish these records, then \`snoutdata domains verify ${n}\`:`),r.domain.verified||ud(r.domain,e)),r.next&&c(r.next)})}async function sr(e){return S("cloud-project-restore",{ref:e},"GET")}async function ar(e,t,n){if(Number.isNaN(Date.parse(t)))throw new y(`--at needs a moment, e.g. 2026-09-20T14:30:00Z (got "${t}")`);return S("cloud-project-restore",{ref:e,at:new Date(t).toISOString(),...n?{name:n}:{}})}async function Gs(e){let t=await sr(e);g(t,()=>{let n=t.restore,r=nr(e,n);if(r){c(r);return}c(`${e} can be restored to any moment from ${n.earliest} to ${n.latest}.`),c(b(` snoutdata db restore --at ${n.latest} --ref ${e}`))})}async function qs(e,t,n){let r=await ar(e,t,n);g(r,()=>{c(`Restoring ${e} as it was at ${r.targetAt} into a new project, ${x(r.project.name)} (${r.project.ref}).`),c(b(" snoutdata projects list to see when it is ready"))})}async function ir(e){let{projects:t}=await oe(),n=t.find(d=>d.ref===e);if(!n)throw new y(`no project ${e} on this account. \`snoutdata projects list\`.`);let[r,o,s,a]=await Promise.all([We(e),S("cloud-project-functions",{ref:e}).then(d=>(d.functions??[]).map(i=>i.name),Ie),S("cloud-project-secrets",{ref:e}).then(d=>(d.secrets??[]).map(i=>i.name),Ie),Qt(e).then(d=>d.domains.map(i=>({hostname:i.hostname,verified:i.verified})),Ie)]);return{...n,products:r,functions:o,secretNames:s,domains:a}}async function Ks(e){let t=await ir(e);g(t,()=>{let n=t,r=s=>s&&typeof s=="object"&&"enabled"in s?s.enabled?"on":"off":"?",o=s=>Array.isArray(s)?s.length?s.map(a=>typeof a=="string"?a:a.hostname).join(", "):"none":"could not read";process.stdout.write(`${F([["name",n.name],["ref",n.ref],["state",n.state],["region",n.region],...typeof n.postgresVersion=="number"?[["postgres",String(n.postgresVersion)]]:[],["auth / storage / data-api / push",`${r(n.products.auth)} / ${r(n.products.storage)} / ${r(n.products.dataApi)} / ${r(n.products.push)}`],["realtime","on (broadcast and presence; table changes on Plus and Pro)"],["functions",o(n.functions)],["secrets",o(n.secretNames)],["domains",o(n.domains)]])}
612
+ `)})}z();te();Ge();z();K();import{existsSync as md,mkdirSync as fd,readFileSync as pd,renameSync as gd,writeFileSync as hd}from"node:fs";import{homedir as cr}from"node:os";import{dirname as yd,join as wd}from"node:path";import{rootCertificates as bd}from"node:tls";var Sd="SNOUTDATA_DB_SSLMODE";function _d(e=cr()){let t=wd(e,".snoutdata","ca-roots.pem"),n=`${bd.join(`
613
613
  `)}
614
- `,r=null;try{r=id(t)?ld(t,"utf8"):null}catch{r=null}if(r!==n){cd(md(t),{recursive:!0,mode:448});let o=`${t}.${process.pid}.tmp`;ud(o,n,{mode:420}),dd(o,t)}return t}function ir(e,t=process.env,n=ar()){if(e==="disable")return{mode:"disable"};let r=t[gd]?.trim();return r?{mode:r}:{mode:"verify-full",rootCert:t.PGSSLROOTCERT||hd(n)}}function Ne(e,t=process.env,n=ar()){let{mode:r,rootCert:o}=ir(e,t,n);return o?{PGSSLMODE:r,PGSSLROOTCERT:o}:{PGSSLMODE:r}}async function Vs(e){try{let{projects:t}=await re(),n=t.find(r=>r.ref===e);n?.readOnly?K(`${e} is read-only: it is over the storage limit on your plan. Reading works; writing does not.`):n?.readOnlyPending&&K(`${e} is about to become read-only: it has grown past the storage limit on your plan.`)}catch{}}async function lr(e){await Vs(e);let t=await te(e);g(t,()=>{process.stdout.write(`${t.uri}
615
- `)})}async function Ys(e,t){if(t.length===0&&!xe()){let o=je();O("usage",`db psql with no statements opens an interactive session, and ${o?Me(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await Vs(e);let n=await te(e);n.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let r=mt("psql",["--host",n.host,"--port",String(n.port),"--username",n.user,"--dbname",n.database,...t],{stdio:"inherit",env:{...process.env,PGPASSWORD:n.password,...Ne(n.ssl)}});return new Promise((o,s)=>{r.on("error",a=>{if(a.code==="ENOENT"){s(new N("tool-missing","psql is not installed. `snoutdata db url` prints a connection string for any client."));return}s(new N("failed",String(a)))}),r.on("close",a=>o(a??0))})}function Xs(e,t,n="DATABASE_URL"){let r=tn(e,".env"),o=`${n}=${t}`,s=_d(e);if(!en(r))return Ge(r,`${o}
616
- `,{mode:384}),{path:r,replaced:!1,ignored:s};process.platform!=="win32"&&yd(r).mode&63&&K(`${r} can be read by other users on this machine, and it holds a database password. \`chmod 600 ${r}\` closes it.`);let a=Js(r,"utf8"),l=new RegExp(`^${n}=.*$`,"m");return l.test(a)?(Ge(r,a.replace(l,o)),{path:r,replaced:!0,ignored:s}):(Ws(r,a.endsWith(`
614
+ `,r=null;try{r=md(t)?pd(t,"utf8"):null}catch{r=null}if(r!==n){fd(yd(t),{recursive:!0,mode:448});let o=`${t}.${process.pid}.tmp`;hd(o,n,{mode:420}),gd(o,t)}return t}function lr(e,t=process.env,n=cr()){if(e==="disable")return{mode:"disable"};let r=t[Sd]?.trim();return r?{mode:r}:{mode:"verify-full",rootCert:t.PGSSLROOTCERT||_d(n)}}function Ce(e,t=process.env,n=cr()){let{mode:r,rootCert:o}=lr(e,t,n);return o?{PGSSLMODE:r,PGSSLROOTCERT:o}:{PGSSLMODE:r}}async function Xs(e){try{let{projects:t}=await oe(),n=t.find(r=>r.ref===e);n?.readOnly?J(`${e} is read-only: it is over the storage limit on your plan. Reading works; writing does not.`):n?.readOnlyPending&&J(`${e} is about to become read-only: it has grown past the storage limit on your plan.`)}catch{}}async function ur(e){await Xs(e);let t=await ne(e);g(t,()=>{process.stdout.write(`${t.uri}
615
+ `)})}async function Zs(e,t){if(t.length===0&&!$e()){let o=He();N("usage",`db psql with no statements opens an interactive session, and ${o?Be(o):"there is nobody to use it"}. Pass statements after --, or use \`snoutdata db url\` and your own client.`)}await Xs(e);let n=await ne(e);n.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let r=mt("psql",["--host",n.host,"--port",String(n.port),"--username",n.user,"--dbname",n.database,...t],{stdio:"inherit",env:{...process.env,PGPASSWORD:n.password,...Ce(n.ssl)}});return new Promise((o,s)=>{r.on("error",a=>{if(a.code==="ENOENT"){s(new C("tool-missing","psql is not installed. `snoutdata db url` prints a connection string for any client."));return}s(new C("failed",String(a)))}),r.on("close",a=>o(a??0))})}function Qs(e,t,n="DATABASE_URL"){let r=nn(e,".env"),o=`${n}=${t}`,s=$d(e);if(!tn(r))return ze(r,`${o}
616
+ `,{mode:384}),{path:r,replaced:!1,ignored:s};process.platform!=="win32"&&xd(r).mode&63&&J(`${r} can be read by other users on this machine, and it holds a database password. \`chmod 600 ${r}\` closes it.`);let a=Vs(r,"utf8"),d=new RegExp(`^${n}=.*$`,"m");return d.test(a)?(ze(r,a.replace(d,o)),{path:r,replaced:!0,ignored:s}):(zs(r,a.endsWith(`
617
617
  `)?`${o}
618
618
  `:`
619
619
  ${o}
620
- `),{path:r,replaced:!1,ignored:s})}function _d(e){let t=e;for(;!en(tn(t,".git"));){let s=bd(t);if(s===t)return!1;t=s}let n=tn(e,".gitignore"),r=en(n)?Js(n,"utf8"):"";return r.split(/\r?\n/).some(s=>/^\/?(\.env|\.env\*|\*\.env|\.env\.\*)$/.test(s.trim()))?!1:(Ws(n,r===""||r.endsWith(`
620
+ `),{path:r,replaced:!1,ignored:s})}function $d(e){let t=e;for(;!tn(nn(t,".git"));){let s=vd(t);if(s===t)return!1;t=s}let n=nn(e,".gitignore"),r=tn(n)?Vs(n,"utf8"):"";return r.split(/\r?\n/).some(s=>/^\/?(\.env|\.env\*|\*\.env|\.env\.\*)$/.test(s.trim()))?!1:(zs(n,r===""||r.endsWith(`
621
621
  `)?`.env
622
622
  `:`
623
623
  .env
624
- `),!0)}async function Zs(e,t={}){if(t.statusOnly){let{export:a}=await Fe(e);return g({ref:e,export:a},()=>cr(a)),0}let n=await at(e);if(!n.export.pending&&n.export.url&&!t.out)return g({ref:e,export:n.export},()=>cr(n.export)),0;c("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await vd(e,t.timeoutMs);if(r.error&&O("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return g({ref:e,export:r},()=>cr(r)),0;r.url||O("failed","The copy was taken, but no download link could be signed. Run this again to get one.",{ref:e});let o=await kd(r.url,t.out),s=r.rolesSql?xd(t.out):null;return s&&r.rolesSql&&Ge(s,r.rolesSql.endsWith(`
624
+ `),!0)}async function ea(e,t={}){if(t.statusOnly){let{export:a}=await Ke(e);return g({ref:e,export:a},()=>dr(a)),0}let n=await at(e);if(!n.export.pending&&n.export.url&&!t.out)return g({ref:e,export:n.export},()=>dr(n.export)),0;c("Taking a copy of the database. This runs on the server and can take a few minutes.");let r=await Ad(e,t.timeoutMs);if(r.error&&N("failed",`The export did not finish: ${r.error}`,{ref:e}),!t.out)return g({ref:e,export:r},()=>dr(r)),0;r.url||N("failed","The copy was taken, but no download link could be signed. Run this again to get one.",{ref:e});let o=await Td(r.url,t.out),s=r.rolesSql?Pd(t.out):null;return s&&r.rolesSql&&ze(s,r.rolesSql.endsWith(`
625
625
  `)?r.rolesSql:`${r.rolesSql}
626
- `,"utf8"),g({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{c(`Saved ${he(o)} to ${t.out}.`),c(b(`Into a SnoutData project: snoutdata db restore --file ${t.out} --ref <ref>`)),s?(c(`Saved the roles it needs to ${s}.`),c(b("Into any other Postgres, in this order:")),c(b(` psql --dbname <your database> -f ${s}`)),c(b(` pg_restore --dbname <your database> ${t.out}`)),c(b("The roles are created without passwords. Set them yourself if anything signs in as one."))):(c(b("Restore it with: pg_restore --no-owner --no-privileges --dbname <your database> "+t.out)),c(b("Owners and grants are skipped: this export carries no roles file.")))}),0}function xd(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function vd(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await Fe(e);if(!r.pending)return r;if(Date.now()>=n)throw new Error(`the export of ${e} was still running after waiting`);await new Promise(o=>setTimeout(o,3e3))}}async function kd(e,t){let n=await fetch(e);if(!n.ok||!n.body)throw new Error(`the download failed: ${n.status} ${n.statusText}`);let r=await zs(t,"w"),o=0;try{for await(let s of n.body)await r.write(s),o+=s.byteLength}finally{await r.close()}return o}function cr(e){if(e.error){K(`The last export did not finish: ${e.error}`);return}if(e.pending){c("An export is running. Ask again in a minute, or run `snoutdata db export`, which waits for it.");return}if(!e.completedAt){c("This project has never been exported.");return}c(`A copy taken ${pe(e.completedAt)}, ${he(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
627
- `),e.urlExpiresAt&&c(b(`That link stops working ${pe(e.urlExpiresAt)}.`))):c(b("The link has expired. Run the export again to sign a new one; the copy is kept."))}function he(e){if(e<1024)return`${e} B`;let t=["KB","MB","GB","TB"],n=e/1024,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n.toFixed(n>=10?0:1)} ${t[r]}`}async function Qs(e,t){let n=t.file;n||O("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),en(n)||O("usage",`no file at ${n}`,{file:n});let r=await zs(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=Ts(o.slice(0,s.bytesRead)),l=await te(e);l.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let{projects:i}=await re(),u=i.find(S=>S.ref===e),d=await Ed(l);d===null&&O("tool-missing","psql is not installed, so the tables in this project could not be counted. Install the Postgres client tools, or restore with your own client using `snoutdata db url`.");let f=Os(a,{existingTables:d,readOnly:u?.readOnly===!0},{force:t.force});f.ok||O("conflict",f.reason,{ref:e,file:n,refusal:f.code});let y=null;if(f.tool==="pg_restore"){let S=await Ks("pg_restore",["--list",n]),w=await Ks("pg_restore",["--section=post-data","--file=-",n]);if((S.code!==0||w.code!==0)&&O("failed",`pg_restore could not read ${n}: ${(S.stderr||w.stderr).trim().slice(0,300)}`,{ref:e,file:n}),y=Ds(S.stdout,w.stdout),y.products.length>0){let x=await He(e),$=v=>!!(v&&typeof v=="object"&&"enabled"in v&&v.enabled),T=y.products.filter(v=>!$(x[v]));T.length>0&&O("conflict",`this dump holds ${T.length>1?`${T.slice(0,-1).join(", ")} and ${T[T.length-1]}`:T[0]} data, and ${e} has ${T.length===1?"it":"them"} off. Their tables are the platform's, so the product has to be on to take the rows. Switch ${T.length===1?"it":"them"} on, wait a minute, and run this again:
628
- ${T.map(v=>` snoutdata products enable ${v} --ref ${e}`).join(`
629
- `)}`,{ref:e,file:n,refusal:"products-off",products:T})}}if(y&&y.platformData.length>0){let S=y.platformData.flatMap(({tables:$})=>$),w=await $d(l,S);w===null&&O("failed",`could not check ${e} for the tables this dump loads into.`,{ref:e});let x=y.platformData.filter(({tables:$})=>$.some(T=>w.includes(T))).map(({product:$})=>$);x.length>0&&O("conflict",`${x.join(", ")} on ${e} ${x.length===1?"has":"have"} not finished setting up ${x.length===1?"its":"their"} tables yet. Run this again in a minute.`,{ref:e,file:n,refusal:"products-starting",products:x})}c(`Restoring ${n} into ${e} with ${f.tool} (${f.format} format).`);let m=y?await Ad(l,n,y):await Qt(l,f.tool,n),p=Ns(m.stderr);for(let S of y?.notes??[])c(b(S));return m.code!==0&&p.fatal.length===0&&p.total>0?(g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format,ignoredErrors:p.harmless},()=>{c(`Restored into ${e}.`),c(`${p.harmless} error${p.harmless===1?"":"s"} above ${p.harmless===1?"is":"are"} expected and nothing is missing: a dump carries comments on the extensions this image installs, and your role does not own them.`)}),0):(m.code!==0&&O("failed",`${f.tool} exited ${m.code}. Nothing here retried it: a half-finished restore is undone by deleting the project and making another.`,{ref:e,file:n,errors:p.fatal}),g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format},()=>c(`Restored into ${e}.`)),0)}async function Ed(e){return new Promise(t=>{let n=mt("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc","select count(*) from pg_class c join pg_namespace n on n.oid = c.relnamespace where c.relkind = 'r' and n.nspname not in ('pg_catalog', 'information_schema') and n.nspname not like 'pg\\_toast%' and not exists (select 1 from pg_depend d where d.objid = c.oid and d.deptype = 'e') and pg_get_userbyid(c.relowner) !~ '^(snoutpod_admin|snout_[a-z]+_admin)$'"],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ne(e.ssl)}}),r="";n.stdout.on("data",o=>r+=String(o)),n.on("error",o=>{K(o.code==="ENOENT"?"psql is not installed, and db restore needs it (and pg_restore for an archive).":String(o)),t(null)}),n.on("close",o=>t(o===0?Number(r.trim())||0:null))})}function $d(e,t){if(t.length===0)return Promise.resolve([]);let n=t.map(r=>`'${r.replace(/'/g,"''")}'`).join(", ");return new Promise(r=>{let o=mt("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc",`select t from unnest(array[${n}]) t where to_regclass(t) is null`],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ne(e.ssl)}}),s="";o.stdout.on("data",a=>s+=String(a)),o.on("error",()=>r(null)),o.on("close",a=>r(a===0?s.split(`
630
- `).map(l=>l.trim()).filter(Boolean):null))})}function Ks(e,t){return new Promise(n=>{let r=mt(e,t,{stdio:["ignore","pipe","pipe"]}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{K(a.code==="ENOENT"?`${e} is not installed, and this dump needs it.`:String(a)),n({code:127,stdout:o,stderr:s})}),r.on("close",a=>n({code:a??0,stdout:o,stderr:s}))})}async function Ad(e,t,n){let r=tn(Sd(),`snoutdata-restore-${process.pid}-${Date.now()}`),o=0,s="";try{for(let{product:l,clear:i,lines:u}of n.platformData){if(i.length>0){Ge(`${r}.sql`,`set role ${Xn[l]};
631
- ${i.map(y=>`delete from ${y};`).join(`
626
+ `,"utf8"),g({ref:e,path:t.out,bytes:o,rolesPath:s},()=>{c(`Saved ${be(o)} to ${t.out}.`),c(b(`Into a SnoutData project: snoutdata db restore --file ${t.out} --ref <ref>`)),s?(c(`Saved the roles it needs to ${s}.`),c(b("Into any other Postgres, in this order:")),c(b(` psql --dbname <your database> -f ${s}`)),c(b(` pg_restore --dbname <your database> ${t.out}`)),c(b("The roles are created without passwords. Set them yourself if anything signs in as one."))):(c(b("Restore it with: pg_restore --no-owner --no-privileges --dbname <your database> "+t.out)),c(b("Owners and grants are skipped: this export carries no roles file.")))}),0}function Pd(e){return e.replace(/\.[^./\\]*$/,"")+".roles.sql"}async function Ad(e,t=30*6e4){let n=Date.now()+t;for(;;){let{export:r}=await Ke(e);if(!r.pending)return r;if(Date.now()>=n)throw new Error(`the export of ${e} was still running after waiting`);await new Promise(o=>setTimeout(o,3e3))}}async function Td(e,t){let n=await fetch(e);if(!n.ok||!n.body)throw new Error(`the download failed: ${n.status} ${n.statusText}`);let r=await Ys(t,"w"),o=0;try{for await(let s of n.body)await r.write(s),o+=s.byteLength}finally{await r.close()}return o}function dr(e){if(e.error){J(`The last export did not finish: ${e.error}`);return}if(e.pending){c("An export is running. Ask again in a minute, or run `snoutdata db export`, which waits for it.");return}if(!e.completedAt){c("This project has never been exported.");return}c(`A copy taken ${he(e.completedAt)}, ${be(e.bytes??0)}.`),e.url?(process.stdout.write(`${e.url}
627
+ `),e.urlExpiresAt&&c(b(`That link stops working ${he(e.urlExpiresAt)}.`))):c(b("The link has expired. Run the export again to sign a new one; the copy is kept."))}function be(e){if(e<1024)return`${e} B`;let t=["KB","MB","GB","TB"],n=e/1024,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n.toFixed(n>=10?0:1)} ${t[r]}`}async function ta(e,t){let n=t.file;n||N("usage","db restore needs --file, pointing at a dump. `snoutdata db export` writes one."),tn(n)||N("usage",`no file at ${n}`,{file:n});let r=await Ys(n,"r"),o=new Uint8Array(512),s=await r.read(o,0,512,0);await r.close();let a=Ns(o.slice(0,s.bytesRead)),d=await ne(e);d.wakesInstantly||c("This project is paused; the connection will wake it, which takes a few seconds.");let{projects:i}=await oe(),u=i.find(p=>p.ref===e),l=await Rd(d);l===null&&N("tool-missing","psql is not installed, so the tables in this project could not be counted. Install the Postgres client tools, or restore with your own client using `snoutdata db url`.");let f=Ds(a,{existingTables:l,readOnly:u?.readOnly===!0},{force:t.force});f.ok||N("conflict",f.reason,{ref:e,file:n,refusal:f.code});let w=null;if(f.tool==="pg_restore"){let p=await Ws("pg_restore",["--list",n]),h=await Ws("pg_restore",["--section=post-data","--file=-",n]);if((p.code!==0||h.code!==0)&&N("failed",`pg_restore could not read ${n}: ${(p.stderr||h.stderr).trim().slice(0,300)}`,{ref:e,file:n}),w=Cs(p.stdout,h.stdout),w.products.length>0){let $=await We(e),k=H=>!!(H&&typeof H=="object"&&"enabled"in H&&H.enabled),A=w.products.filter(H=>!k($[H]));A.length>0&&N("conflict",`this dump holds ${A.length>1?`${A.slice(0,-1).join(", ")} and ${A[A.length-1]}`:A[0]} data, and ${e} has ${A.length===1?"it":"them"} off. Their tables are the platform's, so the product has to be on to take the rows. Switch ${A.length===1?"it":"them"} on, wait a minute, and run this again:
628
+ ${A.map(H=>` snoutdata products enable ${H} --ref ${e}`).join(`
629
+ `)}`,{ref:e,file:n,refusal:"products-off",products:A})}}if(w&&w.platformData.length>0){let p=w.platformData.flatMap(({tables:k})=>k),h=await Od(d,p);h===null&&N("failed",`could not check ${e} for the tables this dump loads into.`,{ref:e});let $=w.platformData.filter(({tables:k})=>k.some(A=>h.includes(A))).map(({product:k})=>k);$.length>0&&N("conflict",`${$.join(", ")} on ${e} ${$.length===1?"has":"have"} not finished setting up ${$.length===1?"its":"their"} tables yet. Run this again in a minute.`,{ref:e,file:n,refusal:"products-starting",products:$})}c(`Restoring ${n} into ${e} with ${f.tool} (${f.format} format).`);let R=w?await Nd(d,n,w):await en(d,f.tool,n),m=Is(R.stderr);for(let p of w?.notes??[])c(b(p));return R.code!==0&&m.fatal.length===0&&m.total>0?(g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format,ignoredErrors:m.harmless},()=>{c(`Restored into ${e}.`),c(`${m.harmless} error${m.harmless===1?"":"s"} above ${m.harmless===1?"is":"are"} expected and nothing is missing: a dump carries comments on the extensions this image installs, and your role does not own them.`)}),0):(R.code!==0&&N("failed",`${f.tool} exited ${R.code}. Nothing here retried it: a half-finished restore is undone by deleting the project and making another.`,{ref:e,file:n,errors:m.fatal}),g({ref:e,file:n,ok:!0,tool:f.tool,format:f.format},()=>c(`Restored into ${e}.`)),0)}async function Rd(e){return new Promise(t=>{let n=mt("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc","select count(*) from pg_class c join pg_namespace n on n.oid = c.relnamespace where c.relkind = 'r' and n.nspname not in ('pg_catalog', 'information_schema') and n.nspname not like 'pg\\_toast%' and not exists (select 1 from pg_depend d where d.objid = c.oid and d.deptype = 'e') and pg_get_userbyid(c.relowner) !~ '^(snoutpod_admin|snout_[a-z]+_admin)$'"],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ce(e.ssl)}}),r="";n.stdout.on("data",o=>r+=String(o)),n.on("error",o=>{J(o.code==="ENOENT"?"psql is not installed, and db restore needs it (and pg_restore for an archive).":String(o)),t(null)}),n.on("close",o=>t(o===0?Number(r.trim())||0:null))})}function Od(e,t){if(t.length===0)return Promise.resolve([]);let n=t.map(r=>`'${r.replace(/'/g,"''")}'`).join(", ");return new Promise(r=>{let o=mt("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-tAc",`select t from unnest(array[${n}]) t where to_regclass(t) is null`],{stdio:["ignore","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ce(e.ssl)}}),s="";o.stdout.on("data",a=>s+=String(a)),o.on("error",()=>r(null)),o.on("close",a=>r(a===0?s.split(`
630
+ `).map(d=>d.trim()).filter(Boolean):null))})}function Ws(e,t){return new Promise(n=>{let r=mt(e,t,{stdio:["ignore","pipe","pipe"]}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{J(a.code==="ENOENT"?`${e} is not installed, and this dump needs it.`:String(a)),n({code:127,stdout:o,stderr:s})}),r.on("close",a=>n({code:a??0,stdout:o,stderr:s}))})}async function Nd(e,t,n){let r=nn(Ed(),`snoutdata-restore-${process.pid}-${Date.now()}`),o=0,s="";try{for(let{product:d,clear:i,lines:u}of n.platformData){if(i.length>0){ze(`${r}.sql`,`set role ${Qn[d]};
631
+ ${i.map(w=>`delete from ${w};`).join(`
632
632
  `)}
633
- `,"utf8");let f=await Qt(e,"psql",`${r}.sql`,["--quiet"]);o=o||f.code,s+=f.stderr}Ge(`${r}.platform`,`${u.join(`
633
+ `,"utf8");let f=await en(e,"psql",`${r}.sql`,["--quiet"]);o=o||f.code,s+=f.stderr}ze(`${r}.platform`,`${u.join(`
634
634
  `)}
635
- `,"utf8");let d=await Qt(e,"pg_restore",t,["--data-only",`--role=${Xn[l]}`,`--use-list=${r}.platform`]);o=o||d.code,s+=d.stderr}Ge(`${r}.rest`,`${n.rest.join(`
635
+ `,"utf8");let l=await en(e,"pg_restore",t,["--data-only",`--role=${Qn[d]}`,`--use-list=${r}.platform`]);o=o||l.code,s+=l.stderr}ze(`${r}.rest`,`${n.rest.join(`
636
636
  `)}
637
- `,"utf8");let a=await Qt(e,"pg_restore",t,[`--use-list=${r}.rest`]);return{code:o||a.code,stderr:s+a.stderr}}finally{for(let a of[".platform",".rest",".sql"])try{wd(`${r}${a}`)}catch{}}}function Qt(e,t,n,r=[]){let o=["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database],s=t==="psql"?[...o,"--no-psqlrc","-v","ON_ERROR_STOP=1",...r,"-f",n]:[...o,"--no-owner","--no-privileges",...r,n];return new Promise(a=>{let l=mt(t,s,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ne(e.ssl)}}),i="";l.stderr?.on("data",u=>{let d=String(u);i+=d,process.stderr.write(d)}),l.on("error",u=>{K(u.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(u)),a({code:127,stderr:i})}),l.on("close",u=>a({code:u??0,stderr:i}))})}J();ne();q();function Rd(e,t){let[n="list",r,...o]=e;if(o.length>0)throw new h(`db access ${n} takes one argument, not ${1+o.length}`);switch(n){case"list":if(r!==void 0)throw new h("db access list takes no argument");if(t!==void 0)throw new h("--level is for db access grant");return{action:"list"};case"grant":{if(!r||!r.includes("@"))throw new h("db access grant needs an email address: db access grant you@example.com [--level full|read]");let s=t??"read";if(s!=="full"&&s!=="read")throw new h(`--level is full or read, not ${JSON.stringify(s)}`);return{action:"grant",email:r,level:s}}case"revoke":if(!r)throw new h("db access revoke needs an email address or a role name");if(t!==void 0)throw new h("--level is for db access grant");return{action:"revoke",who:r};default:throw new h(`unknown command: db access ${n}. db access list|grant|revoke`)}}async function ea(e,t,n){let r=Rd(t,n.level);if(r.action==="list"){let s=await _("cloud-project-db-access",{ref:e,action:"list"});g(s,()=>Td(s));return}if(r.action==="grant"){let s=await _("cloud-project-db-access",{ref:e,action:"grant",email:r.email,level:r.level});g(s,()=>Od(s));return}let o=await _("cloud-project-db-access",{ref:e,action:"revoke",who:r.who});g(o,()=>Dd(o))}function Td(e){if(e.note&&c(e.note),e.access.length===0){c(`Nobody signs in to ${e.ref}'s database as themselves yet. \`snoutdata db access grant you@example.com --level full\`.`);return}process.stdout.write(`${F([["EMAIL","LEVEL","ROLE"],...e.access.map(n=>[n.you?k(n.email):n.email,n.level,n.role])])}
638
- `);let t=e.access.find(n=>n.you);t?.connection&&(c(""),c("You sign in with (psql 18; a browser approves it):"),c(b(` psql "${t.connection.keywords}"`)))}function Od(e){e.connection&&process.stdout.write(`${e.connection.keywords}
639
- `),c("");let t=e.outcome==="unchanged"?"already has":"now has";c(`${e.email} ${t} ${k(e.level)} access to ${e.ref}'s database, as ${e.role}${e.means?`: ${e.means}`:""}.`),e.outcome!=="unchanged"&&c(e.appliesIn),c("They sign in with psql 18 (libpq 18 with OAuth support) and approve the code it prints in a browser:"),e.connection&&c(b(` psql "${e.connection.keywords}"`))}function Nd(e){return[...e.revoked.map(t=>`Took away ${t.email}'s ${t.level} access to ${e.ref}'s database (${t.role}).`),e.note]}function Dd(e){let t=Nd(e);for(let n of t.slice(0,-1))c(n);K(t[t.length-1])}J();ne();q();var Id=6e4;async function dr(e,t={}){return _("cloud-project-realtime",{ref:e,...t.channel?{channel:t.channel}:{},...t.since!==void 0?{since:t.since}:{}})}function Cd(e,t=Date.now()){let n=/^(\d+)\s*(s|m|h|d)$/i.exec(e.trim());if(n){let o={s:1e3,m:6e4,h:36e5,d:864e5}[n[2].toLowerCase()];return t-Number(n[1])*o}let r=Date.parse(e);if(Number.isFinite(r))return r;throw new h(`--since wants a duration like 10m, 2h or 1d, or a time like 2026-10-03T14:00, not "${e}"`)}function Md(e){let t=new Date(e),n=r=>String(r).padStart(2,"0");return`${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`}function ta(e){if(e===null)return"-";let t=Math.round(e/1e3);if(t<60)return`${t}s ago`;let n=Math.round(t/60);return n<60?`${n}m ago`:`${Math.round(n/60)}h ago`}var jd={connect:"connected",connect_refused:"refused at connect",join:"joined",join_refused:"join refused",leave:"left",channel_closed:"channel closed by the server",disconnect:"disconnected"};function na(e){let t=[e.presence_key?e.presence_key:null,e.socket!==void 0?`socket ${e.socket}`:null].filter(Boolean).join(", "),n=[Md(e.at),jd[e.kind]??e.kind];return e.channel&&n.push(e.channel),t&&n.push(`(${t})`),e.reason?`${n.join(" ")}: ${e.reason}`:n.join(" ")}function Ld(e){let t=Object.fromEntries(Object.entries(e).filter(([n])=>!n.startsWith("phx_ref")));return JSON.stringify(t)}function Ud(e){let t=e.inspect;if(c(k(`Realtime for ${e.ref}`)),c(`${t.connections} connection${t.connections===1?"":"s"}, ${t.connected_users} on a channel. The project's limit is ${t.limits.max_events_per_second} messages a second, counted once per message sent, however many receive it.`),t.channels.length===0){c("No channels are open.");return}for(let n of t.channels){let r=n.messages;if(c(""),c(k(`${n.name}${n.private?" (private)":""}`)+` ${n.clients.length} client${n.clients.length===1?"":"s"}`),c(b(` last ${r.window_seconds}s: ${r.broadcasts_received} broadcast${r.broadcasts_received===1?"":"s"} sent, ${r.broadcasts_delivered} delivered, busiest second ${r.peak_broadcasts_per_second}; ${r.presence_diffs_delivered} presence update${r.presence_diffs_delivered===1?"":"s"} delivered`)),n.clients.length>0){let s=n.clients.map(a=>[String(a.socket),a.presence_key,ta(Date.now()-a.joined_at),ta(a.last_seen_ms_ago)+(a.last_seen_ms_ago!==null&&a.last_seen_ms_ago>Id?" (silent: likely gone)":"")]);c(F([["SOCKET","PRESENCE KEY","JOINED","LAST HEARD"],...s]).split(`
637
+ `,"utf8");let a=await en(e,"pg_restore",t,[`--use-list=${r}.rest`]);return{code:o||a.code,stderr:s+a.stderr}}finally{for(let a of[".platform",".rest",".sql"])try{kd(`${r}${a}`)}catch{}}}function en(e,t,n,r=[]){let o=["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database],s=t==="psql"?[...o,"--no-psqlrc","-v","ON_ERROR_STOP=1",...r,"-f",n]:[...o,"--no-owner","--no-privileges",...r,n];return new Promise(a=>{let d=mt(t,s,{stdio:["ignore","inherit","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ce(e.ssl)}}),i="";d.stderr?.on("data",u=>{let l=String(u);i+=l,process.stderr.write(l)}),d.on("error",u=>{J(u.code==="ENOENT"?`${t} is not installed, and this dump needs it.`:String(u)),a({code:127,stderr:i})}),d.on("close",u=>a({code:u??0,stderr:i}))})}z();re();K();function Id(e,t){let[n="list",r,...o]=e;if(o.length>0)throw new y(`db access ${n} takes one argument, not ${1+o.length}`);switch(n){case"list":if(r!==void 0)throw new y("db access list takes no argument");if(t!==void 0)throw new y("--level is for db access grant");return{action:"list"};case"grant":{if(!r||!r.includes("@"))throw new y("db access grant needs an email address: db access grant you@example.com [--level full|read]");let s=t??"read";if(s!=="full"&&s!=="read")throw new y(`--level is full or read, not ${JSON.stringify(s)}`);return{action:"grant",email:r,level:s}}case"revoke":if(!r)throw new y("db access revoke needs an email address or a role name");if(t!==void 0)throw new y("--level is for db access grant");return{action:"revoke",who:r};default:throw new y(`unknown command: db access ${n}. db access list|grant|revoke`)}}async function na(e,t,n){let r=Id(t,n.level);if(r.action==="list"){let s=await S("cloud-project-db-access",{ref:e,action:"list"});g(s,()=>Cd(s));return}if(r.action==="grant"){let s=await S("cloud-project-db-access",{ref:e,action:"grant",email:r.email,level:r.level});g(s,()=>Md(s));return}let o=await S("cloud-project-db-access",{ref:e,action:"revoke",who:r.who});g(o,()=>Ld(o))}function Cd(e){if(e.note&&c(e.note),e.access.length===0){c(`Nobody signs in to ${e.ref}'s database as themselves yet. \`snoutdata db access grant you@example.com --level full\`.`);return}process.stdout.write(`${F([["EMAIL","LEVEL","ROLE"],...e.access.map(n=>[n.you?x(n.email):n.email,n.level,n.role])])}
638
+ `);let t=e.access.find(n=>n.you);t?.connection&&(c(""),c("You sign in with (psql 18; a browser approves it):"),c(b(` psql "${t.connection.keywords}"`)))}function Md(e){e.connection&&process.stdout.write(`${e.connection.keywords}
639
+ `),c("");let t=e.outcome==="unchanged"?"already has":"now has";c(`${e.email} ${t} ${x(e.level)} access to ${e.ref}'s database, as ${e.role}${e.means?`: ${e.means}`:""}.`),e.outcome!=="unchanged"&&c(e.appliesIn),c("They sign in with psql 18 (libpq 18 with OAuth support) and approve the code it prints in a browser:"),e.connection&&c(b(` psql "${e.connection.keywords}"`))}function jd(e){return[...e.revoked.map(t=>`Took away ${t.email}'s ${t.level} access to ${e.ref}'s database (${t.role}).`),e.note]}function Ld(e){let t=jd(e);for(let n of t.slice(0,-1))c(n);J(t[t.length-1])}z();re();K();var Ud=6e4;async function mr(e,t={}){return S("cloud-project-realtime",{ref:e,...t.channel?{channel:t.channel}:{},...t.since!==void 0?{since:t.since}:{}})}function Fd(e,t=Date.now()){let n=/^(\d+)\s*(s|m|h|d)$/i.exec(e.trim());if(n){let o={s:1e3,m:6e4,h:36e5,d:864e5}[n[2].toLowerCase()];return t-Number(n[1])*o}let r=Date.parse(e);if(Number.isFinite(r))return r;throw new y(`--since wants a duration like 10m, 2h or 1d, or a time like 2026-10-03T14:00, not "${e}"`)}function Bd(e){let t=new Date(e),n=r=>String(r).padStart(2,"0");return`${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`}function ra(e){if(e===null)return"-";let t=Math.round(e/1e3);if(t<60)return`${t}s ago`;let n=Math.round(t/60);return n<60?`${n}m ago`:`${Math.round(n/60)}h ago`}var Hd={connect:"connected",connect_refused:"refused at connect",join:"joined",join_refused:"join refused",leave:"left",channel_closed:"channel closed by the server",disconnect:"disconnected"};function oa(e){let t=[e.presence_key?e.presence_key:null,e.socket!==void 0?`socket ${e.socket}`:null].filter(Boolean).join(", "),n=[Bd(e.at),Hd[e.kind]??e.kind];return e.channel&&n.push(e.channel),t&&n.push(`(${t})`),e.reason?`${n.join(" ")}: ${e.reason}`:n.join(" ")}function Gd(e){let t=Object.fromEntries(Object.entries(e).filter(([n])=>!n.startsWith("phx_ref")));return JSON.stringify(t)}function qd(e){let t=e.inspect;if(c(x(`Realtime for ${e.ref}`)),c(`${t.connections} connection${t.connections===1?"":"s"}, ${t.connected_users} on a channel. The project's limit is ${t.limits.max_events_per_second} messages a second, counted once per message sent, however many receive it.`),t.channels.length===0){c("No channels are open.");return}for(let n of t.channels){let r=n.messages;if(c(""),c(x(`${n.name}${n.private?" (private)":""}`)+` ${n.clients.length} client${n.clients.length===1?"":"s"}`),c(b(` last ${r.window_seconds}s: ${r.broadcasts_received} broadcast${r.broadcasts_received===1?"":"s"} sent, ${r.broadcasts_delivered} delivered, busiest second ${r.peak_broadcasts_per_second}; ${r.presence_diffs_delivered} presence update${r.presence_diffs_delivered===1?"":"s"} delivered`)),n.clients.length>0){let s=n.clients.map(a=>[String(a.socket),a.presence_key,ra(Date.now()-a.joined_at),ra(a.last_seen_ms_ago)+(a.last_seen_ms_ago!==null&&a.last_seen_ms_ago>Ud?" (silent: likely gone)":"")]);c(F([["SOCKET","PRESENCE KEY","JOINED","LAST HEARD"],...s]).split(`
640
640
  `).map(a=>` ${a}`).join(`
641
- `))}let o=Object.keys(n.presence);if(o.length>0){c(" presence:");for(let s of o)for(let a of n.presence[s].metas)c(` ${s} ${Ld(a)}`)}}}async function ra(e,t={}){if(t.watch&&Po())throw new h("--watch is for a person; with --json, poll `realtime inspect --json` or `realtime logs --since 1m --json`");let n=await dr(e,{channel:t.channel});if(g(n.inspect,()=>Ud(n)),!t.watch)return 0;c(""),c(b("Watching. New events appear below as they happen; Ctrl+C to stop."));let r=n.at+1,o=t.intervalMs??2e3;for(;;){await new Promise(a=>setTimeout(a,o));let s=await dr(e,{channel:t.channel,since:r});for(let a of s.events)process.stdout.write(`${na(a)}
642
- `);if(s.events.length>0){r=Math.max(...s.events.map(l=>l.at))+1;let a=s.inspect.channels.map(l=>`${l.name}: ${Object.keys(l.presence).join(", ")||"nobody tracked"}`).join("; ");process.stdout.write(b(` now ${a||"no channels open"}
643
- `))}}}async function oa(e,t={}){let n=t.since?Cd(t.since):0,r=await dr(e,{channel:t.channel,since:n});return g({ref:r.ref,events:r.events,capacity:r.logCapacity},()=>{if(r.events.length===0){c(t.since||t.channel?"Nothing in the log for that.":"Nothing in the log yet. It fills as clients connect, and starts again when the project's host restarts.");return}for(let o of r.events)process.stdout.write(`${na(o)}
644
- `);r.logCapacity!==null&&r.events.length>=r.logCapacity&&c(b(`The log keeps the newest ${r.logCapacity} events; older ones have gone.`))}),0}J();ne();q();import{readFile as Bd}from"node:fs/promises";var Hd="api.snoutdata.com";function ia(e,t){return{ref:e,enabled:t.enabled===!0,siteUrl:t.settings?.site_url??null,redirects:t.settings?.uri_allow_list??[],google:{enabled:t.google?.enabled===!0,clientId:t.google?.clientId??null,clientSecretSet:t.google?.clientSecretSet===!0,ready:t.google?.ready===!0},googleCallback:`https://${e}.${Hd}/auth/v1/callback`,anonymous:{enabled:t.anonymous?.enabled===!0,ready:t.anonymous?.ready===!0}}}async function Gd(e){return ia(e,await _("cloud-project-auth",{ref:e,action:"status"}))}async function nn(e,t){return ia(e,await _("cloud-project-auth",{ref:e,action:"settings",settings:t}))}function ur(e){let t=e.google,n="off";t.ready?n=k("on"):t.enabled&&(n=`switched on, not offered yet: needs ${[!t.clientId&&"a client id",!t.clientSecretSet&&"a client secret"].filter(Boolean).join(" and ")}`),process.stdout.write(`${F([["auth",e.enabled?k("on"):"off"],["site url",e.siteUrl??b("not set")],["redirects",e.redirects.length?e.redirects.join(", "):b("none")],["google",n],["google client id",t.clientId??b("not set")],["google callback",e.googleCallback],["guests",e.anonymous.ready?k("on"):e.anonymous.enabled?"switched on, not applied yet":"off"]])}
645
- `),e.enabled||c(b(` snoutdata products enable auth --ref ${e.ref}`))}async function qd(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\r?\n$/,"")}async function ca(e,t,n,r){if(t===void 0||t==="show"){let o=await Gd(e);g(o,()=>ur(o));return}if(t==="google"){if(n[0]==="off"){let a=await nn(e,{googleEnabled:!1});g(a,()=>c("Google sign-in is off. Your auth service restarts within about a minute."));return}if(n.length>0)throw new h('auth google takes --client-id and --stdin, or "off"');let o={googleEnabled:!0};if(r.clientId&&(o.googleClientId=r.clientId),r.stdin){let a=await qd();if(!a)throw new h("--stdin read nothing. Pipe the Google client secret in.");o.googleClientSecret=a}let s=await nn(e,o);g(s,()=>{s.google.ready?c("Google sign-in is on. Your auth service restarts within about a minute."):ur(s),c(b(` Add ${s.googleCallback} to your Google client's Authorized redirect URIs.`))});return}if(t==="anonymous"||t==="guests"){let o=n[0];if(o!=="on"&&o!=="off")throw new h('auth anonymous takes "on" or "off"');let s=await nn(e,{anonymousEnabled:o==="on"});g(s,()=>{o==="off"?c("Guest sign-in is off. Guests who already have a session keep it until it ends. Your auth service restarts within about a minute."):s.enabled?c("Guest sign-in is on: signInAnonymously() gives a browser a session with no email or password. Your auth service restarts within about a minute."):(c("Guest sign-in is switched on, and takes effect when auth is."),c(b(` snoutdata products enable auth --ref ${s.ref}`)))});return}if(t==="redirects"){let o={};if(r.siteUrl!==void 0&&(o.siteUrl=r.siteUrl||null),r.allow!==void 0&&(o.uriAllowList=r.allow.split(",").map(a=>a.trim()).filter(Boolean)),Object.keys(o).length===0)throw new h("auth redirects needs --site-url and/or --allow URL,URL (an empty --allow= clears the list)");let s=await nn(e,o);g(s,()=>ur(s));return}if(t==="templates"){let o=await _("cloud-project-auth",{ref:e,action:"templates"});g(o,()=>aa(o.templates));return}if(t==="template"){let o=n[0];if(!o||!sa.includes(o))throw new h(`auth template needs a kind: ${sa.join(", ")}`);let s;if(n[1]==="reset")s={kind:o,reset:!0};else{if(!r.file)throw new h(`auth template ${o} needs --file body.html (and --subject), or "reset"`);let l=await Bd(r.file,"utf8"),i=await _("cloud-project-auth",{ref:e,action:"templates"}),u=r.subject??i.templates.find(d=>d.kind===o)?.subject;s={kind:o,subject:u,body:l}}let a=await _("cloud-project-auth",{ref:e,action:"template",template:s});g(a,()=>{aa(a.templates),c(b(" Your auth service restarts with it within about a minute."))});return}throw new h(`unknown command: auth ${t}. auth [google [off] | anonymous on|off | redirects | templates | template KIND]`)}var sa=["confirmation","recovery","magic_link","invite","email_change"];function aa(e){process.stdout.write(`${F([["KIND","SOURCE","SUBJECT"],...e.map(t=>[t.kind,t.custom?k("yours"):"ours",t.subject])])}
646
- `)}J();q();ee();import{spawn as Wd}from"node:child_process";import{createHash as Jd}from"node:crypto";import{existsSync as zd,readdirSync as Vd,readFileSync as Yd,statSync as Xd}from"node:fs";import{isAbsolute as Zd,join as mr,relative as Qd,resolve as eu}from"node:path";function Kd(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function rn(e,t,n={}){let r=Kd(e),o=new Map(r.map(u=>[u.name,u])),s=[];for(let u of t){let d=o.get(u.name);if(!d){s.push({code:"missing",name:u.name,reason:`${u.name} has already run against this database and is no longer in the folder. If it was renamed, the old name is still recorded and the new one looks new, so its statements would run a second time.`});continue}d.checksum!==u.checksum&&s.push({code:"changed",name:u.name,reason:`${u.name} has changed since it ran. The database and this folder no longer agree about what happened to the schema, and running anything cannot fix that. Put the change in a new migration instead.`})}let a=t.map(u=>u.name).sort((u,d)=>u<d?-1:u>d?1:0)[t.length-1],l=new Set(t.map(u=>u.name)),i=[];for(let u of r)if(!l.has(u.name)){if(a!==void 0&&u.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:u.name,reason:`${u.name} sorts before ${a}, which has already run. Applying it now gives this database a history no fresh database will have. Pass --out-of-order if that is what you mean.`});continue}i.push(u)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:i}}function ft(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function da(e){let t=Qd(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var pt="_snoutdata_migrations",tu=`create table if not exists ${pt} (
641
+ `))}let o=Object.keys(n.presence);if(o.length>0){c(" presence:");for(let s of o)for(let a of n.presence[s].metas)c(` ${s} ${Gd(a)}`)}}}async function sa(e,t={}){if(t.watch&&Oo())throw new y("--watch is for a person; with --json, poll `realtime inspect --json` or `realtime logs --since 1m --json`");let n=await mr(e,{channel:t.channel});if(g(n.inspect,()=>qd(n)),!t.watch)return 0;c(""),c(b("Watching. New events appear below as they happen; Ctrl+C to stop."));let r=n.at+1,o=t.intervalMs??2e3;for(;;){await new Promise(a=>setTimeout(a,o));let s=await mr(e,{channel:t.channel,since:r});for(let a of s.events)process.stdout.write(`${oa(a)}
642
+ `);if(s.events.length>0){r=Math.max(...s.events.map(d=>d.at))+1;let a=s.inspect.channels.map(d=>`${d.name}: ${Object.keys(d.presence).join(", ")||"nobody tracked"}`).join("; ");process.stdout.write(b(` now ${a||"no channels open"}
643
+ `))}}}async function aa(e,t={}){let n=t.since?Fd(t.since):0,r=await mr(e,{channel:t.channel,since:n});return g({ref:r.ref,events:r.events,capacity:r.logCapacity},()=>{if(r.events.length===0){c(t.since||t.channel?"Nothing in the log for that.":"Nothing in the log yet. It fills as clients connect, and starts again when the project's host restarts.");return}for(let o of r.events)process.stdout.write(`${oa(o)}
644
+ `);r.logCapacity!==null&&r.events.length>=r.logCapacity&&c(b(`The log keeps the newest ${r.logCapacity} events; older ones have gone.`))}),0}z();re();K();import{readFile as Jd}from"node:fs/promises";var Wd="api.snoutdata.com";function la(e,t){return{ref:e,enabled:t.enabled===!0,siteUrl:t.settings?.site_url??null,redirects:t.settings?.uri_allow_list??[],google:{enabled:t.google?.enabled===!0,clientId:t.google?.clientId??null,clientSecretSet:t.google?.clientSecretSet===!0,ready:t.google?.ready===!0},googleCallback:`https://${e}.${Wd}/auth/v1/callback`,anonymous:{enabled:t.anonymous?.enabled===!0,ready:t.anonymous?.ready===!0}}}async function zd(e){return la(e,await S("cloud-project-auth",{ref:e,action:"status"}))}async function rn(e,t){return la(e,await S("cloud-project-auth",{ref:e,action:"settings",settings:t}))}function fr(e){let t=e.google,n="off";t.ready?n=x("on"):t.enabled&&(n=`switched on, not offered yet: needs ${[!t.clientId&&"a client id",!t.clientSecretSet&&"a client secret"].filter(Boolean).join(" and ")}`),process.stdout.write(`${F([["auth",e.enabled?x("on"):"off"],["site url",e.siteUrl??b("not set")],["redirects",e.redirects.length?e.redirects.join(", "):b("none")],["google",n],["google client id",t.clientId??b("not set")],["google callback",e.googleCallback],["guests",e.anonymous.ready?x("on"):e.anonymous.enabled?"switched on, not applied yet":"off"]])}
645
+ `),e.enabled||c(b(` snoutdata products enable auth --ref ${e.ref}`))}async function Vd(){let e=[];for await(let t of process.stdin)e.push(Buffer.from(t));return Buffer.concat(e).toString("utf8").replace(/\r?\n$/,"")}async function da(e,t,n,r){if(t===void 0||t==="show"){let o=await zd(e);g(o,()=>fr(o));return}if(t==="google"){if(n[0]==="off"){let a=await rn(e,{googleEnabled:!1});g(a,()=>c("Google sign-in is off. Your auth service restarts within about a minute."));return}if(n.length>0)throw new y('auth google takes --client-id and --stdin, or "off"');let o={googleEnabled:!0};if(r.clientId&&(o.googleClientId=r.clientId),r.stdin){let a=await Vd();if(!a)throw new y("--stdin read nothing. Pipe the Google client secret in.");o.googleClientSecret=a}let s=await rn(e,o);g(s,()=>{s.google.ready?c("Google sign-in is on. Your auth service restarts within about a minute."):fr(s),c(b(` Add ${s.googleCallback} to your Google client's Authorized redirect URIs.`))});return}if(t==="anonymous"||t==="guests"){let o=n[0];if(o!=="on"&&o!=="off")throw new y('auth anonymous takes "on" or "off"');let s=await rn(e,{anonymousEnabled:o==="on"});g(s,()=>{o==="off"?c("Guest sign-in is off. Guests who already have a session keep it until it ends. Your auth service restarts within about a minute."):s.enabled?c("Guest sign-in is on: signInAnonymously() gives a browser a session with no email or password. Your auth service restarts within about a minute."):(c("Guest sign-in is switched on, and takes effect when auth is."),c(b(` snoutdata products enable auth --ref ${s.ref}`)))});return}if(t==="redirects"){let o={};if(r.siteUrl!==void 0&&(o.siteUrl=r.siteUrl||null),r.allow!==void 0&&(o.uriAllowList=r.allow.split(",").map(a=>a.trim()).filter(Boolean)),Object.keys(o).length===0)throw new y("auth redirects needs --site-url and/or --allow URL,URL (an empty --allow= clears the list)");let s=await rn(e,o);g(s,()=>fr(s));return}if(t==="templates"){let o=await S("cloud-project-auth",{ref:e,action:"templates"});g(o,()=>ca(o.templates));return}if(t==="template"){let o=n[0];if(!o||!ia.includes(o))throw new y(`auth template needs a kind: ${ia.join(", ")}`);let s;if(n[1]==="reset")s={kind:o,reset:!0};else{if(!r.file)throw new y(`auth template ${o} needs --file body.html (and --subject), or "reset"`);let d=await Jd(r.file,"utf8"),i=await S("cloud-project-auth",{ref:e,action:"templates"}),u=r.subject??i.templates.find(l=>l.kind===o)?.subject;s={kind:o,subject:u,body:d}}let a=await S("cloud-project-auth",{ref:e,action:"template",template:s});g(a,()=>{ca(a.templates),c(b(" Your auth service restarts with it within about a minute."))});return}throw new y(`unknown command: auth ${t}. auth [google [off] | anonymous on|off | redirects | templates | template KIND]`)}var ia=["confirmation","recovery","magic_link","invite","email_change"];function ca(e){process.stdout.write(`${F([["KIND","SOURCE","SUBJECT"],...e.map(t=>[t.kind,t.custom?x("yours"):"ours",t.subject])])}
646
+ `)}z();K();te();import{spawn as Xd}from"node:child_process";import{createHash as Zd}from"node:crypto";import{existsSync as Qd,readdirSync as eu,readFileSync as tu,statSync as nu}from"node:fs";import{isAbsolute as ru,join as pr,relative as ou,resolve as su}from"node:path";function Yd(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function on(e,t,n={}){let r=Yd(e),o=new Map(r.map(u=>[u.name,u])),s=[];for(let u of t){let l=o.get(u.name);if(!l){s.push({code:"missing",name:u.name,reason:`${u.name} has already run against this database and is no longer in the folder. If it was renamed, the old name is still recorded and the new one looks new, so its statements would run a second time.`});continue}l.checksum!==u.checksum&&s.push({code:"changed",name:u.name,reason:`${u.name} has changed since it ran. The database and this folder no longer agree about what happened to the schema, and running anything cannot fix that. Put the change in a new migration instead.`})}let a=t.map(u=>u.name).sort((u,l)=>u<l?-1:u>l?1:0)[t.length-1],d=new Set(t.map(u=>u.name)),i=[];for(let u of r)if(!d.has(u.name)){if(a!==void 0&&u.name<a&&!n.outOfOrder){s.push({code:"out-of-order",name:u.name,reason:`${u.name} sorts before ${a}, which has already run. Applying it now gives this database a history no fresh database will have. Pass --out-of-order if that is what you mean.`});continue}i.push(u)}return s.length>0?{ok:!1,problems:s}:{ok:!0,pending:i}}function ft(e){return!/^[\t ]*--[\t ]*snoutdata:no-transaction[\t ]*$/im.test(e)}function ma(e){let t=ou(process.cwd(),e);return t&&!t.startsWith("..")?t:e}var pt="_snoutdata_migrations",au=`create table if not exists ${pt} (
647
647
  name text primary key,
648
648
  checksum text not null,
649
649
  applied_at timestamptz not null default now()
650
- );`;function nu(e){if(!zd(e)||!Xd(e).isDirectory())throw new Error(`no migrations folder at ${da(e)}. Put your .sql files there, or pass --dir.`);return Vd(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=mr(e,t),r=Yd(n,"utf8");return{name:t,path:n,sql:r,checksum:Jd("sha256").update(r,"utf8").digest("hex")}})}function fr(e,t,n=[]){return new Promise(r=>{let o=Wd("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1",...n],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ne(e.ssl)}}),s="",a="";o.stdout.on("data",l=>s+=String(l)),o.stderr.on("data",l=>a+=String(l)),o.on("error",l=>{r({code:127,out:"",err:l.code==="ENOENT"?"psql is not installed, and db push needs it to run your migrations. `snoutdata db url` prints a connection string for any client.":String(l)})}),o.on("close",l=>r({code:l??0,out:s,err:a})),o.stdin.end(t)})}async function ru(e){let t=await fr(e,tu);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${pt}`);let n=await fr(e,`select name || E'\\t' || checksum from ${pt};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${pt}`);return n.out.split(`
651
- `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let o=r.indexOf(" ");return{name:r.slice(0,o),checksum:r.slice(o+1)}})}function la(e){return`'${e.replace(/'/g,"''")}'`}async function ua(e,t={}){let n=await pr(e,t,r=>c(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
652
- `);O("conflict",`Refused. This folder and ${e} do not agree:
653
- ${r}`,{ref:e,problems:n.problems})}O("failed",`${n.failed} failed, and nothing after it was tried.
654
- ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return g(n,()=>{if(n.wouldApply){c(`Would apply ${n.wouldApply.length} migration${n.wouldApply.length===1?"":"s"} to ${e}:`);for(let r of n.wouldApply)c(` ${r}`);return}if(n.upToDate){c(`${e} is up to date.`);return}c(`Applied ${n.applied.length} migration${n.applied.length===1?"":"s"} to ${e}.`)}),0}async function pr(e,t={},n=()=>{}){let r=eu(t.dir===void 0?mr(process.cwd(),"migrations"):Zd(t.dir)?t.dir:mr(process.cwd(),t.dir)),o=[];function s(m){o.push(m),n(m)}let a=nu(r);if(a.length===0)return s(`No .sql files in ${da(r)}.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};let l=await te(e);l.wakesInstantly||s("This project is paused; the connection will wake it, which takes a few seconds.");let i=await ru(l),u=rn(a,i,{outOfOrder:t.outOfOrder});if(!u.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...u.problems],notes:o};let d=new Map(a.map(m=>[m.name,m])),f=u.pending.map(m=>d.get(m.name));if(f.length===0)return s(`${e} is up to date: ${i.length} migration${i.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let m of f)ft(m.sql)||s(`${m.name} would run outside a transaction.`);return{ref:e,directory:r,ok:!0,applied:[],wouldApply:f.map(m=>m.name),notes:o}}let y=[];for(let m of f){let p=`insert into ${pt} (name, checksum) values (${la(m.name)}, ${la(m.checksum)});`,S=ft(m.sql);S||s(`${m.name} runs outside a transaction, so an interruption can leave it half applied.`);let w=S?`begin;
655
- ${m.sql}
656
- ${p}
650
+ );`;function iu(e){if(!Qd(e)||!nu(e).isDirectory())throw new Error(`no migrations folder at ${ma(e)}. Put your .sql files there, or pass --dir.`);return eu(e).filter(t=>t.endsWith(".sql")).map(t=>{let n=pr(e,t),r=tu(n,"utf8");return{name:t,path:n,sql:r,checksum:Zd("sha256").update(r,"utf8").digest("hex")}})}function gr(e,t,n=[]){return new Promise(r=>{let o=Xd("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1",...n],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,...Ce(e.ssl)}}),s="",a="";o.stdout.on("data",d=>s+=String(d)),o.stderr.on("data",d=>a+=String(d)),o.on("error",d=>{r({code:127,out:"",err:d.code==="ENOENT"?"psql is not installed, and db push needs it to run your migrations. `snoutdata db url` prints a connection string for any client.":String(d)})}),o.on("close",d=>r({code:d??0,out:s,err:a})),o.stdin.end(t)})}async function cu(e){let t=await gr(e,au);if(t.code!==0)throw new Error(t.err.trim()||`could not create ${pt}`);let n=await gr(e,`select name || E'\\t' || checksum from ${pt};`,["-tA"]);if(n.code!==0)throw new Error(n.err.trim()||`could not read ${pt}`);return n.out.split(`
651
+ `).map(r=>r.trim()).filter(r=>r.length>0).map(r=>{let o=r.indexOf(" ");return{name:r.slice(0,o),checksum:r.slice(o+1)}})}function ua(e){return`'${e.replace(/'/g,"''")}'`}async function fa(e,t={}){let n=await hr(e,t,r=>c(r));if(!n.ok){if(n.problems){let r=n.problems.map(o=>` ${o.reason}`).join(`
652
+ `);N("conflict",`Refused. This folder and ${e} do not agree:
653
+ ${r}`,{ref:e,problems:n.problems})}N("failed",`${n.failed} failed, and nothing after it was tried.
654
+ ${n.error??""}`.trimEnd(),{ref:e,failed:n.failed})}return g(n,()=>{if(n.wouldApply){c(`Would apply ${n.wouldApply.length} migration${n.wouldApply.length===1?"":"s"} to ${e}:`);for(let r of n.wouldApply)c(` ${r}`);return}if(n.upToDate){c(`${e} is up to date.`);return}c(`Applied ${n.applied.length} migration${n.applied.length===1?"":"s"} to ${e}.`)}),0}async function hr(e,t={},n=()=>{}){let r=su(t.dir===void 0?pr(process.cwd(),"migrations"):ru(t.dir)?t.dir:pr(process.cwd(),t.dir)),o=[];function s(R){o.push(R),n(R)}let a=iu(r);if(a.length===0)return s(`No .sql files in ${ma(r)}.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};let d=await ne(e);d.wakesInstantly||s("This project is paused; the connection will wake it, which takes a few seconds.");let i=await cu(d),u=on(a,i,{outOfOrder:t.outOfOrder});if(!u.ok)return{ref:e,directory:r,ok:!1,applied:[],problems:[...u.problems],notes:o};let l=new Map(a.map(R=>[R.name,R])),f=u.pending.map(R=>l.get(R.name));if(f.length===0)return s(`${e} is up to date: ${i.length} migration${i.length===1?"":"s"} already applied.`),{ref:e,directory:r,ok:!0,applied:[],upToDate:!0,notes:o};if(t.dryRun){for(let R of f)ft(R.sql)||s(`${R.name} would run outside a transaction.`);return{ref:e,directory:r,ok:!0,applied:[],wouldApply:f.map(R=>R.name),notes:o}}let w=[];for(let R of f){let m=`insert into ${pt} (name, checksum) values (${ua(R.name)}, ${ua(R.checksum)});`,p=ft(R.sql);p||s(`${R.name} runs outside a transaction, so an interruption can leave it half applied.`);let h=p?`begin;
655
+ ${R.sql}
656
+ ${m}
657
657
  commit;
658
- `:`${m.sql}
659
- ${p}
660
- `,x=await fr(l,w);if(x.code!==0)return S&&s("It ran inside a transaction, so nothing from it is in the database."),{ref:e,directory:r,ok:!1,applied:y,failed:m.name,error:x.err.trim(),notes:o};y.push(m.name),s(` applied ${m.name}`)}return{ref:e,directory:r,ok:!0,applied:y,notes:o}}J();ne();q();import{readFile as ma}from"node:fs/promises";function gr(e){if(e==="apns"||e==="fcm")return e;throw new h("say which: apns (an Apple .p8) or fcm (a Firebase service account)")}async function ou(e){let t=await _("cloud-project-push",{ref:e,credentials:{list:!0}});return{ref:e,credentials:t??{}}}async function fa(e){if(!e.p8||!e.keyId||!e.teamId||!e.topic)throw new h("push credentials set apns needs --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app");if(e.environment!==void 0&&e.environment!=="production"&&e.environment!=="sandbox")throw new h("--environment is production or sandbox; leave it out for a key that serves both");return{topic:e.topic,keys:[{p8:await ma(e.p8,"utf8"),key_id:e.keyId,team_id:e.teamId,environment:e.environment??null}]}}async function pa(e){if(!e)throw new h("push credentials set fcm needs --file service-account.json");return{service_account:await ma(e,"utf8")}}async function su(e,t,n){let r=await _("cloud-project-push",{ref:e,credentials:{set:{kind:t,body:n}}});return{ref:e,kind:t,summary:r}}async function au(e,t){return await _("cloud-project-push",{ref:e,credentials:{remove:t}}),{ref:e,kind:t,removed:!0}}async function ga(e){let t=await ou(e);g(t,()=>{let{apns:n,fcm:r,vapid:o}=t.credentials;process.stdout.write(`${F([["TRANSPORT","SET",""],["web push",o?"yes":"starting",o?.public_key?`public key ${o.public_key.slice(0,16)}\u2026`:""],["apns",n?"yes":"no",n?`${n.topic}, key ${n.keys?.map(s=>s.key_id).join(", ")}`:""],["fcm",r?"yes":"no",r?`${r.project_id} as ${r.client_email}`:""]])}
661
- `),(!n||!r)&&c(b(` snoutdata push credentials set ${n?"fcm --file service-account.json":"apns --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app"} --ref ${e}`))})}async function ha(e,t,n){let r=await su(e,t,n);g(r,()=>c(`${t==="apns"?"The Apple key signed":"Google accepted the service account"}, and it is stored in the project's own database.`))}async function ya(e,t){let n=await au(e,t);g(n,()=>c(`${t} credentials removed. Devices on it stop receiving until new ones are set.`))}J();ee();q();import{spawn as Su}from"node:child_process";import{writeFileSync as _u}from"node:fs";import{isAbsolute as xu,resolve as vu}from"node:path";var cu=" ";function E(e){return cu.repeat(e)}function X(e){return JSON.stringify(e)}function de(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:X(e)}function De(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function lu(e){let t=(e??"").trim(),n=!1;for(;t.endsWith("[]");)n=!0,t=t.slice(0,-2).trim();t.startsWith("_")&&t.length>1&&(n=!0,t=t.slice(1));let r=t.indexOf("(");return r>0&&(t=t.slice(0,r)),{base:t.trim().toLowerCase(),array:n}}var du={smallint:"number",int2:"number",integer:"number",int:"number",int4:"number",bigint:"number",int8:"number",decimal:"number",numeric:"number",number:"number",real:"number",float:"number",float4:"number",float8:"number","double precision":"number",double:"number",binary_float:"number",binary_double:"number",smallserial:"number",serial:"number",bigserial:"number",oid:"number",smallmoney:"number",money:"number",bool:"boolean",boolean:"boolean",json:"json",jsonb:"json",variant:"json",object:"json",char:"string",character:"string","character varying":"string",varchar:"string",varchar2:"string",nvarchar:"string",nvarchar2:"string",nchar:"string",text:"string",ntext:"string",citext:"string",clob:"string",nclob:"string",longtext:"string",mediumtext:"string",tinytext:"string",uuid:"string",uniqueidentifier:"string",date:"string",time:"string","time without time zone":"string","time with time zone":"string",timetz:"string",timestamp:"string","timestamp without time zone":"string","timestamp with time zone":"string",timestamptz:"string",datetime:"string",datetime2:"string",datetimeoffset:"string",smalldatetime:"string",timestamp_ntz:"string",timestamp_ltz:"string",timestamp_tz:"string",interval:"string",year:"string",bytea:"string",blob:"string",longblob:"string",mediumblob:"string",tinyblob:"string",binary:"string",varbinary:"string",raw:"string",xml:"string",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",tsvector:"string",tsquery:"string",name:"string",bit:"string",varbit:"string",tinyint:"number",enum:"string",set:"string"},uu={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function mu(e,t){let n=uu[e]?.[t];return n||(du[t]??"unknown")}function on(e,t){let{base:n,array:r}=lu(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=mu(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function fu(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function hr(e,t,n,r,o=!1){if(o&&t.generatedAlways===!0)return`${E(n)}${de(t.name)}?: never`;let s=on(e,t.dataType),a=t.nullable?`${s} | null`:s;return`${E(n)}${de(t.name)}${r?"?":""}: ${a}`}function pu(e,t){let n=[...t.columns].sort(),r=o=>{let s=[...o].sort();return s.length===n.length&&s.every((a,l)=>a===n[l])};return e.primaryKey.length>0&&r(e.primaryKey)?!0:(e.indexes??[]).some(o=>o.unique&&r(o.columns))}function gu(e,t){let n=[],r=[...e.foreignKeys].sort((o,s)=>o.constraintName<s.constraintName?-1:o.constraintName>s.constraintName?1:0);if(r.length===0)return n.push(`${E(t)}Relationships: []`),n;n.push(`${E(t)}Relationships: [`);for(let o of r)n.push(`${E(t+1)}{`),n.push(`${E(t+2)}foreignKeyName: ${X(o.constraintName)}`),n.push(`${E(t+2)}columns: [${o.columns.map(X).join(", ")}]`),n.push(`${E(t+2)}isOneToOne: ${pu(e,o)?"true":"false"}`),n.push(`${E(t+2)}referencedRelation: ${X(o.referencedTable)}`),n.push(`${E(t+2)}referencedColumns: [${o.referencedColumns.map(X).join(", ")}]`),n.push(`${E(t+1)}},`);return n.push(`${E(t)}]`),n}function wa(e,t,n){let r=De(t.columns),o=[];o.push(`${E(n)}${de(t.name)}: {`),o.push(`${E(n+1)}Row: {`);for(let s of r)o.push(hr(e,s,n+2,!1));if(o.push(`${E(n+1)}}`),t.kind!=="view"){o.push(`${E(n+1)}Insert: {`);for(let s of r)o.push(hr(e,s,n+2,fu(s),!0));o.push(`${E(n+1)}}`),o.push(`${E(n+1)}Update: {`);for(let s of r)o.push(hr(e,s,n+2,!0,!0));o.push(`${E(n+1)}}`)}return o.push(...gu(t,n+1)),o.push(`${E(n)}}`),o}function gt(e,t){return[`${E(t)}${e}: {`,`${E(t+1)}[_ in never]: never`,`${E(t)}}`]}function hu(e,t){if(t.kind==="procedure"||!t.returnType)return"undefined";let n=t.returnType.trim(),r=n.toLowerCase();if(r==="void"||r==="")return"undefined";if(r.startsWith("table("))return"Record<string, unknown>[]";if(r.startsWith("setof ")){let s=n.slice(6).trim();if(s.toLowerCase()==="record")return"Record<string, unknown>[]";let a=e.rowRefs.get(s.toLowerCase());return a?`${a}[]`:`${on(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??on(e,n)}function yu(e,t,n){let r=[],o=De((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${E(n)}${de(t.name)}: {`),o.length===0)r.push(`${E(n+1)}Args: never`);else{r.push(`${E(n+1)}Args: {`);for(let s of o)r.push(`${E(n+2)}${de(s.name)}: ${on(e,s.dataType)}`);r.push(`${E(n+1)}}`)}return r.push(`${E(n+1)}Returns: ${hu(e,t)}`),r.push(`${E(n)}}`),r}function qe(e,t){return e.schema&&e.schema.length>0?e.schema:t}function ba(e,t={}){let n=t.defaultSchema??"public",r=t.driverId??"postgres",o=t.enums??[],s=new Map;for(let f of o){let y=f.schema&&f.schema.length>0?f.schema:n;s.set(f.name.toLowerCase(),`Database[${X(y)}][${X("Enums")}][${X(f.name)}]`)}let a=new Map;for(let f of e.tables){let y=qe(f,n),m=f.kind==="view"?"Views":"Tables";a.set(f.name.toLowerCase(),`Database[${X(y)}][${X(m)}][${X(f.name)}][${X("Row")}]`)}let l={driverId:r,defaultSchema:n,enumRefs:s,rowRefs:a},i=new Set([n]);for(let f of e.tables)i.add(qe(f,n));for(let f of e.routines??[])i.add(qe(f,n));for(let f of o)i.add(f.schema&&f.schema.length>0?f.schema:n);let u=[...i].sort(),d=[];d.push("export type Json ="),d.push(" | string"),d.push(" | number"),d.push(" | boolean"),d.push(" | null"),d.push(" | { [key: string]: Json | undefined }"),d.push(" | Json[]"),d.push(""),d.push("export type Database = {");for(let f of u){let y=De(e.tables.filter(w=>qe(w,n)===f&&w.kind!=="view")),m=De(e.tables.filter(w=>qe(w,n)===f&&w.kind==="view")),p=De((e.routines??[]).filter(w=>qe(w,n)===f)),S=De(o.filter(w=>(w.schema&&w.schema.length>0?w.schema:n)===f));if(d.push(`${E(1)}${de(f)}: {`),y.length===0)d.push(...gt("Tables",2));else{d.push(`${E(2)}Tables: {`);for(let w of y)d.push(...wa(l,w,3));d.push(`${E(2)}}`)}if(m.length===0)d.push(...gt("Views",2));else{d.push(`${E(2)}Views: {`);for(let w of m)d.push(...wa(l,w,3));d.push(`${E(2)}}`)}if(p.length===0)d.push(...gt("Functions",2));else{d.push(`${E(2)}Functions: {`);for(let w of p)d.push(...yu(l,w,3));d.push(`${E(2)}}`)}if(S.length===0)d.push(...gt("Enums",2));else{d.push(`${E(2)}Enums: {`);for(let w of S)d.push(`${E(3)}${de(w.name)}: ${w.values.map(X).join(" | ")}`);d.push(`${E(2)}}`)}d.push(...gt("CompositeTypes",2)),d.push(`${E(1)}}`)}return d.push("}"),t.helpers!==!1&&(d.push(""),d.push(bu(n))),t.constants!==!1&&(d.push(""),d.push(wu(u,o,n))),`${d.join(`
658
+ `:`${R.sql}
659
+ ${m}
660
+ `,$=await gr(d,h);if($.code!==0)return p&&s("It ran inside a transaction, so nothing from it is in the database."),{ref:e,directory:r,ok:!1,applied:w,failed:R.name,error:$.err.trim(),notes:o};w.push(R.name),s(` applied ${R.name}`)}return{ref:e,directory:r,ok:!0,applied:w,notes:o}}z();re();K();import{readFile as pa}from"node:fs/promises";function yr(e){if(e==="apns"||e==="fcm")return e;throw new y("say which: apns (an Apple .p8) or fcm (a Firebase service account)")}async function lu(e){let t=await S("cloud-project-push",{ref:e,credentials:{list:!0}});return{ref:e,credentials:t??{}}}async function ga(e){if(!e.p8||!e.keyId||!e.teamId||!e.topic)throw new y("push credentials set apns needs --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app");if(e.environment!==void 0&&e.environment!=="production"&&e.environment!=="sandbox")throw new y("--environment is production or sandbox; leave it out for a key that serves both");return{topic:e.topic,keys:[{p8:await pa(e.p8,"utf8"),key_id:e.keyId,team_id:e.teamId,environment:e.environment??null}]}}async function ha(e){if(!e)throw new y("push credentials set fcm needs --file service-account.json");return{service_account:await pa(e,"utf8")}}async function du(e,t,n){let r=await S("cloud-project-push",{ref:e,credentials:{set:{kind:t,body:n}}});return{ref:e,kind:t,summary:r}}async function uu(e,t){return await S("cloud-project-push",{ref:e,credentials:{remove:t}}),{ref:e,kind:t,removed:!0}}async function ya(e){let t=await lu(e);g(t,()=>{let{apns:n,fcm:r,vapid:o}=t.credentials;process.stdout.write(`${F([["TRANSPORT","SET",""],["web push",o?"yes":"starting",o?.public_key?`public key ${o.public_key.slice(0,16)}\u2026`:""],["apns",n?"yes":"no",n?`${n.topic}, key ${n.keys?.map(s=>s.key_id).join(", ")}`:""],["fcm",r?"yes":"no",r?`${r.project_id} as ${r.client_email}`:""]])}
661
+ `),(!n||!r)&&c(b(` snoutdata push credentials set ${n?"fcm --file service-account.json":"apns --p8 AuthKey.p8 --key-id ID --team-id ID --topic com.example.app"} --ref ${e}`))})}async function wa(e,t,n){let r=await du(e,t,n);g(r,()=>c(`${t==="apns"?"The Apple key signed":"Google accepted the service account"}, and it is stored in the project's own database.`))}async function ba(e,t){let n=await uu(e,t);g(n,()=>c(`${t} credentials removed. Devices on it stop receiving until new ones are set.`))}z();te();K();import{spawn as Eu}from"node:child_process";import{writeFileSync as $u}from"node:fs";import{isAbsolute as Pu,resolve as Au}from"node:path";var fu=" ";function E(e){return fu.repeat(e)}function Z(e){return JSON.stringify(e)}function me(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)?e:Z(e)}function Me(e){return[...e].sort((t,n)=>t.name<n.name?-1:t.name>n.name?1:0)}function pu(e){let t=(e??"").trim(),n=!1;for(;t.endsWith("[]");)n=!0,t=t.slice(0,-2).trim();t.startsWith("_")&&t.length>1&&(n=!0,t=t.slice(1));let r=t.indexOf("(");return r>0&&(t=t.slice(0,r)),{base:t.trim().toLowerCase(),array:n}}var gu={smallint:"number",int2:"number",integer:"number",int:"number",int4:"number",bigint:"number",int8:"number",decimal:"number",numeric:"number",number:"number",real:"number",float:"number",float4:"number",float8:"number","double precision":"number",double:"number",binary_float:"number",binary_double:"number",smallserial:"number",serial:"number",bigserial:"number",oid:"number",smallmoney:"number",money:"number",bool:"boolean",boolean:"boolean",json:"json",jsonb:"json",variant:"json",object:"json",char:"string",character:"string","character varying":"string",varchar:"string",varchar2:"string",nvarchar:"string",nvarchar2:"string",nchar:"string",text:"string",ntext:"string",citext:"string",clob:"string",nclob:"string",longtext:"string",mediumtext:"string",tinytext:"string",uuid:"string",uniqueidentifier:"string",date:"string",time:"string","time without time zone":"string","time with time zone":"string",timetz:"string",timestamp:"string","timestamp without time zone":"string","timestamp with time zone":"string",timestamptz:"string",datetime:"string",datetime2:"string",datetimeoffset:"string",smalldatetime:"string",timestamp_ntz:"string",timestamp_ltz:"string",timestamp_tz:"string",interval:"string",year:"string",bytea:"string",blob:"string",longblob:"string",mediumblob:"string",tinyblob:"string",binary:"string",varbinary:"string",raw:"string",xml:"string",inet:"string",cidr:"string",macaddr:"string",macaddr8:"string",tsvector:"string",tsquery:"string",name:"string",bit:"string",varbit:"string",tinyint:"number",enum:"string",set:"string"},hu={sqlserver:{bit:"boolean"},"mysql-aurora":{bit:"number"},mariadb:{bit:"number"},sqlite:{bit:"number"},snowflake:{array:"json"}};function yu(e,t){let n=hu[e]?.[t];return n||(gu[t]??"unknown")}function sn(e,t){let{base:n,array:r}=pu(t),o=e.enumRefs.get(n);if(o)return r?`${o}[]`:o;if(n==="array")return"unknown[]";let s=yu(e.driverId,n),a=s==="json"?"Json":s;return r?`${a}[]`:a}function wu(e){return e.nullable||e.autoIncrement===!0||e.default!==null&&e.default!==void 0}function wr(e,t,n,r,o=!1){if(o&&t.generatedAlways===!0)return`${E(n)}${me(t.name)}?: never`;let s=sn(e,t.dataType),a=t.nullable?`${s} | null`:s;return`${E(n)}${me(t.name)}${r?"?":""}: ${a}`}function bu(e,t){let n=[...t.columns].sort(),r=o=>{let s=[...o].sort();return s.length===n.length&&s.every((a,d)=>a===n[d])};return e.primaryKey.length>0&&r(e.primaryKey)?!0:(e.indexes??[]).some(o=>o.unique&&r(o.columns))}function Su(e,t){let n=[],r=[...e.foreignKeys].sort((o,s)=>o.constraintName<s.constraintName?-1:o.constraintName>s.constraintName?1:0);if(r.length===0)return n.push(`${E(t)}Relationships: []`),n;n.push(`${E(t)}Relationships: [`);for(let o of r)n.push(`${E(t+1)}{`),n.push(`${E(t+2)}foreignKeyName: ${Z(o.constraintName)}`),n.push(`${E(t+2)}columns: [${o.columns.map(Z).join(", ")}]`),n.push(`${E(t+2)}isOneToOne: ${bu(e,o)?"true":"false"}`),n.push(`${E(t+2)}referencedRelation: ${Z(o.referencedTable)}`),n.push(`${E(t+2)}referencedColumns: [${o.referencedColumns.map(Z).join(", ")}]`),n.push(`${E(t+1)}},`);return n.push(`${E(t)}]`),n}function Sa(e,t,n){let r=Me(t.columns),o=[];o.push(`${E(n)}${me(t.name)}: {`),o.push(`${E(n+1)}Row: {`);for(let s of r)o.push(wr(e,s,n+2,!1));if(o.push(`${E(n+1)}}`),t.kind!=="view"){o.push(`${E(n+1)}Insert: {`);for(let s of r)o.push(wr(e,s,n+2,wu(s),!0));o.push(`${E(n+1)}}`),o.push(`${E(n+1)}Update: {`);for(let s of r)o.push(wr(e,s,n+2,!0,!0));o.push(`${E(n+1)}}`)}return o.push(...Su(t,n+1)),o.push(`${E(n)}}`),o}function gt(e,t){return[`${E(t)}${e}: {`,`${E(t+1)}[_ in never]: never`,`${E(t)}}`]}function _u(e,t){if(t.kind==="procedure"||!t.returnType)return"undefined";let n=t.returnType.trim(),r=n.toLowerCase();if(r==="void"||r==="")return"undefined";if(r.startsWith("table("))return"Record<string, unknown>[]";if(r.startsWith("setof ")){let s=n.slice(6).trim();if(s.toLowerCase()==="record")return"Record<string, unknown>[]";let a=e.rowRefs.get(s.toLowerCase());return a?`${a}[]`:`${sn(e,s)}[]`}return r==="record"||r==="trigger"||r==="event_trigger"?"unknown":e.rowRefs.get(r)??sn(e,n)}function xu(e,t,n){let r=[],o=Me((t.parameters??[]).filter(s=>s.name.length>0&&(s.mode??"IN").toUpperCase()!=="OUT").map(s=>({name:s.name,dataType:s.dataType})));if(r.push(`${E(n)}${me(t.name)}: {`),o.length===0)r.push(`${E(n+1)}Args: never`);else{r.push(`${E(n+1)}Args: {`);for(let s of o)r.push(`${E(n+2)}${me(s.name)}: ${sn(e,s.dataType)}`);r.push(`${E(n+1)}}`)}return r.push(`${E(n+1)}Returns: ${_u(e,t)}`),r.push(`${E(n)}}`),r}function Ve(e,t){return e.schema&&e.schema.length>0?e.schema:t}function _a(e,t={}){let n=t.defaultSchema??"public",r=t.driverId??"postgres",o=t.enums??[],s=new Map;for(let f of o){let w=f.schema&&f.schema.length>0?f.schema:n;s.set(f.name.toLowerCase(),`Database[${Z(w)}][${Z("Enums")}][${Z(f.name)}]`)}let a=new Map;for(let f of e.tables){let w=Ve(f,n),R=f.kind==="view"?"Views":"Tables";a.set(f.name.toLowerCase(),`Database[${Z(w)}][${Z(R)}][${Z(f.name)}][${Z("Row")}]`)}let d={driverId:r,defaultSchema:n,enumRefs:s,rowRefs:a},i=new Set([n]);for(let f of e.tables)i.add(Ve(f,n));for(let f of e.routines??[])i.add(Ve(f,n));for(let f of o)i.add(f.schema&&f.schema.length>0?f.schema:n);let u=[...i].sort(),l=[];l.push("export type Json ="),l.push(" | string"),l.push(" | number"),l.push(" | boolean"),l.push(" | null"),l.push(" | { [key: string]: Json | undefined }"),l.push(" | Json[]"),l.push(""),l.push("export type Database = {");for(let f of u){let w=Me(e.tables.filter(h=>Ve(h,n)===f&&h.kind!=="view")),R=Me(e.tables.filter(h=>Ve(h,n)===f&&h.kind==="view")),m=Me((e.routines??[]).filter(h=>Ve(h,n)===f)),p=Me(o.filter(h=>(h.schema&&h.schema.length>0?h.schema:n)===f));if(l.push(`${E(1)}${me(f)}: {`),w.length===0)l.push(...gt("Tables",2));else{l.push(`${E(2)}Tables: {`);for(let h of w)l.push(...Sa(d,h,3));l.push(`${E(2)}}`)}if(R.length===0)l.push(...gt("Views",2));else{l.push(`${E(2)}Views: {`);for(let h of R)l.push(...Sa(d,h,3));l.push(`${E(2)}}`)}if(m.length===0)l.push(...gt("Functions",2));else{l.push(`${E(2)}Functions: {`);for(let h of m)l.push(...xu(d,h,3));l.push(`${E(2)}}`)}if(p.length===0)l.push(...gt("Enums",2));else{l.push(`${E(2)}Enums: {`);for(let h of p)l.push(`${E(3)}${me(h.name)}: ${h.values.map(Z).join(" | ")}`);l.push(`${E(2)}}`)}l.push(...gt("CompositeTypes",2)),l.push(`${E(1)}}`)}return l.push("}"),t.helpers!==!1&&(l.push(""),l.push(vu(n))),t.constants!==!1&&(l.push(""),l.push(ku(u,o,n))),`${l.join(`
662
662
  `)}
663
- `}function wu(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=De(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${E(1)}${de(o)}: {`),r.push(`${E(2)}Enums: {},`),r.push(`${E(1)}},`);continue}r.push(`${E(1)}${de(o)}: {`),r.push(`${E(2)}Enums: {`);for(let a of s)r.push(`${E(3)}${de(a.name)}: [${a.values.map(X).join(", ")}],`);r.push(`${E(2)}},`),r.push(`${E(1)}},`)}return r.push("} as const"),r.join(`
664
- `)}function bu(e){return`type DatabaseWithoutInternals = Database
663
+ `}function ku(e,t,n){let r=["export const Constants = {"];for(let o of e){let s=Me(t.filter(a=>(a.schema&&a.schema.length>0?a.schema:n)===o));if(s.length===0){r.push(`${E(1)}${me(o)}: {`),r.push(`${E(2)}Enums: {},`),r.push(`${E(1)}},`);continue}r.push(`${E(1)}${me(o)}: {`),r.push(`${E(2)}Enums: {`);for(let a of s)r.push(`${E(3)}${me(a.name)}: [${a.values.map(Z).join(", ")}],`);r.push(`${E(2)}},`),r.push(`${E(1)}},`)}return r.push("} as const"),r.join(`
664
+ `)}function vu(e){return`type DatabaseWithoutInternals = Database
665
665
 
666
- type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${X(e)}>]
666
+ type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${Z(e)}>]
667
667
 
668
668
  export type Tables<
669
669
  DefaultSchemaTableNameOrOptions extends
@@ -776,7 +776,7 @@ export type CompositeTypes<
776
776
  ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
777
777
  : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
778
778
  ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
779
- : never`}function ku(e){let t=ir(e.ssl);return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:t.mode,...t.rootCert?{sslRootCert:t.rootCert}:{},label:e.ref}}function sn(e){let t;try{t=new URL(e)}catch{throw new Error(`--db-url is not a URL: ${e}`)}if(t.protocol!=="postgres:"&&t.protocol!=="postgresql:")throw new Error(`--db-url must be a postgres:// URL, not ${t.protocol}//`);let n=decodeURIComponent(t.pathname.replace(/^\//,""));if(!n)throw new Error("--db-url has no database name in its path");let r=t.searchParams.get("sslmode")??"prefer";return{host:t.hostname||"127.0.0.1",port:t.port?Number(t.port):5432,database:n,user:decodeURIComponent(t.username)||"postgres",password:decodeURIComponent(t.password),sslMode:r,label:`${t.hostname||"127.0.0.1"}/${n}`}}function yr(e,t){return new Promise(n=>{let r=Su("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA"],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.sslMode,...e.sslRootCert?{PGSSLROOTCERT:e.sslRootCert}:{}}}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{n({code:127,out:"",err:a.code==="ENOENT"?"psql is not installed, and gen types needs it to read the schema. `snoutdata db url` prints a connection string for any client.":String(a)})}),r.on("close",a=>n({code:a??0,out:o,err:s})),r.stdin.end(t)})}var Eu=`
779
+ : never`}function Tu(e){let t=lr(e.ssl);return{host:e.host,port:e.port,database:e.database,user:e.user,password:e.password,sslMode:t.mode,...t.rootCert?{sslRootCert:t.rootCert}:{},label:e.ref}}function an(e){let t;try{t=new URL(e)}catch{throw new Error(`--db-url is not a URL: ${e}`)}if(t.protocol!=="postgres:"&&t.protocol!=="postgresql:")throw new Error(`--db-url must be a postgres:// URL, not ${t.protocol}//`);let n=decodeURIComponent(t.pathname.replace(/^\//,""));if(!n)throw new Error("--db-url has no database name in its path");let r=t.searchParams.get("sslmode")??"prefer";return{host:t.hostname||"127.0.0.1",port:t.port?Number(t.port):5432,database:n,user:decodeURIComponent(t.username)||"postgres",password:decodeURIComponent(t.password),sslMode:r,label:`${t.hostname||"127.0.0.1"}/${n}`}}function br(e,t){return new Promise(n=>{let r=Eu("psql",["--host",e.host,"--port",String(e.port),"--username",e.user,"--dbname",e.database,"--no-psqlrc","-v","ON_ERROR_STOP=1","-tA"],{stdio:["pipe","pipe","pipe"],env:{...process.env,PGPASSWORD:e.password,PGSSLMODE:e.sslMode,...e.sslRootCert?{PGSSLROOTCERT:e.sslRootCert}:{}}}),o="",s="";r.stdout.on("data",a=>o+=String(a)),r.stderr.on("data",a=>s+=String(a)),r.on("error",a=>{n({code:127,out:"",err:a.code==="ENOENT"?"psql is not installed, and gen types needs it to read the schema. `snoutdata db url` prints a connection string for any client.":String(a)})}),r.on("close",a=>n({code:a??0,out:o,err:s})),r.stdin.end(t)})}var Ru=`
780
780
  with wanted as (select unnest($SCHEMAS$::text[]) as nspname)
781
781
  select json_build_object(
782
782
  'tables', coalesce((
@@ -880,29 +880,29 @@ select json_build_object(
880
880
  where t.typtype = 'e' and n.nspname in (select nspname from wanted)
881
881
  ), '[]'::json)
882
882
  )::text
883
- `;function $u(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function Au(e,t,n=0){let r=e.tables.map(a=>{let l=a.primaryKey??[],i=new Set(l),u=a.columns.map(d=>({name:d.name,dataType:d.dataType,nullable:d.nullable,isPrimaryKey:i.has(d.name),default:d.default,autoIncrement:d.autoIncrement,...d.generatedAlways===!0?{generatedAlways:!0}:{}}));return{name:a.name,schema:a.schema,kind:a.kind,columns:u,primaryKey:l,foreignKeys:(a.foreignKeys??[]).map(d=>({constraintName:d.constraintName,columns:d.columns??[],referencedTable:d.referencedTable,referencedColumns:d.referencedColumns??[]})),indexes:(a.indexes??[]).filter(d=>Array.isArray(d.columns)&&d.columns.every(f=>typeof f=="string")).map(d=>({name:d.name,unique:d.unique,columns:d.columns??[]}))}}),o=e.routines.map(a=>({name:a.name,schema:a.schema,kind:a.kind,returnType:a.returnType??void 0,parameters:(a.parameters??[]).map(l=>({name:l.name,dataType:l.dataType,mode:l.mode}))})),s=e.enums.map(a=>({name:a.name,schema:a.schema,values:a.values??[]}));return{schema:{database:t,tables:r,routines:o,fetchedAt:n},enums:s}}async function Pu(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?sn(e.dbUrl):ku(await te(Tu(e))),r=Eu.replace("$SCHEMAS$",$u(t)),o=await(e.sql?e.sql(r):yr(n,r));o.code===127&&!e.sql&&!e.dbUrl&&e.ref&&(o=await Ru(e.ref,r)),o.code===127&&O("tool-missing",o.err.trim()),o.code!==0&&O("failed",o.err.trim()||`could not read the schema of ${n.label}`);let s=o.out.trim();s||O("failed",`${n.label} returned nothing for the schema query`);let a;try{a=JSON.parse(s)}catch{O("failed",`could not read the schema of ${n.label}: the answer was not JSON`)}let{schema:l,enums:i}=Au(a,n.database,Date.now()),u=ba(l,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:i}),d=null;return e.out&&(d=xu(e.out)?e.out:vu(process.cwd(),e.out),_u(d,u,"utf8")),{target:n.label,schemas:t,tables:l.tables.filter(f=>f.kind!=="view").length,views:l.tables.filter(f=>f.kind==="view").length,functions:(l.routines??[]).length,enums:i.length,path:d,types:u}}async function Ru(e,t,n=_){try{let r=await n("cloud-project-sql",{ref:e,sql:t});if(r.error)return{code:1,out:"",err:r.error};let o=Object.values(r.rows?.[0]??{})[0];return{code:0,out:typeof o=="string"?o:o==null?"":JSON.stringify(o),err:""}}catch(r){return{code:1,out:"",err:r instanceof Error?r.message:String(r)}}}function Tu(e){if(!e.ref)throw new Error("no project: pass --ref, --db-url, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return e.ref}async function Sa(e,t){let n=(e??"typescript").toLowerCase();n!=="typescript"&&n!=="ts"&&O("usage",`gen types does not know how to write ${e}. It writes typescript, which is the only language it claims.`);let r=await Pu(t);return r.path&&c(`Wrote ${r.tables} table${r.tables===1?"":"s"}, ${r.views} view${r.views===1?"":"s"}, ${r.functions} function${r.functions===1?"":"s"} and ${r.enums} enum${r.enums===1?"":"s"} to ${r.path}.`),g(r,()=>{r.path||process.stdout.write(r.types)}),0}ee();import{createHash as Zm}from"node:crypto";import{existsSync as qr,mkdirSync as Qm,readFileSync as Kr,readdirSync as ef,statSync as tf,writeFileSync as nf}from"node:fs";import{dirname as rf,isAbsolute as ec,join as vt,resolve as tc}from"node:path";import{pathToFileURL as of}from"node:url";q();function sf(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:ec(e)||e.startsWith("./")||e.startsWith("../")?of(tc(e)).href:e}function af(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(sf(e)):Promise.resolve().then(()=>(Yi(),Vi))}function cf(e){let t=e instanceof Error?e.message:String(e),n=process.env.SNOUTDATA_LOCAL_RUNTIME;return n?`SNOUTDATA_LOCAL_RUNTIME points at ${n}, and it could not be loaded: ${t}`:`this build of the CLI has no local pod runtime in it, so \`snoutdata start\` cannot run. That is a packaging fault rather than anything you did: please report it. (${t})`}var Xi="It is fetched from ghcr.io/snoutdata/snoutpod-postgres on the first `snoutdata start`, so this usually means that registry could not be reached from here.";async function Zi(e,t){try{return await e.localImageFor(t)}catch(n){return n instanceof Error?n.message:String(n)}}async function kt(){let e;try{e=await af()}catch(t){O("tool-missing",cf(t))}return{ready:async t=>{let n=await Zi(e,t);return typeof n=="string"?await e.podmanReady(Xi)??n:e.podmanReady(Xi,n.name)},ensureImage:async(t,n)=>{let r=await Zi(e,t);return typeof r=="string"?r:e.ensureImage(n,void 0,r)},prepare:()=>e.ensureNetwork(),start:(t,n)=>e.startLocal(e.localRuntime(),t,{onProgress:n}),stop:t=>e.stopLocal(e.localRuntime(),t),status:t=>e.localStatus(e.localRuntime(),t),remove:(t,n)=>e.removeLocal(e.localRuntime(),t,n),uri:t=>e.localUri(t),psql:async(t,n)=>{let r=await e.localPsql(t,n);return{code:r.code,out:r.stdout,err:r.stderr}},mint:t=>({ref:e.newLocalRef(),port:t,ownerPassword:e.newLocalPassword(),adminPassword:e.newLocalPassword()})}}function nc(e){return vt(e,".snoutdata","local.json")}function wn(e){let t=nc(e);if(!qr(t))return null;try{let n=JSON.parse(Kr(t,"utf8"));return!n.ref||!n.port||!n.ownerPassword||!n.adminPassword?null:{ref:n.ref,port:n.port,ownerPassword:n.ownerPassword,adminPassword:n.adminPassword}}catch{return null}}function lf(e,t){let n=nc(e);return Qm(rf(n),{recursive:!0}),nf(n,`${JSON.stringify(t,null,2)}
884
- `,{encoding:"utf8",mode:384}),n}var df=54322;async function rc(e,t){let n=await e.ensureImage(t,o=>c(o));n&&O("tool-missing",n);let r=await e.ready(t);r&&O("tool-missing",r),await e.prepare()}function oc(e,t,n,r=yr){let o=!1;return async s=>{if(o)return e.psql(t,s);let a=await r(n,s);return a.code!==127?a:(o=!0,e.psql(t,s))}}async function uf(e,t={}){let n=t.cwd??process.cwd(),r=wn(n);await rc(e,r?.ref??null);let o=r??e.mint(t.port??df),s=r?null:lf(n,o),a=await e.start(o,f=>c(f)),l=e.uri(o);if(t.noMigrations)return{status:a,uri:l,applied:[],seeded:!1,created:s};let i=oc(e,o,sn(l)),u=await mf(i,n,t),d=await ff(i,n,u.length>0||!!s);return{status:a,uri:l,applied:u,seeded:d,created:s}}async function mf(e,t,n){let r=tc(n.dir===void 0?vt(t,"migrations"):ec(n.dir)?n.dir:vt(t,n.dir));if(!qr(r)||!tf(r).isDirectory())return[];let o=ef(r).filter(f=>f.endsWith(".sql")).map(f=>{let y=Kr(vt(r,f),"utf8");return{name:f,sql:y,checksum:Zm("sha256").update(y,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(pf);s.code!==0&&O(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${xt}`);let a=await e(`select name || E'\\t' || checksum from ${xt};`);a.code!==0&&O("failed",a.err.trim()||`could not read ${xt}`);let l=a.out.split(`
885
- `).map(f=>f.trim()).filter(f=>f.length>0).map(f=>{let y=f.indexOf(" ");return{name:f.slice(0,y),checksum:f.slice(y+1)}}),i=rn(o,l,{outOfOrder:n.outOfOrder});i.ok||O("conflict",`Refused. This folder and the local database do not agree:
883
+ `;function Ou(e){return`'{${e.map(n=>`"${n.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(",").replace(/'/g,"''")}}'`}function Nu(e,t,n=0){let r=e.tables.map(a=>{let d=a.primaryKey??[],i=new Set(d),u=a.columns.map(l=>({name:l.name,dataType:l.dataType,nullable:l.nullable,isPrimaryKey:i.has(l.name),default:l.default,autoIncrement:l.autoIncrement,...l.generatedAlways===!0?{generatedAlways:!0}:{}}));return{name:a.name,schema:a.schema,kind:a.kind,columns:u,primaryKey:d,foreignKeys:(a.foreignKeys??[]).map(l=>({constraintName:l.constraintName,columns:l.columns??[],referencedTable:l.referencedTable,referencedColumns:l.referencedColumns??[]})),indexes:(a.indexes??[]).filter(l=>Array.isArray(l.columns)&&l.columns.every(f=>typeof f=="string")).map(l=>({name:l.name,unique:l.unique,columns:l.columns??[]}))}}),o=e.routines.map(a=>({name:a.name,schema:a.schema,kind:a.kind,returnType:a.returnType??void 0,parameters:(a.parameters??[]).map(d=>({name:d.name,dataType:d.dataType,mode:d.mode}))})),s=e.enums.map(a=>({name:a.name,schema:a.schema,values:a.values??[]}));return{schema:{database:t,tables:r,routines:o,fetchedAt:n},enums:s}}async function Du(e){let t=e.schemas&&e.schemas.length>0?[...e.schemas]:["public"],n=e.dbUrl?an(e.dbUrl):Tu(await ne(Cu(e))),r=Ru.replace("$SCHEMAS$",Ou(t)),o=await(e.sql?e.sql(r):br(n,r));o.code===127&&!e.sql&&!e.dbUrl&&e.ref&&(o=await Iu(e.ref,r)),o.code===127&&N("tool-missing",o.err.trim()),o.code!==0&&N("failed",o.err.trim()||`could not read the schema of ${n.label}`);let s=o.out.trim();s||N("failed",`${n.label} returned nothing for the schema query`);let a;try{a=JSON.parse(s)}catch{N("failed",`could not read the schema of ${n.label}: the answer was not JSON`)}let{schema:d,enums:i}=Nu(a,n.database,Date.now()),u=_a(d,{driverId:"postgres",defaultSchema:e.defaultSchema??t[0]??"public",enums:i}),l=null;return e.out&&(l=Pu(e.out)?e.out:Au(process.cwd(),e.out),$u(l,u,"utf8")),{target:n.label,schemas:t,tables:d.tables.filter(f=>f.kind!=="view").length,views:d.tables.filter(f=>f.kind==="view").length,functions:(d.routines??[]).length,enums:i.length,path:l,types:u}}async function Iu(e,t,n=S){try{let r=await n("cloud-project-sql",{ref:e,sql:t});if(r.error)return{code:1,out:"",err:r.error};let o=Object.values(r.rows?.[0]??{})[0];return{code:0,out:typeof o=="string"?o:o==null?"":JSON.stringify(o),err:""}}catch(r){return{code:1,out:"",err:r instanceof Error?r.message:String(r)}}}function Cu(e){if(!e.ref)throw new Error("no project: pass --ref, --db-url, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return e.ref}async function xa(e,t){let n=(e??"typescript").toLowerCase();n!=="typescript"&&n!=="ts"&&N("usage",`gen types does not know how to write ${e}. It writes typescript, which is the only language it claims.`);let r=await Du(t);return r.path&&c(`Wrote ${r.tables} table${r.tables===1?"":"s"}, ${r.views} view${r.views===1?"":"s"}, ${r.functions} function${r.functions===1?"":"s"} and ${r.enums} enum${r.enums===1?"":"s"} to ${r.path}.`),g(r,()=>{r.path||process.stdout.write(r.types)}),0}te();import{createHash as sf}from"node:crypto";import{existsSync as Jr,mkdirSync as af,readFileSync as Wr,readdirSync as cf,statSync as lf,writeFileSync as df}from"node:fs";import{dirname as uf,isAbsolute as oc,join as kt,resolve as sc}from"node:path";import{pathToFileURL as mf}from"node:url";K();function ff(e){return/^[a-z][a-z0-9+.-]+:/i.test(e)?e:oc(e)||e.startsWith("./")||e.startsWith("../")?mf(sc(e)).href:e}function pf(){let e=process.env.SNOUTDATA_LOCAL_RUNTIME;return e?import(ff(e)):Promise.resolve().then(()=>(ec(),Qi))}function gf(e){let t=e instanceof Error?e.message:String(e),n=process.env.SNOUTDATA_LOCAL_RUNTIME;return n?`SNOUTDATA_LOCAL_RUNTIME points at ${n}, and it could not be loaded: ${t}`:`this build of the CLI has no local pod runtime in it, so \`snoutdata start\` cannot run. That is a packaging fault rather than anything you did: please report it. (${t})`}var tc="It is fetched from ghcr.io/snoutdata/snoutpod-postgres on the first `snoutdata start`, so this usually means that registry could not be reached from here.";async function nc(e,t){try{return await e.localImageFor(t)}catch(n){return n instanceof Error?n.message:String(n)}}async function vt(){let e;try{e=await pf()}catch(t){N("tool-missing",gf(t))}return{ready:async t=>{let n=await nc(e,t);return typeof n=="string"?await e.podmanReady(tc)??n:e.podmanReady(tc,n.name)},ensureImage:async(t,n)=>{let r=await nc(e,t);return typeof r=="string"?r:e.ensureImage(n,void 0,r)},prepare:()=>e.ensureNetwork(),start:(t,n)=>e.startLocal(e.localRuntime(),t,{onProgress:n}),stop:t=>e.stopLocal(e.localRuntime(),t),status:t=>e.localStatus(e.localRuntime(),t),remove:(t,n)=>e.removeLocal(e.localRuntime(),t,n),uri:t=>e.localUri(t),psql:async(t,n)=>{let r=await e.localPsql(t,n);return{code:r.code,out:r.stdout,err:r.stderr}},mint:t=>({ref:e.newLocalRef(),port:t,ownerPassword:e.newLocalPassword(),adminPassword:e.newLocalPassword()})}}function ac(e){return kt(e,".snoutdata","local.json")}function bn(e){let t=ac(e);if(!Jr(t))return null;try{let n=JSON.parse(Wr(t,"utf8"));return!n.ref||!n.port||!n.ownerPassword||!n.adminPassword?null:{ref:n.ref,port:n.port,ownerPassword:n.ownerPassword,adminPassword:n.adminPassword}}catch{return null}}function hf(e,t){let n=ac(e);return af(uf(n),{recursive:!0}),df(n,`${JSON.stringify(t,null,2)}
884
+ `,{encoding:"utf8",mode:384}),n}var yf=54322;async function ic(e,t){let n=await e.ensureImage(t,o=>c(o));n&&N("tool-missing",n);let r=await e.ready(t);r&&N("tool-missing",r),await e.prepare()}function cc(e,t,n,r=br){let o=!1;return async s=>{if(o)return e.psql(t,s);let a=await r(n,s);return a.code!==127?a:(o=!0,e.psql(t,s))}}async function wf(e,t={}){let n=t.cwd??process.cwd(),r=bn(n);await ic(e,r?.ref??null);let o=r??e.mint(t.port??yf),s=r?null:hf(n,o),a=await e.start(o,f=>c(f)),d=e.uri(o);if(t.noMigrations)return{status:a,uri:d,applied:[],seeded:!1,created:s};let i=cc(e,o,an(d)),u=await bf(i,n,t),l=await Sf(i,n,u.length>0||!!s);return{status:a,uri:d,applied:u,seeded:l,created:s}}async function bf(e,t,n){let r=sc(n.dir===void 0?kt(t,"migrations"):oc(n.dir)?n.dir:kt(t,n.dir));if(!Jr(r)||!lf(r).isDirectory())return[];let o=cf(r).filter(f=>f.endsWith(".sql")).map(f=>{let w=Wr(kt(r,f),"utf8");return{name:f,sql:w,checksum:sf("sha256").update(w,"utf8").digest("hex")}});if(o.length===0)return[];let s=await e(_f);s.code!==0&&N(s.code===127?"tool-missing":"failed",s.err.trim()||`could not create ${xt}`);let a=await e(`select name || E'\\t' || checksum from ${xt};`);a.code!==0&&N("failed",a.err.trim()||`could not read ${xt}`);let d=a.out.split(`
885
+ `).map(f=>f.trim()).filter(f=>f.length>0).map(f=>{let w=f.indexOf(" ");return{name:f.slice(0,w),checksum:f.slice(w+1)}}),i=on(o,d,{outOfOrder:n.outOfOrder});i.ok||N("conflict",`Refused. This folder and the local database do not agree:
886
886
  ${i.problems.map(f=>` ${f.reason}`).join(`
887
- `)}`,{problems:i.problems});let u=new Map(o.map(f=>[f.name,f])),d=[];for(let f of i.pending){let y=u.get(f.name),m=`insert into ${xt} (name, checksum) values (${Qi(y.name)}, ${Qi(y.checksum)});`,p=ft(y.sql);p||c(`${y.name} runs outside a transaction, so an interruption can leave it half applied.`);let S=p?`begin;
888
- ${y.sql}
889
- ${m}
887
+ `)}`,{problems:i.problems});let u=new Map(o.map(f=>[f.name,f])),l=[];for(let f of i.pending){let w=u.get(f.name),R=`insert into ${xt} (name, checksum) values (${rc(w.name)}, ${rc(w.checksum)});`,m=ft(w.sql);m||c(`${w.name} runs outside a transaction, so an interruption can leave it half applied.`);let p=m?`begin;
888
+ ${w.sql}
889
+ ${R}
890
890
  commit;
891
- `:`${y.sql}
892
- ${m}
893
- `,w=await e(S);w.code!==0&&O("failed",`${y.name} failed, and nothing after it was tried.
894
- ${w.err.trim()}`,{failed:y.name,applied:d}),d.push(y.name),c(` applied ${y.name}`)}return d}async function ff(e,t,n){let r=vt(t,"seed.sql");if(!n||!qr(r))return!1;let o=await e(Kr(r,"utf8"));return o.code!==0&&O("failed",`seed.sql failed.
895
- ${o.err.trim()}`),c(" ran seed.sql"),!0}var xt="_snoutdata_migrations",pf=`create table if not exists ${xt} (
891
+ `:`${w.sql}
892
+ ${R}
893
+ `,h=await e(p);h.code!==0&&N("failed",`${w.name} failed, and nothing after it was tried.
894
+ ${h.err.trim()}`,{failed:w.name,applied:l}),l.push(w.name),c(` applied ${w.name}`)}return l}async function Sf(e,t,n){let r=kt(t,"seed.sql");if(!n||!Jr(r))return!1;let o=await e(Wr(r,"utf8"));return o.code!==0&&N("failed",`seed.sql failed.
895
+ ${o.err.trim()}`),c(" ran seed.sql"),!0}var xt="_snoutdata_migrations",_f=`create table if not exists ${xt} (
896
896
  name text primary key,
897
897
  checksum text not null,
898
898
  applied_at timestamptz not null default now()
899
- );`;function Qi(e){return`'${e.replace(/'/g,"''")}'`}function gf(e){if(e===null)return"unknown";let t=["B","kB","MB","GB"],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n<10&&r>0?n.toFixed(1):Math.round(n)} ${t[r]}`}async function sc(e,t={}){let n=await uf(e,t);return g(n,()=>{n.created&&(c(""),c(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),c(""),c(` ${k("Database")} running on port ${n.status.port}`),n.applied.length>0&&c(` ${k("Migrations")} ${n.applied.length} applied`),n.seeded&&c(` ${k("Seed")} seed.sql ran`),c(""),process.stdout.write(`${n.uri}
900
- `),c(b(" snoutdata gen types typescript --local > database.types.ts")),c(b(" snoutdata stop"))}),0}async function ac(e,t={}){let n=t.cwd??process.cwd(),r=wn(n);r||O("not-found","there is no local database for this folder. `snoutdata start` makes one."),await rc(e,r.ref);let o=await e.stop(r.ref);return g(o,()=>c("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function ic(e,t={}){let n=t.cwd??process.cwd(),r=wn(n);if(!r)return g({exists:!1,ref:null,status:"absent"},()=>c("No local database for this folder. `snoutdata start` makes one.")),0;let o=await e.ready(r.ref);if(o)return g({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>c(o)),0;let s=await e.status(r);return g({...s,uri:s.uri},()=>{c(` ${k("Project")} ${s.ref}`),c(` ${k("State")} ${s.ready?"running":s.status}`),c(` ${k("Port")} ${s.port}`),c(` ${k("Size")} ${gf(s.databaseBytes)}`),s.services.length>0&&c(` ${k("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
901
- `)}),0}async function cc(e,t=process.cwd()){let n=wn(t);n||O("not-found","there is no local database for this folder. `snoutdata start` makes one.");let r=e.uri(n);return{dbUrl:r,sql:oc(e,n,sn(r))}}J();q();Dt();tt();be();import{resolve as lc}from"node:path";function hf(){let e=t=>Ee(t);return{whoami:()=>Re(),listProjects:async()=>({...await re(),local:yf()}),localProject:t=>e(t),createProject:async t=>{let n=await _("cloud-project-create",{name:t.name,...t.region?{region:t.region}:{},...t.teamId?{teamId:t.teamId}:{}});try{return await ct(n.project.ref)}catch{return{...n.project,ready:!1,note:`${n.project.ref} was created and is still starting. It was not ready within the wait. Call list_projects in a few seconds; do not connect to it yet.`}}},usage:(t,n)=>_("cloud-project-usage",{ref:t,...n?{days:n}:{}}),exportStatus:t=>Fe(t),requestExport:t=>at(t),resetPassword:t=>_("cloud-project-reset-password",{ref:t}),listTeams:()=>_("cloud-team-list",{}),deployFunction:(t,n,r)=>{let o=e(t);return o?Bn(o,n,r):Jn(t,n,r).then(s=>s.answer)},listFunctions:async t=>{let n=e(t);return n?Wt(n):zn(t)},deleteFunction:(t,n)=>{let r=e(t);return r?Hn(r,n):Vn(t,n)},sizeFunction:(t,n,r)=>Yn(t,n,r),listFunctionSecrets:async t=>{let n=e(t);return n?zt(n):_("cloud-project-secrets",{ref:t})},listTokens:()=>_("cloud-token-list",{}),createToken:(t,n,r)=>_("cloud-token-create",{name:t,...n?{expiresInDays:n}:{},...r?{project:r}:{}}),revokeToken:t=>_("cloud-token-revoke",{id:t}),getProject:async t=>{let n=e(t);return n?{ref:n.ref,name:n.name,local:n.folder,api:n.apiUrl,database:`127.0.0.1:${n.dbPort}`,functions:Wt(n).functions.map(r=>r.name),secretNames:zt(n).secrets.map(r=>r.name)}:sr(t)},getProducts:t=>He(t),setProduct:(t,n,r)=>tr(t,n,r),listDomains:t=>Zt(t),domainAction:(t,n,r)=>nr(t,n,r),restoreWindow:t=>rr(t),restoreTo:(t,n,r)=>or(t,n,r),setProjectState:async(t,n)=>{let r=await _(`cloud-project-${t}`,{ref:n});try{let o=await it(n,t,{timeoutMs:12e4});return{changed:r.changed,state:o?.state??"deleted",...o?{project:o}:{}}}catch{return{...r,settled:!1,note:`${n} has not finished yet. Call list_projects in a few seconds.`}}},connection:t=>te(t),pushMigrations:(t,n)=>pr(t,{dir:n.dir,dryRun:n.dryRun})}}function yf(){let e=At().filter(n=>n.ref!==null).map(n=>({ref:n.ref,name:n.name,folder:n.folder})),t=Xe(process.cwd())?.local;if(t&&!e.some(n=>lc(n.folder)===lc(t)))try{let n=_e(t);e.push({ref:n.ref,name:n.name,folder:n.folder})}catch{}return e}var dc="app_";async function wf(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await Ot();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await _o(t)).map(o=>({name:`${dc}${o.name}`,description:`[SnoutData Studio, on this machine] ${o.description??o.name}`,inputSchema:o.inputSchema??{type:"object",properties:{}}})),config:t}}catch{return{tools:[],config:null}}}async function uc(e){st(!0);let{tools:t,config:n}=await wf(),r=hf();n&&(r.callBorrowed=(i,u)=>xo(n,i.slice(dc.length),u));let o={...e,borrowed:t};K(t.length>0?`snoutdata mcp: ${Yt.length} cloud tools, and ${t.length} borrowed from SnoutData Studio on this machine.`:"snoutdata mcp: cloud tools only. SnoutData Studio is not answering here, so its database tools are not on this endpoint.");let s="";process.stdin.setEncoding("utf8");try{let i=await Re();K(`snoutdata mcp: signed in as ${i.email??"this account"}.`)}catch(i){return K(`snoutdata mcp: ${i instanceof Error?i.message:String(i)}`),K("Sign in with `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN."),3}K(`snoutdata mcp: ready${e.allowDelete?" (delete_project is ON)":""}. Speaking MCP on stdin/stdout; stop it with Ctrl-C.`);let a=new Set;function l(i){a.add(i),i.finally(()=>a.delete(i))}return new Promise(i=>{process.stdin.on("data",u=>{let d=Ms(s+u);s=d.rest;for(let f of d.messages)l(bf(f,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);i(0)})()}),process.stdin.on("error",()=>i(1))})}async function bf(e,t,n){let r;try{r=JSON.parse(e)}catch{K(`snoutdata mcp: ignoring a line that is not JSON: ${e.slice(0,120)}`);return}try{let o=await Cs(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
899
+ );`;function rc(e){return`'${e.replace(/'/g,"''")}'`}function xf(e){if(e===null)return"unknown";let t=["B","kB","MB","GB"],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r+=1;return`${n<10&&r>0?n.toFixed(1):Math.round(n)} ${t[r]}`}async function lc(e,t={}){let n=await wf(e,t);return g(n,()=>{n.created&&(c(""),c(`Wrote ${n.created}. It holds a password, so put .snoutdata/ in your .gitignore.`)),c(""),c(` ${x("Database")} running on port ${n.status.port}`),n.applied.length>0&&c(` ${x("Migrations")} ${n.applied.length} applied`),n.seeded&&c(` ${x("Seed")} seed.sql ran`),c(""),process.stdout.write(`${n.uri}
900
+ `),c(b(" snoutdata gen types typescript --local > database.types.ts")),c(b(" snoutdata stop"))}),0}async function dc(e,t={}){let n=t.cwd??process.cwd(),r=bn(n);r||N("not-found","there is no local database for this folder. `snoutdata start` makes one."),await ic(e,r.ref);let o=await e.stop(r.ref);return g(o,()=>c("Stopped. The data is still there; `snoutdata start` brings it back.")),0}async function uc(e,t={}){let n=t.cwd??process.cwd(),r=bn(n);if(!r)return g({exists:!1,ref:null,status:"absent"},()=>c("No local database for this folder. `snoutdata start` makes one.")),0;let o=await e.ready(r.ref);if(o)return g({exists:!1,ref:r.ref,status:"unknown",reason:o},()=>c(o)),0;let s=await e.status(r);return g({...s,uri:s.uri},()=>{c(` ${x("Project")} ${s.ref}`),c(` ${x("State")} ${s.ready?"running":s.status}`),c(` ${x("Port")} ${s.port}`),c(` ${x("Size")} ${xf(s.databaseBytes)}`),s.services.length>0&&c(` ${x("Services")} ${s.services.join(", ")}`),s.uri&&process.stdout.write(`${s.uri}
901
+ `)}),0}async function mc(e,t=process.cwd()){let n=bn(t);n||N("not-found","there is no local database for this folder. `snoutdata start` makes one.");let r=e.uri(n);return{dbUrl:r,sql:cc(e,n,an(r))}}z();K();It();tt();ke();import{resolve as fc}from"node:path";function kf(){let e=t=>ye(t);return{whoami:()=>Ne(),listProjects:async()=>({...await oe(),local:vf()}),localProject:t=>e(t),createProject:async t=>{let n=await S("cloud-project-create",{name:t.name,...t.region?{region:t.region}:{},...t.teamId?{teamId:t.teamId}:{}});try{return await ct(n.project.ref)}catch{return{...n.project,ready:!1,note:`${n.project.ref} was created and is still starting. It was not ready within the wait. Call list_projects in a few seconds; do not connect to it yet.`}}},usage:(t,n)=>S("cloud-project-usage",{ref:t,...n?{days:n}:{}}),exportStatus:t=>Ke(t),requestExport:t=>at(t),resetPassword:t=>S("cloud-project-reset-password",{ref:t}),listTeams:()=>S("cloud-team-list",{}),deployFunction:(t,n,r)=>{let o=e(t);return o?Gn(o,n,r):Vn(t,n,r).then(s=>s.answer)},listFunctions:async t=>{let n=e(t);return n?Wt(n):Yn(t)},deleteFunction:(t,n)=>{let r=e(t);return r?qn(r,n):Xn(t,n)},sizeFunction:(t,n,r)=>Zn(t,n,r),listFunctionSecrets:async t=>{let n=e(t);return n?Vt(n):S("cloud-project-secrets",{ref:t})},listTokens:()=>S("cloud-token-list",{}),createToken:(t,n,r)=>S("cloud-token-create",{name:t,...n?{expiresInDays:n}:{},...r?{project:r}:{}}),revokeToken:t=>S("cloud-token-revoke",{id:t}),getProject:async t=>{let n=e(t);return n?{ref:n.ref,name:n.name,local:n.folder,api:n.apiUrl,database:`127.0.0.1:${n.dbPort}`,functions:Wt(n).functions.map(r=>r.name),secretNames:Vt(n).secrets.map(r=>r.name)}:ir(t)},getProducts:t=>We(t),setProduct:(t,n,r)=>rr(t,n,r),listDomains:t=>Qt(t),domainAction:(t,n,r)=>or(t,n,r),restoreWindow:t=>sr(t),restoreTo:(t,n,r)=>ar(t,n,r),setProjectState:async(t,n)=>{let r=await S(`cloud-project-${t}`,{ref:n});try{let o=await it(n,t,{timeoutMs:12e4});return{changed:r.changed,state:o?.state??"deleted",...o?{project:o}:{}}}catch{return{...r,settled:!1,note:`${n} has not finished yet. Call list_projects in a few seconds.`}}},connection:t=>ne(t),pushMigrations:(t,n)=>hr(t,{dir:n.dir,dryRun:n.dryRun})}}function vf(){let e=At().filter(n=>n.ref!==null).map(n=>({ref:n.ref,name:n.name,folder:n.folder})),t=Le(process.cwd())?.local;if(t&&!e.some(n=>fc(n.folder)===fc(t)))try{let n=Ee(t);e.push({ref:n.ref,name:n.name,folder:n.folder})}catch{}return e}var pc="app_";async function Ef(){if(process.env.SNOUTDATA_NO_DESKTOP)return{tools:[],config:null};let e=await Nt();if(!e.available)return{tools:[],config:null};let t=e.config;try{return{tools:(await vo(t)).map(o=>({name:`${pc}${o.name}`,description:`[SnoutData Studio, on this machine] ${o.description??o.name}`,inputSchema:o.inputSchema??{type:"object",properties:{}}})),config:t}}catch{return{tools:[],config:null}}}async function gc(e){st(!0);let{tools:t,config:n}=await Ef(),r=kf();n&&(r.callBorrowed=(i,u)=>Eo(n,i.slice(pc.length),u));let o={...e,borrowed:t};J(t.length>0?`snoutdata mcp: ${Xt.length} cloud tools, and ${t.length} borrowed from SnoutData Studio on this machine.`:"snoutdata mcp: cloud tools only. SnoutData Studio is not answering here, so its database tools are not on this endpoint.");let s="";process.stdin.setEncoding("utf8");try{let i=await Ne();J(`snoutdata mcp: signed in as ${i.email??"this account"}.`)}catch(i){return J(`snoutdata mcp: ${i instanceof Error?i.message:String(i)}`),J("Sign in with `snoutdata login`, or set SNOUTDATA_ACCESS_TOKEN."),3}J(`snoutdata mcp: ready${e.allowDelete?" (delete_project is ON)":""}. Speaking MCP on stdin/stdout; stop it with Ctrl-C.`);let a=new Set;function d(i){a.add(i),i.finally(()=>a.delete(i))}return new Promise(i=>{process.stdin.on("data",u=>{let l=Ls(s+u);s=l.rest;for(let f of l.messages)d($f(f,r,o))}),process.stdin.on("end",()=>{(async()=>{for(;a.size>0;)await Promise.allSettled([...a]);i(0)})()}),process.stdin.on("error",()=>i(1))})}async function $f(e,t,n){let r;try{r=JSON.parse(e)}catch{J(`snoutdata mcp: ignoring a line that is not JSON: ${e.slice(0,120)}`);return}try{let o=await js(r,t,n);o&&process.stdout.write(`${JSON.stringify(o)}
902
902
  `)}catch(o){let s=r.id??null;process.stdout.write(`${JSON.stringify({jsonrpc:"2.0",id:s,error:{code:-32603,message:o instanceof Error?o.message:String(o)}})}
903
- `)}}tt();J();q();function Sf(e){return e>=1?"over":e>=.8?"near":"ok"}function mc(e,t){let n=null,r=null,o=0,s=0;for(let l of e)o+=Number.isFinite(l.computeSeconds)?l.computeSeconds:0,s+=Number.isFinite(l.connections)?l.connections:0,!(l.dbBytes===null||!Number.isFinite(l.dbBytes))&&(r=r===null?l.dbBytes:Math.max(r,l.dbBytes),(!n||l.day>=n.day)&&(n={day:l.day,dbBytes:l.dbBytes,repoBytes:l.repoBytes}));let a=n&&t&&t.maxStorageBytes>0?{limitBytes:t.maxStorageBytes,usedBytes:n.dbBytes,fraction:n.dbBytes/t.maxStorageBytes,state:Sf(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function Wr(e){if(!Number.isFinite(e)||e<=0)return"none";let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?n>0?`${t}d ${n}h`:`${t}d`:n>0?r>0?`${n}h ${r}m`:`${n}h`:r>0?`${r}m`:`${Math.round(e)}s`}async function fc(e,t={}){let n=await _("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=mc(n.days,n.limit);return g({...n,summary:r},()=>{if(c(k(`${n.name} (${n.ref})`)),!r.latest)c("Nothing measured yet. The host samples every fifteen minutes.");else if(r.storage){let o=Math.round(r.storage.fraction*100),s=`${he(r.storage.usedBytes)} of ${he(r.storage.limitBytes)} on ${n.limit?.tier}`;c(r.storage.state==="over"?`${s} (${o}%). Over the limit, so writes are refused until it shrinks or the plan changes.`:r.storage.state==="near"?`${s} (${o}%). Close to the limit: at 100% the database goes read-only.`:`${s} (${o}%).`)}else c(`${he(r.latest.dbBytes)} as of ${r.latest.day}.`);n.readOnly&&r.storage?.state!=="over"&&c("This project is currently read-only."),c(`Over ${r.days} day${r.days===1?"":"s"}: ${Wr(r.computeSeconds)} of compute, ${r.connections} connection${r.connections===1?"":"s"}.`),r.peakDbBytes!==null&&r.latest&&r.peakDbBytes>r.latest.dbBytes&&c(b(`Peak in that window was ${he(r.peakDbBytes)}.`)),t.history&&n.days.length>0&&(c(""),c(F([["DAY","SIZE","BACKUPS","COMPUTE","CONNS"],...n.days.map(o=>[o.day,o.dbBytes===null?"-":he(o.dbBytes),o.repoBytes===null?"-":he(o.repoBytes),Wr(o.computeSeconds),String(o.connections)])])),c(b("A dash is a day nothing measured it, which is what a paused project looks like.")))}),0}ee();q();rt();import{spawn as _f}from"node:child_process";import{createHash as xf}from"node:crypto";import{chmodSync as vf,renameSync as bn,rmSync as pc,writeFileSync as kf}from"node:fs";import{dirname as Ef,join as $f}from"node:path";var Sn=process.env.SNOUTDATA_NPM_REGISTRY??"https://registry.npmjs.org",Af="https://github.com/snoutdata/app/releases/download";function Pf(e,t){let n=t==="x64"||t==="amd64"?"x64":t==="arm64"?"arm64":null;return n?e==="linux"||e==="darwin"?`snoutdata-${e}-${n}`:e==="win32"&&n==="x64"?"snoutdata-windows-x64.exe":null:null}function Rf(e,t){for(let n of e.split(/\r?\n/)){let r=/^([0-9a-f]{64})\s+\*?(.+)$/i.exec(n.trim());if(r&&r[2]===t)return r[1].toLowerCase()}return null}async function Tf(){let e;try{e=await fetch(`${Sn}/snoutdata/latest`,{headers:{accept:"application/json"}})}catch(n){throw new N("network",`could not reach ${Sn} to find the newest version: ${n instanceof Error?n.message:String(n)}`)}if(!e.ok)throw new N("server",`${Sn} answered ${e.status} when asked for the newest version`);let t=await e.json();if(typeof t.version!="string")throw new N("server",`${Sn} did not say which version is newest`);return t.version}async function gc(e){let t;try{t=await fetch(e)}catch(n){throw new N("network",`could not download ${e}: ${n instanceof Error?n.message:String(n)}`)}if(!t.ok)throw new N("failed",`download failed (${t.status}): ${e}`);return Buffer.from(await t.arrayBuffer())}function Of(e,t){let n=$f(Ef(e),`.snoutdata-upgrade-${process.pid}`);kf(n,t,{mode:493});try{if(process.platform==="win32"){let r=`${e}.old`;pc(r,{force:!0}),bn(e,r);try{bn(n,e)}catch(o){throw bn(r,e),o}}else vf(n,493),bn(n,e)}catch(r){pc(n,{force:!0});let o=r.code;throw o==="EACCES"||o==="EPERM"?new N("forbidden",`${e} is not writable by this user. Run the installer instead: ${process.platform==="win32"?me:nt}`):r}}function Nf(){return new Promise((e,t)=>{let n=_f(process.platform==="win32"?"npm.cmd":"npm",["install","-g","snoutdata@latest"],{stdio:["ignore","inherit","inherit"],shell:process.platform==="win32"});n.on("error",r=>t(new N("tool-missing",`npm could not be run (${r.message}). Run it yourself: ${me}`))),n.on("exit",r=>e(r??1))})}async function hc(e={}){let t=e.kind??Ct(),n=await Tf(),r=ko(V,n),o={installed:V,latest:n,upToDate:!r,install:t};if(!r||e.check)return g({...o,upgraded:!1},()=>{c(r?`snoutdata ${V} is installed; ${n} is the newest. \`snoutdata upgrade\` installs it.`:`snoutdata ${V} is the newest version.`)}),0;switch(t){case"npx":return g({...o,upgraded:!1},()=>{c(`This is a copy npx fetched (${V}). Run \`npx snoutdata@latest\` to use ${n}; there is nothing installed to upgrade.`)}),0;case"source":throw new N("usage",`this snoutdata (${V}) is running from source, so there is nothing to replace. Install the published one: ${process.platform==="win32"?me:nt}`);case"npm":{c(`Upgrading snoutdata ${V} to ${n} with npm\u2026`);let s=await Nf();if(s!==0)throw new N("failed",`npm exited ${s}. Run it yourself: ${me}`);return g({...o,upgraded:!0},()=>c(`Upgraded to ${n}.`)),0}case"binary":{let s=Pf(process.platform,process.arch);if(!s)throw new N("failed",`no snoutdata binary is built for ${process.platform}-${process.arch}. Use npm: ${me}`);c(`Downloading snoutdata ${n} for ${process.platform}-${process.arch}\u2026`);let a=`${Af}/cli-v${n}`,[l,i]=await Promise.all([gc(`${a}/${s}`),gc(`${a}/SHA256SUMS`)]),u=Rf(i.toString("utf8"),s);if(!u)throw new N("failed",`${s} is not listed in the release's SHA256SUMS. Refusing to install.`);let d=xf("sha256").update(l).digest("hex");if(d!==u)throw new N("failed",`checksum mismatch for ${s}. Refusing to install.
903
+ `)}}tt();z();K();function Pf(e){return e>=1?"over":e>=.8?"near":"ok"}function hc(e,t){let n=null,r=null,o=0,s=0;for(let d of e)o+=Number.isFinite(d.computeSeconds)?d.computeSeconds:0,s+=Number.isFinite(d.connections)?d.connections:0,!(d.dbBytes===null||!Number.isFinite(d.dbBytes))&&(r=r===null?d.dbBytes:Math.max(r,d.dbBytes),(!n||d.day>=n.day)&&(n={day:d.day,dbBytes:d.dbBytes,repoBytes:d.repoBytes}));let a=n&&t&&t.maxStorageBytes>0?{limitBytes:t.maxStorageBytes,usedBytes:n.dbBytes,fraction:n.dbBytes/t.maxStorageBytes,state:Pf(n.dbBytes/t.maxStorageBytes)}:null;return{days:e.length,latest:n,peakDbBytes:r,computeSeconds:o,connections:s,storage:a}}function zr(e){if(!Number.isFinite(e)||e<=0)return"none";let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60);return t>0?n>0?`${t}d ${n}h`:`${t}d`:n>0?r>0?`${n}h ${r}m`:`${n}h`:r>0?`${r}m`:`${Math.round(e)}s`}async function yc(e,t={}){let n=await S("cloud-project-usage",{ref:e,...t.days?{days:t.days}:{}}),r=hc(n.days,n.limit);return g({...n,summary:r},()=>{if(c(x(`${n.name} (${n.ref})`)),!r.latest)c("Nothing measured yet. The host samples every fifteen minutes.");else if(r.storage){let o=Math.round(r.storage.fraction*100),s=`${be(r.storage.usedBytes)} of ${be(r.storage.limitBytes)} on ${n.limit?.tier}`;c(r.storage.state==="over"?`${s} (${o}%). Over the limit, so writes are refused until it shrinks or the plan changes.`:r.storage.state==="near"?`${s} (${o}%). Close to the limit: at 100% the database goes read-only.`:`${s} (${o}%).`)}else c(`${be(r.latest.dbBytes)} as of ${r.latest.day}.`);n.readOnly&&r.storage?.state!=="over"&&c("This project is currently read-only."),c(`Over ${r.days} day${r.days===1?"":"s"}: ${zr(r.computeSeconds)} of compute, ${r.connections} connection${r.connections===1?"":"s"}.`),r.peakDbBytes!==null&&r.latest&&r.peakDbBytes>r.latest.dbBytes&&c(b(`Peak in that window was ${be(r.peakDbBytes)}.`)),t.history&&n.days.length>0&&(c(""),c(F([["DAY","SIZE","BACKUPS","COMPUTE","CONNS"],...n.days.map(o=>[o.day,o.dbBytes===null?"-":be(o.dbBytes),o.repoBytes===null?"-":be(o.repoBytes),zr(o.computeSeconds),String(o.connections)])])),c(b("A dash is a day nothing measured it, which is what a paused project looks like.")))}),0}te();K();rt();import{spawn as Af}from"node:child_process";import{createHash as Tf}from"node:crypto";import{chmodSync as Rf,renameSync as Sn,rmSync as wc,writeFileSync as Of}from"node:fs";import{dirname as Nf,join as Df}from"node:path";var _n=process.env.SNOUTDATA_NPM_REGISTRY??"https://registry.npmjs.org",If="https://github.com/snoutdata/app/releases/download";function Cf(e,t){let n=t==="x64"||t==="amd64"?"x64":t==="arm64"?"arm64":null;return n?e==="linux"||e==="darwin"?`snoutdata-${e}-${n}`:e==="win32"&&n==="x64"?"snoutdata-windows-x64.exe":null:null}function Mf(e,t){for(let n of e.split(/\r?\n/)){let r=/^([0-9a-f]{64})\s+\*?(.+)$/i.exec(n.trim());if(r&&r[2]===t)return r[1].toLowerCase()}return null}async function jf(){let e;try{e=await fetch(`${_n}/snoutdata/latest`,{headers:{accept:"application/json"}})}catch(n){throw new C("network",`could not reach ${_n} to find the newest version: ${n instanceof Error?n.message:String(n)}`)}if(!e.ok)throw new C("server",`${_n} answered ${e.status} when asked for the newest version`);let t=await e.json();if(typeof t.version!="string")throw new C("server",`${_n} did not say which version is newest`);return t.version}async function bc(e){let t;try{t=await fetch(e)}catch(n){throw new C("network",`could not download ${e}: ${n instanceof Error?n.message:String(n)}`)}if(!t.ok)throw new C("failed",`download failed (${t.status}): ${e}`);return Buffer.from(await t.arrayBuffer())}function Lf(e,t){let n=Df(Nf(e),`.snoutdata-upgrade-${process.pid}`);Of(n,t,{mode:493});try{if(process.platform==="win32"){let r=`${e}.old`;wc(r,{force:!0}),Sn(e,r);try{Sn(n,e)}catch(o){throw Sn(r,e),o}}else Rf(n,493),Sn(n,e)}catch(r){wc(n,{force:!0});let o=r.code;throw o==="EACCES"||o==="EPERM"?new C("forbidden",`${e} is not writable by this user. Run the installer instead: ${process.platform==="win32"?pe:nt}`):r}}function Uf(){return new Promise((e,t)=>{let n=Af(process.platform==="win32"?"npm.cmd":"npm",["install","-g","snoutdata@latest"],{stdio:["ignore","inherit","inherit"],shell:process.platform==="win32"});n.on("error",r=>t(new C("tool-missing",`npm could not be run (${r.message}). Run it yourself: ${pe}`))),n.on("exit",r=>e(r??1))})}async function Sc(e={}){let t=e.kind??Mt(),n=await jf(),r=Po(X,n),o={installed:X,latest:n,upToDate:!r,install:t};if(!r||e.check)return g({...o,upgraded:!1},()=>{c(r?`snoutdata ${X} is installed; ${n} is the newest. \`snoutdata upgrade\` installs it.`:`snoutdata ${X} is the newest version.`)}),0;switch(t){case"npx":return g({...o,upgraded:!1},()=>{c(`This is a copy npx fetched (${X}). Run \`npx snoutdata@latest\` to use ${n}; there is nothing installed to upgrade.`)}),0;case"source":throw new C("usage",`this snoutdata (${X}) is running from source, so there is nothing to replace. Install the published one: ${process.platform==="win32"?pe:nt}`);case"npm":{c(`Upgrading snoutdata ${X} to ${n} with npm\u2026`);let s=await Uf();if(s!==0)throw new C("failed",`npm exited ${s}. Run it yourself: ${pe}`);return g({...o,upgraded:!0},()=>c(`Upgraded to ${n}.`)),0}case"binary":{let s=Cf(process.platform,process.arch);if(!s)throw new C("failed",`no snoutdata binary is built for ${process.platform}-${process.arch}. Use npm: ${pe}`);c(`Downloading snoutdata ${n} for ${process.platform}-${process.arch}\u2026`);let a=`${If}/cli-v${n}`,[d,i]=await Promise.all([bc(`${a}/${s}`),bc(`${a}/SHA256SUMS`)]),u=Mf(i.toString("utf8"),s);if(!u)throw new C("failed",`${s} is not listed in the release's SHA256SUMS. Refusing to install.`);let l=Tf("sha256").update(d).digest("hex");if(l!==u)throw new C("failed",`checksum mismatch for ${s}. Refusing to install.
904
904
  expected ${u}
905
- actual ${d}`);return Of(process.execPath,l),g({...o,upgraded:!0,path:process.execPath},()=>c(`Upgraded ${process.execPath} to ${n}.`)),0}}}Ht();be();rt();var yc=`snoutdata ${V} \u2014 hosted Postgres, from a terminal or an agent
905
+ actual ${l}`);return Lf(process.execPath,d),g({...o,upgraded:!0,path:process.execPath},()=>c(`Upgraded ${process.execPath} to ${n}.`)),0}}}Gt();ke();rt();var _c=`snoutdata ${X} \u2014 hosted Postgres, from a terminal or an agent
906
906
 
907
907
  snoutdata init [--name X] [--env] a database for this folder, linked, ready to use;
908
908
  --env writes DATABASE_URL into ./.env (and adds
@@ -1003,7 +1003,8 @@ Every command takes --json, --quiet and --help, and anything that waits takes --
1003
1003
  SECONDS. A failure in --json mode is {"ok":false,"code","error"} on stdout, and the exit
1004
1004
  code says the same thing more coarsely: 2 the command was wrong, 3 the credential, 4 not
1005
1005
  ready yet, 5 forbidden, 6 not found, 7 conflict, 8 quota, 9 network, 10 timed out, 11 this
1006
- CLI is out of date ("code":"outdated", run "snoutdata upgrade"). "<command> --help"
1006
+ CLI is out of date ("code":"outdated", run "snoutdata upgrade"), 12 the region is full and
1007
+ nothing was created ("code":"no-capacity", do not retry in a loop). "<command> --help"
1007
1008
  explains each of a command's flags.
1008
1009
 
1009
1010
  With no credential and a person present, sign-in is offered: SnoutData Studio if it is
@@ -1022,11 +1023,11 @@ that re-signs Postgres traffic.
1022
1023
  Docs: https://docs.snoutdata.com/developers/cli
1023
1024
  Examples: https://github.com/snoutdata/snoutdata (a star helps other people find it)
1024
1025
  Source: https://github.com/snoutdata/snout-cli
1025
- `;async function Df(e){let[t,n,...r]=e.command;if(G(e,"version")||t==="version")return g({version:V},()=>process.stdout.write(`${V}
1026
- `)),0;if(G(e,"help")&&t&&t!=="help"){let i=_n.filter(u=>u.name===t||u.name.startsWith(`${t} `));if(i.length>0)return g({commands:i.map(u=>({...u,flagHelp:Object.fromEntries(u.flags.map(d=>[d,xn(u.name,d)]))}))},()=>{for(let u of i){let d=u.flags.length>0?` ${u.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${u.name}${d}
1026
+ `;async function Ff(e){let[t,n,...r]=e.command;if(q(e,"version")||t==="version")return g({version:X},()=>process.stdout.write(`${X}
1027
+ `)),0;if(q(e,"help")&&t&&t!=="help"){let i=xn.filter(u=>u.name===t||u.name.startsWith(`${t} `));if(i.length>0)return g({commands:i.map(u=>({...u,flagHelp:Object.fromEntries(u.flags.map(l=>[l,kn(u.name,l)]))}))},()=>{for(let u of i){let l=u.flags.length>0?` ${u.flags.join(" ")}`:"";process.stdout.write(` snoutdata ${u.name}${l}
1027
1028
  ${u.summary}
1028
- `);let f=Math.max(0,...u.flags.map(y=>y.length));for(let y of u.flags)process.stdout.write(` ${y.padEnd(f)} ${xn(u.name,y)}
1029
+ `);let f=Math.max(0,...u.flags.map(w=>w.length));for(let w of u.flags)process.stdout.write(` ${w.padEnd(f)} ${kn(u.name,w)}
1029
1030
  `)}process.stdout.write(`
1030
1031
  Every command also takes --json, --quiet and --help.
1031
- `)}),0}let o=G(e,"help")||t==="help";if(o||!t)return g({version:V,usage:yc.trim(),commands:_n},()=>process.stdout.write(yc)),o?0:ue.usage;let s=()=>{let i=Pe(e,"timeout");if(i!==void 0){if(!(i>0))throw new h("--timeout is a number of seconds greater than zero");return i*1e3}},a=()=>{let i=Qe({flag:A(e,"ref")});if(!i)throw new h("no project: pass --ref, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return i},l=()=>{let i=Qe({flag:A(e,"ref")});return i?Ee(i):null};switch(t){case"login":{if(G(e,"device")){let f=await Mt({name:Nt(),platform:process.platform});g({verifyUrl:f.verifyUrl,code:f.code,expiresAt:f.expiresAt},()=>{c(""),c(` Open ${f.verifyUrl}`),c(` Type ${f.code}`),c(""),c(b("Waiting for that to be approved\u2026"))});let y=await jt(f.secret,{intervalMs:Math.max(1,f.interval)*1e3,expiresAt:f.expiresAt});ie({accessToken:y.token});let m=A(e,"email"),p=m?(await Re()).email??void 0:void 0;return m&&jn(m,{accessToken:y.token,email:p}),g({ok:!0,name:y.name??null,email:p??null},()=>c(`Signed in${p?` as ${p}`:""}.`)),0}let i=A(e,"domain"),u=G(e,"sso")||i!==void 0;if(u&&A(e,"provider"))throw new h("--sso and --provider are two different doors: pass one or the other");let d=await Bt({provider:A(e,"provider"),sso:u,domain:i,noBrowser:G(e,"no-browser")||!xe(),timeoutMs:s(),email:A(e,"email")});return g({email:d.email??null},()=>c(`Signed in${d.email?` as ${d.email}`:""}.`)),0}case"logout":{ro();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return g({ok:!0,environmentTokenStillSet:i},()=>{c("Signed out."),i&&(c("SNOUTDATA_ACCESS_TOKEN is still set in this shell, so commands will keep working."),c(b(" unset SNOUTDATA_ACCESS_TOKEN")))}),0}case"whoami":{let i=await Re(),u=$t()?.expectedEmail,d=!!(u&&i.email&&!Ln(u,i.email));return d&&K(`This session is ${i.email}, and login asked for ${u}. Run "snoutdata login --email ${u}" to sign in as that account.`),g({...i,...u?{expectedEmail:u,matchesExpected:!d}:{}},()=>{process.stdout.write(`${i.email??i.id}
1032
- `),i.token&&c(`via access token ${i.token.prefix} (${i.token.name})${i.token.project?`, limited to project ${i.token.project}`:""}.`)}),0}case"tokens":switch(n){case void 0:case"list":return await ms(),0;case"create":{let i=A(e,"name")??r[0];if(!i)throw new h("tokens create needs --name (what is it for, and where does it live)");return await us({name:i,expiresInDays:Pe(e,"expires"),project:A(e,"project")}),0}case"revoke":{let i=r[0];if(!i)throw new h("tokens revoke needs the token's id or its sdt_ prefix");return await fs(i),0}default:throw new h(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await as(),0;case"create":{let i=A(e,"name")??r[0];if(!i)throw new h("projects create needs --name");let u=A(e,"team");return await qn({teamId:u?await ds(u):void 0,name:i,region:A(e,"region"),wait:!G(e,"no-wait"),timeoutMs:s(),link:void 0,showUrl:G(e,"show-url")}),0}case"pause":case"resume":case"delete":return await is(n,A(e,"ref")??r[0]??a(),{wait:!G(e,"no-wait"),timeoutMs:s()}),0;case"show":{let i=A(e,"ref")??r[0]??a(),u=Ee(i);u?qo(u):await Gs(i)}return 0;default:throw new h(`unknown command: projects ${n}`)}case"functions":{let i=l();if(i){if(n==="deploy"){if(!r[0])throw new h("functions deploy needs a name: snoutdata functions deploy <name>");return await Qo(i,r[0],{dir:A(e,"dir"),verifyJwt:!G(e,"no-verify-jwt")}),0}if(n===void 0||n==="list")return es(i),0;if((n==="delete"||n==="remove")&&r[0])return await ts(i,r[0]),0}switch(n){case"deploy":{let u=r[0];if(!u)throw new h("functions deploy needs a name: snoutdata functions deploy <name>");return await bs(a(),u,{dir:A(e,"dir"),entrypoint:A(e,"entrypoint"),verifyJwt:!G(e,"no-verify-jwt")}),0}case void 0:case"list":return await xs(a()),0;case"size":{let u=r[0];if(!u)throw new h("functions size needs a name: snoutdata functions size <name> --memory MB --concurrency N");return await _s(a(),u,{memoryMb:Pe(e,"memory"),concurrency:Pe(e,"concurrency"),reset:G(e,"reset")}),0}case"delete":case"remove":{let u=r[0];if(!u)throw new h("functions delete needs a name");return await vs(a(),u),0}default:throw new h(`unknown command: functions ${n}`)}}case"secrets":{let i=l();if(i){if(n==="set")return await rs(i,await $s(r,{stdin:G(e,"stdin")})),0;if(n===void 0||n==="list")return os(i),0;if((n==="unset"||n==="remove")&&r[0])return await ss(i,r[0]),0}switch(n){case"set":return await As(a(),r,{stdin:G(e,"stdin")}),0;case void 0:case"list":return await Rs(a()),0;case"unset":case"remove":{let u=r[0];if(!u)throw new h("secrets unset needs a name");return await Ps(a(),u),0}default:throw new h(`unknown command: secrets ${n}`)}}case"keys":switch(n){case void 0:case"show":case"list":return await ps(a()),0;case"rotate":return await gs(a(),{force:G(e,"force")}),0;default:throw new h(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await Ls(a()),0;if(n==="enable"||n==="disable")return await Us(a(),js(r[0]),n==="enable"),0;throw new h(`unknown command: products ${n}. products [enable|disable ${Xt.join("|")}]`)}case"push":{if(n!=="credentials")throw new h("push credentials [set apns|fcm | remove apns|fcm]");let i=r[0];if(i===void 0||i==="list")return await ga(a()),0;if(i==="set"){let u=gr(r[1]),d=u==="apns"?await fa({p8:A(e,"p8"),keyId:A(e,"key-id"),teamId:A(e,"team-id"),topic:A(e,"topic"),environment:A(e,"environment")}):await pa(A(e,"file"));return await ha(a(),u,d),0}if(i==="remove"||i==="unset")return await ya(a(),gr(r[1])),0;throw new h(`unknown command: push credentials ${i}`)}case"auth":return await ca(a(),n,r,{clientId:A(e,"client-id"),stdin:G(e,"stdin"),siteUrl:A(e,"site-url"),allow:A(e,"allow"),file:A(e,"file"),subject:A(e,"subject")}),0;case"realtime":{if(n===void 0||n==="inspect")return ra(a(),{channel:A(e,"channel"),watch:G(e,"watch")});if(n==="logs")return oa(a(),{channel:A(e,"channel"),since:A(e,"since")});throw new h(`unknown command: realtime ${n}. realtime inspect|logs`)}case"domains":return await Fs(a(),n,r[0]),0;case"teams":return await ls(),0;case"link":{if(G(e,"local")){let d;try{d=io(n)}catch(y){throw new N("not-found",y instanceof Error?y.message:String(y))}let f=Ze(process.cwd(),{ref:d.ref,...d.name?{name:d.name}:{},local:d.folder});return g({ref:d.ref,local:d.folder,api:d.apiUrl,path:f},()=>{c(`Linked ${d.name??d.ref}, the local project in ${d.folder} (${f}).`),c(`Its API is ${d.apiUrl}; its database is on 127.0.0.1:${d.dbPort}.`)}),0}let i=A(e,"ref")??n;if(!i)throw new h("link needs --ref");let u=Ze(process.cwd(),{ref:i});return g({ref:i,path:u},()=>c(`Linked ${i} (${u}).`)),0}case"db":switch(n){case"url":return await lr(a()),0;case"psql":return Ys(a(),r);case"reset-password":return await cs(a()),0;case"access":return await ea(a(),r,{level:A(e,"level")}),0;case"push":return ua(a(),{dir:A(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return G(e,"window")?(await Bs(a()),0):A(e,"at")?(await Hs(a(),A(e,"at"),A(e,"name")),0):Qs(a(),{file:A(e,"file"),force:!!e.flags.force});case"export":return Zs(a(),{timeoutMs:s(),out:A(e,"out"),statusOnly:!!e.flags.status});default:throw new h(n?`unknown command: db ${n}`:"db needs an action: url, psql, reset-password, access, export, push, restore")}case"gen":{if(n!=="types")throw new h(n?`unknown command: gen ${n}`:"gen needs an action: types");let i=G(e,"local")?await cc(await kt()):null,u=i?i.dbUrl:A(e,"db-url");return Sa(r[0],{ref:u?void 0:a(),dbUrl:u,schemas:(A(e,"schema")??"public").split(",").map(d=>d.trim()).filter(Boolean),out:A(e,"out"),sql:i?.sql})}case"start":{let i=l();if(i)return Ho(i)}return sc(await kt(),{port:Pe(e,"port"),dir:A(e,"dir"),noMigrations:G(e,"no-migrations"),outOfOrder:G(e,"out-of-order")});case"stop":{let i=l();return i?Go(i):ac(await kt())}case"status":{let i=l();return i?Bo(i):ic(await kt())}case"usage":return fc(a(),{days:Pe(e,"days"),history:!!e.flags.history});case"mcp":return uc({version:V,allowDelete:!!e.flags["allow-delete"]});case"init":return If(e);case"upgrade":return hc({check:G(e,"check")});default:throw new h(`unknown command: ${t}`)}}async function If(e){let t=Qe({flag:A(e,"ref")});if(t)return c(`This folder is already linked to ${t}.`),G(e,"env")?(await wc(t),0):(await lr(t),0);let n=A(e,"name")??Cf(process.cwd());c(`Creating a project called "${n}"\u2026`),await qn({name:n,region:A(e,"region"),wait:!0,link:process.cwd(),showUrl:!G(e,"env")});let r=Qe({cwd:process.cwd()});return r&&G(e,"env")&&await wc(r),0}async function wc(e){let{connection:t}=await Promise.resolve().then(()=>(J(),ge)),n=await t(e),r=Xs(process.cwd(),n.uri);c(`${r.replaced?"Updated":"Wrote"} DATABASE_URL in ${r.path}.`),r.ignored&&c("Added .env to .gitignore, since it holds the password.")}function Cf(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function Mf(e){if(e instanceof N)return ve(e.code,e.message,e.details),e.exitCode;if(e instanceof h)return ve("usage",e.message),ue.usage;if(e instanceof ke)return ve("not-signed-in",e.message),ue["not-signed-in"];if(e instanceof le){let r=Rt(e.status);return ve(r,e.message,{status:e.status}),ue[r]}let t=uo(e);if(t)return ve("usage",t),ue.usage;let n=mo(e);return ve(n,e instanceof Error?e.message:String(e)),ue[n]}function Jr(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function jf(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function Lf(){st(jf(process.argv.slice(2)));let e;try{e=Yr(process.argv.slice(2))}catch(t){ve("usage",String(t instanceof Error?t.message:t)),Jr(ue.usage);return}st(e.json),Ao(e.flags.quiet===!0),fo(Tt({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{Jr(await Df(e))}catch(t){Jr(Mf(t))}}Lf();
1032
+ `)}),0}let o=q(e,"help")||t==="help";if(o||!t)return g({version:X,usage:_c.trim(),commands:xn},()=>process.stdout.write(_c)),o?0:fe.usage;let s=()=>{let i=Oe(e,"timeout");if(i!==void 0){if(!(i>0))throw new y("--timeout is a number of seconds greater than zero");return i*1e3}},a=()=>{let i=Pt({flag:P(e,"ref")});if(!i)throw new y("no project: pass --ref, set SNOUTDATA_PROJECT, or run `snoutdata link --ref <ref>`");return i},d=()=>{let i=Pt({flag:P(e,"ref")});return i?ye(i):null};switch(t){case"login":{if(q(e,"device")){let f=await jt({name:Dt(),platform:process.platform});g({verifyUrl:f.verifyUrl,code:f.code,expiresAt:f.expiresAt},()=>{c(""),c(` Open ${f.verifyUrl}`),c(` Type ${f.code}`),c(""),c(b("Waiting for that to be approved\u2026"))});let w=await Lt(f.secret,{intervalMs:Math.max(1,f.interval)*1e3,expiresAt:f.expiresAt});le({accessToken:w.token});let R=P(e,"email"),m=R?(await Ne()).email??void 0:void 0;return R&&Un(R,{accessToken:w.token,email:m}),g({ok:!0,name:w.name??null,email:m??null},()=>c(`Signed in${m?` as ${m}`:""}.`)),0}let i=P(e,"domain"),u=q(e,"sso")||i!==void 0;if(u&&P(e,"provider"))throw new y("--sso and --provider are two different doors: pass one or the other");let l=await Ht({provider:P(e,"provider"),sso:u,domain:i,noBrowser:q(e,"no-browser")||!$e(),timeoutMs:s(),email:P(e,"email")});return g({email:l.email??null},()=>c(`Signed in${l.email?` as ${l.email}`:""}.`)),0}case"logout":{so();let i=!!process.env.SNOUTDATA_ACCESS_TOKEN;return g({ok:!0,environmentTokenStillSet:i},()=>{c("Signed out."),i&&(c("SNOUTDATA_ACCESS_TOKEN is still set in this shell, so commands will keep working."),c(b(" unset SNOUTDATA_ACCESS_TOKEN")))}),0}case"whoami":{let i=await Ne(),u=$t()?.expectedEmail,l=!!(u&&i.email&&!Fn(u,i.email));return l&&J(`This session is ${i.email}, and login asked for ${u}. Run "snoutdata login --email ${u}" to sign in as that account.`),g({...i,...u?{expectedEmail:u,matchesExpected:!l}:{}},()=>{process.stdout.write(`${i.email??i.id}
1033
+ `),i.token&&c(`via access token ${i.token.prefix} (${i.token.name})${i.token.project?`, limited to project ${i.token.project}`:""}.`)}),0}case"tokens":switch(n){case void 0:case"list":return await ps(),0;case"create":{let i=P(e,"name")??r[0];if(!i)throw new y("tokens create needs --name (what is it for, and where does it live)");return await fs({name:i,expiresInDays:Oe(e,"expires"),project:P(e,"project")}),0}case"revoke":{let i=r[0];if(!i)throw new y("tokens revoke needs the token's id or its sdt_ prefix");return await gs(i),0}default:throw new y(`unknown command: tokens ${n}`)}case"projects":switch(n){case void 0:case"list":return await cs(),0;case"create":{let i=P(e,"name")??r[0];if(!i)throw new y("projects create needs --name");let u=P(e,"team");return await Jn({teamId:u?await ms(u):void 0,name:i,region:P(e,"region"),wait:!q(e,"no-wait"),timeoutMs:s(),link:void 0,showUrl:q(e,"show-url")}),0}case"pause":case"resume":case"delete":return await ls(n,P(e,"ref")??r[0]??a(),{wait:!q(e,"no-wait"),timeoutMs:s()}),0;case"show":{let i=P(e,"ref")??r[0]??a(),u=ye(i);u?Jo(u):await Ks(i)}return 0;default:throw new y(`unknown command: projects ${n}`)}case"functions":{let i=d();if(i){if(n==="deploy"){if(!r[0])throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await ts(i,r[0],{dir:P(e,"dir"),verifyJwt:!q(e,"no-verify-jwt")}),0}if(n===void 0||n==="list")return ns(i),0;if((n==="delete"||n==="remove")&&r[0])return await rs(i,r[0]),0}switch(n){case"deploy":{let u=r[0];if(!u)throw new y("functions deploy needs a name: snoutdata functions deploy <name>");return await _s(a(),u,{dir:P(e,"dir"),entrypoint:P(e,"entrypoint"),verifyJwt:!q(e,"no-verify-jwt")}),0}case void 0:case"list":return await vs(a()),0;case"size":{let u=r[0];if(!u)throw new y("functions size needs a name: snoutdata functions size <name> --memory MB --concurrency N");return await ks(a(),u,{memoryMb:Oe(e,"memory"),concurrency:Oe(e,"concurrency"),reset:q(e,"reset")}),0}case"delete":case"remove":{let u=r[0];if(!u)throw new y("functions delete needs a name");return await Es(a(),u),0}default:throw new y(`unknown command: functions ${n}`)}}case"secrets":{let i=d();if(i){if(n==="set")return await ss(i,await As(r,{stdin:q(e,"stdin")})),0;if(n===void 0||n==="list")return as(i),0;if((n==="unset"||n==="remove")&&r[0])return await is(i,r[0]),0}switch(n){case"set":return await Ts(a(),r,{stdin:q(e,"stdin")}),0;case void 0:case"list":return await Os(a()),0;case"unset":case"remove":{let u=r[0];if(!u)throw new y("secrets unset needs a name");return await Rs(a(),u),0}default:throw new y(`unknown command: secrets ${n}`)}}case"keys":switch(n){case void 0:case"show":case"list":return await hs(a()),0;case"rotate":return await ys(a(),{force:q(e,"force")}),0;default:throw new y(`unknown command: keys ${n}`)}case"products":{if(n===void 0||n==="list")return await Fs(a()),0;if(n==="enable"||n==="disable")return await Bs(a(),Us(r[0]),n==="enable"),0;throw new y(`unknown command: products ${n}. products [enable|disable ${Zt.join("|")}]`)}case"push":{if(n!=="credentials")throw new y("push credentials [set apns|fcm | remove apns|fcm]");let i=r[0];if(i===void 0||i==="list")return await ya(a()),0;if(i==="set"){let u=yr(r[1]),l=u==="apns"?await ga({p8:P(e,"p8"),keyId:P(e,"key-id"),teamId:P(e,"team-id"),topic:P(e,"topic"),environment:P(e,"environment")}):await ha(P(e,"file"));return await wa(a(),u,l),0}if(i==="remove"||i==="unset")return await ba(a(),yr(r[1])),0;throw new y(`unknown command: push credentials ${i}`)}case"auth":return await da(a(),n,r,{clientId:P(e,"client-id"),stdin:q(e,"stdin"),siteUrl:P(e,"site-url"),allow:P(e,"allow"),file:P(e,"file"),subject:P(e,"subject")}),0;case"realtime":{if(n===void 0||n==="inspect")return sa(a(),{channel:P(e,"channel"),watch:q(e,"watch")});if(n==="logs")return aa(a(),{channel:P(e,"channel"),since:P(e,"since")});throw new y(`unknown command: realtime ${n}. realtime inspect|logs`)}case"domains":return await Hs(a(),n,r[0]),0;case"teams":return await us(),0;case"link":{if(q(e,"local")){let l;try{l=uo(n)}catch(w){throw new C("not-found",w instanceof Error?w.message:String(w))}let f=Ue(process.cwd(),{ref:l.ref,...l.name?{name:l.name}:{},local:l.folder});return g({ref:l.ref,local:l.folder,api:l.apiUrl,path:f},()=>{c(`Linked ${l.name??l.ref}, the local project in ${l.folder} (${f}).`),c(`Its API is ${l.apiUrl}; its database is on 127.0.0.1:${l.dbPort}.`)}),0}let i=P(e,"ref")??n;if(!i)throw new y("link needs --ref");let u=Ue(process.cwd(),{ref:i});return g({ref:i,path:u},()=>c(`Linked ${i} (${u}).`)),0}case"db":switch(n){case"url":return await ur(a()),0;case"psql":return Zs(a(),r);case"reset-password":return await ds(a()),0;case"access":return await na(a(),r,{level:P(e,"level")}),0;case"push":return fa(a(),{dir:P(e,"dir"),dryRun:!!e.flags["dry-run"],outOfOrder:!!e.flags["out-of-order"]});case"restore":return q(e,"window")?(await Gs(a()),0):P(e,"at")?(await qs(a(),P(e,"at"),P(e,"name")),0):ta(a(),{file:P(e,"file"),force:!!e.flags.force});case"export":return ea(a(),{timeoutMs:s(),out:P(e,"out"),statusOnly:!!e.flags.status});default:throw new y(n?`unknown command: db ${n}`:"db needs an action: url, psql, reset-password, access, export, push, restore")}case"gen":{if(n!=="types")throw new y(n?`unknown command: gen ${n}`:"gen needs an action: types");let i=q(e,"local")?await mc(await vt()):null,u=i?i.dbUrl:P(e,"db-url");return xa(r[0],{ref:u?void 0:a(),dbUrl:u,schemas:(P(e,"schema")??"public").split(",").map(l=>l.trim()).filter(Boolean),out:P(e,"out"),sql:i?.sql})}case"start":{let i=d();if(i)return qo(i)}return lc(await vt(),{port:Oe(e,"port"),dir:P(e,"dir"),noMigrations:q(e,"no-migrations"),outOfOrder:q(e,"out-of-order")});case"stop":{let i=d();return i?Ko(i):dc(await vt())}case"status":{let i=d();return i?Go(i):uc(await vt())}case"usage":return yc(a(),{days:Oe(e,"days"),history:!!e.flags.history});case"mcp":return gc({version:X,allowDelete:!!e.flags["allow-delete"]});case"init":return Bf(e);case"upgrade":return Sc({check:q(e,"check")});default:throw new y(`unknown command: ${t}`)}}async function Bf(e){let t=co({flag:P(e,"ref")});if(t.kind!=="create"){let o=t.ref;if(t.kind==="linked"?c(`This folder is already linked to ${o}.`):t.kind==="environment"&&c(`Using ${o}, from SNOUTDATA_PROJECT. This folder is not linked to it.`),q(e,"env")?await xc(o):await ur(o),t.kind==="link"){let s=ye(o),a=Ue(process.cwd(),s?{ref:o,...s.name?{name:s.name}:{},local:s.folder}:{ref:o});c(`Linked this folder to ${o}${t.replaces?`, in place of ${t.replaces}`:""} (${a}).`)}return 0}let n=P(e,"name")??Hf(process.cwd());c(`Creating a project called "${n}"\u2026`),await Jn({name:n,region:P(e,"region"),wait:!0,link:process.cwd(),showUrl:!q(e,"env")});let r=Pt({cwd:process.cwd()});return r&&q(e,"env")&&await xc(r),0}async function xc(e){let{connection:t}=await Promise.resolve().then(()=>(z(),we)),n=await t(e),r=Qs(process.cwd(),n.uri);c(`${r.replaced?"Updated":"Wrote"} DATABASE_URL in ${r.path}.`),r.ignored&&c("Added .env to .gitignore, since it holds the password.")}function Hf(e){let t=e.split(/[\\/]/).filter(Boolean);return t[t.length-1]??"project"}function Gf(e){if(e instanceof C)return Pe(e.code,e.message,e.details),e.exitCode;if(e instanceof y)return Pe("usage",e.message),fe.usage;if(e instanceof Ae)return Pe("not-signed-in",e.message),fe["not-signed-in"];if(e instanceof ue){let r=Rt(e.status,e.serverCode);return Pe(r,e.message,{status:e.status,...e.serverCode?{serverCode:e.serverCode}:{}}),fe[r]}let t=po(e);if(t)return Pe("usage",t),fe.usage;let n=go(e);return Pe(n,e instanceof Error?e.message:String(e)),fe[n]}function Vr(e){process.exitCode=e,setTimeout(()=>process.exit(e),2e3).unref()}function qf(e){let t=e.indexOf("--");return(t===-1?e:e.slice(0,t)).includes("--json")}async function Kf(){st(qf(process.argv.slice(2)));let e;try{e=Zr(process.argv.slice(2))}catch(t){Pe("usage",String(t instanceof Error?t.message:t)),Vr(fe.usage);return}st(e.json),Ro(e.flags.quiet===!0),ho(Ot({isTty:!!process.stdin.isTTY,json:e.json,env:process.env}));try{Vr(await Ff(e))}catch(t){Vr(Gf(t))}}Kf();