UNPKG

@prisma/dev

Version:

A local Prisma Postgres server for development and testing

4 lines (3 loc) • 12.6 kB
"use strict";var de=Object.create;var D=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var fe=Object.getPrototypeOf,he=Object.prototype.hasOwnProperty;var me=(t,e)=>{for(var r in e)D(t,r,{get:e[r],enumerable:!0})},W=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of le(e))!he.call(t,a)&&a!==r&&D(t,a,{get:()=>e[a],enumerable:!(n=pe(e,a))||n.enumerable});return t};var O=(t,e,r)=>(r=t!=null?de(fe(t)):{},W(e||!t||!t.__esModule?D(r,"default",{value:t,enumerable:!0}):r,t)),Pe=t=>W(D({},"__esModule",{value:!0}),t);var $e={};me($e,{ServerAlreadyRunningError:()=>M,ServerState:()=>b,ServerStateAlreadyExistsError:()=>j});module.exports=Pe($e);var ue=require("fs/promises"),y=require("pathe"),L=require("proper-lockfile"),K=require("std-env"),o=require("valibot");var X=require("fs"),l=require("fs/promises"),z=require("util"),Y=require("zlib");var u=O(require("path"),1),B=O(require("os"),1),x=O(require("process"),1),h=B.default.homedir(),N=B.default.tmpdir(),{env:v}=x.default,be=t=>{let e=u.default.join(h,"Library");return{data:u.default.join(e,"Application Support",t),config:u.default.join(e,"Preferences",t),cache:u.default.join(e,"Caches",t),log:u.default.join(e,"Logs",t),temp:u.default.join(N,t)}},ge=t=>{let e=v.APPDATA||u.default.join(h,"AppData","Roaming"),r=v.LOCALAPPDATA||u.default.join(h,"AppData","Local");return{data:u.default.join(r,t,"Data"),config:u.default.join(e,t,"Config"),cache:u.default.join(r,t,"Cache"),log:u.default.join(r,t,"Log"),temp:u.default.join(N,t)}},ye=t=>{let e=u.default.basename(h);return{data:u.default.join(v.XDG_DATA_HOME||u.default.join(h,".local","share"),t),config:u.default.join(v.XDG_CONFIG_HOME||u.default.join(h,".config"),t),cache:u.default.join(v.XDG_CACHE_HOME||u.default.join(h,".cache"),t),log:u.default.join(v.XDG_STATE_HOME||u.default.join(h,".local","state"),t),temp:u.default.join(N,e,t)}};function F(t,{suffix:e="nodejs"}={}){if(typeof t!="string")throw new TypeError(`Expected a string, got ${typeof t}`);return e&&(t+=`-${e}`),x.default.platform==="darwin"?be(t):x.default.platform==="win32"?ge(t):ye(t)}var ve=F("prisma-dev"),je=(0,z.promisify)(Y.unzip);function S(t){return`${ve.data}/${t}`}function Z(t){return t!=null&&typeof t=="object"&&"code"in t&&t.code==="ENOENT"}async function Q(t){try{return await(0,l.readFile)(t,{encoding:"utf-8"})}catch(e){if(Z(e))return null;throw e}}async function ee(t){await(0,l.mkdir)(t,{recursive:!0})}async function te(t){try{return(await(0,l.readdir)(t,{withFileTypes:!0})).reduce((r,n)=>(n.isDirectory()&&!n.name.startsWith(".")&&r.push(n.name),r),[])}catch(e){if(Z(e))return[];throw e}}var re=require("std-env");function se(t,e){if(t==null)return!1;try{return re.process.kill?.(t,0)??!0}catch(r){return e&&console.error(`Error checking if process with PID ${t} exists:`,r),!1}}var P=require("get-port-please"),R=require("remeda"),$=51214,A=51213,E=51215,T=65535,m=-1/0;async function ae(t){let{debug:e,name:r,requestedPorts:n,servers:a}=t,{portsUsedByOtherServers:s,portsUsedByThisServerLastTime:c}=De(r,a);e&&(console.debug(`ports used by other servers: ${Object.keys(s).join(", ")}`),console.debug(`ports used by "${r}" server last time: ${JSON.stringify(c)}`));let d={databasePort:m,port:m,shadowDatabasePort:m},i=["port","databasePort","shadowDatabasePort"];for(let p of i){let f=await Se({debug:e,portKey:p,portsUsedByOtherServers:s,portsUsedByThisServerLastTime:c,requestedPorts:n})??await we({debug:e,pickedPorts:d,portKey:p,portsUsedByOtherServers:s,portsUsedByThisServerLastTime:c});e&&console.debug(`Got port for "${p}": ${f}`),d[p]=f}return e&&console.debug(`Picked ports: ${JSON.stringify(d)}`),d}async function Se(t){let{debug:e,portKey:r,portsUsedByOtherServers:n,portsUsedByThisServerLastTime:a,requestedPorts:s}=t,{[r]:c,...d}=s;if(oe(c))return await Oe({debug:e,otherRequestedPorts:d,portKey:r,portsUsedByOtherServers:n,requestedPort:c}),c;let i=a?.[r]??m;if(!oe(i))return e&&console.debug(`No port specified for "${r}". Trying to pick a new port.`),null;let p=i in n;return p||Object.values(d).includes(i)?(e&&console.debug(`Port ${i} that was used last time for this server, ${p?"is also used by another server":"has been requested for another service"}. Trying to pick a new port.`),null):await(0,P.checkPort)(i)===!1?(e&&console.debug(`Port ${i}, that was used last time for this server, is not available. Trying to pick a new port.`),null):(e&&console.debug(`Using port ${i} for "${r}" as it was used last time and is available.`),i)}async function we(t){let{debug:e,pickedPorts:r,portKey:n,portsUsedByOtherServers:a,portsUsedByThisServerLastTime:s}=t,c=Math.max($,A,E)+1,d=[...Object.values(r),...Object.keys(a).map(Number),...Object.values(s||{})],i=Math.min(Math.max(c,...d)+100,T),p=(0,R.difference)((0,R.range)(c,i),d),f={port:A,databasePort:$,shadowDatabasePort:E}[n];try{return await(0,P.getPort)({port:f in a||Object.values(r).includes(f)||Object.values(s||{}).includes(f)?void 0:f,ports:p})}catch(g){if(g instanceof Error&&g.name==="GetPortError"&&i+1<=T)return e&&console.debug(`Expanding port lookup to range [${i+1}, ${T}].`),await(0,P.getPort)({portRange:[i+1,T]});throw g}}function oe(t){return t>=0}function De(t,e){let r={},n;for(let a of e){let{databasePort:s,port:c,shadowDatabasePort:d}=a;if(a.name===t){n={databasePort:s,port:c,shadowDatabasePort:d};continue}r[s]=!0,r[c]=!0,r[d]=!0}return{portsUsedByOtherServers:r,portsUsedByThisServerLastTime:n}}async function Oe(t){let{debug:e,otherRequestedPorts:r,portKey:n,portsUsedByOtherServers:a,requestedPort:s}=t;if(s in a)throw e&&console.error(`Port ${s} was requested for "${n}", but is already used by another server.`),new q(s);if(Object.values(r).includes(s))throw e&&console.error(`Port ${s} was requested for "${n}", but also for another key.`),new I(s);if((0,P.isUnsafePort)(s))throw e&&console.error(`Port ${s} was requested for "${n}", but is unsafe.`),new k(s);if(await(0,P.checkPort)(s)===!1)throw e&&console.error(`Port ${s} was requested for "${n}", but is not available.`),new k(s)}var k=class extends Error{constructor(r){super(`Port \`${r}\` is not available.`);this.port=r}name="PortNotAvailableError"},I=class extends Error{constructor(r){super(`Port number \`${r}\` was requested twice. Please choose a different port for each service.`);this.port=r}name="PortRequestedTwiceError"},q=class extends Error{constructor(r){super(`Port number \`${r}\` belongs to another Prisma Dev server. Please choose a different port.`);this.port=r}name="PortBelongsToAnotherServerError"};var H=(0,o.pipe)((0,o.string)(),(0,o.url)()),ne=(0,o.object)({connectionString:H,prismaORMConnectionString:(0,o.optional)(H),terminalCommand:(0,o.optional)((0,o.string)())}),ie=(0,o.object)({url:H}),V=(0,o.pipe)((0,o.number)(),(0,o.integer)(),(0,o.minValue)(1)),xe=(0,o.object)({database:ne,http:ie,ppg:ie,shadowDatabase:ne}),_e=(0,o.object)({databasePort:V,exports:(0,o.optional)(xe),name:(0,o.pipe)((0,o.string)(),(0,o.minLength)(1)),pid:(0,o.optional)((0,o.pipe)((0,o.number)(),(0,o.integer)(),(0,o.minValue)(0))),port:V,shadowDatabasePort:V,version:(0,o.literal)("1")}),G=Symbol("initialize"),C="default",b=class{_databasePort;debug;dryRun;name;persistenceMode;pid;_port;_shadowDatabasePort;constructor(e){this._databasePort=e.databasePort??m,this.debug=e.debug??!1,this.dryRun=e.dryRun??!1,this.name=e.name??C,this.persistenceMode=e.persistenceMode,this.pid=e.pid??K.process.pid,this._port=e.port??m,this._shadowDatabasePort=e.shadowDatabasePort??m}static async createExclusively(e){let r=e?.dryRun!==!0&&e?.persistenceMode!=="stateless"?new w(e):new J(e);return await r[G](),r}static async fromServerDump(e){let{debug:r,name:n=C}=e??{},a=S(n),s=w.getServerDumpPath(a),c=await Q(s);if(c==null)return r&&console.debug(`[State] No server dump file found at: ${s}`),null;r&&(console.debug(`[State] server dump file found at "${s}":`),console.debug(c));let{issues:d,output:i,success:p}=(0,o.safeParse)((0,o.pipe)((0,o.string)(),(0,o.parseJson)(),_e),c);if(!p)throw r&&console.debug(`[State] Invalid server dump file at "${s}": ${JSON.stringify(d,null,2)}`),new Error(`Invalid Prisma Dev state for "${n}".`);return new w({databasePort:i.databasePort,debug:r,dryRun:!1,name:n,pid:i.pid,port:i.port,serverDump:i,shadowDatabasePort:i.shadowDatabasePort})}static async scan(e){let{debug:r}=e??{},n=(0,y.join)(S(C),"..");r&&console.debug(`[State] scanning for server states in: ${n}`);let a=await te(n);return r&&console.debug(`[State] found server names: ${JSON.stringify(a)}`),await Promise.all(a.map(s=>Te(s,e)))}get databasePort(){return this._databasePort}get port(){return this._port}get shadowDatabasePort(){return this._shadowDatabasePort}},J=class extends b{constructor(e){super({...e,databasePort:e?.databasePort??$,persistenceMode:"stateless",port:e?.port??A,shadowDatabasePort:e?.shadowDatabasePort??E})}get databaseDumpPath(){return"<DUMP_PATH>"}get pgliteDataDirPath(){return"memory://"}async[G](){}async close(){}async writeServerDump(){}},w=class t extends b{#t;#e;#o;#a;#r;#s=null;constructor(e){super({...e,persistenceMode:"stateful"}),this.#e=S(this.name),this.#t=(0,y.join)(this.#e,"db_dump.bak"),this.#o=(0,y.join)(this.#e,".pglite"),this.#r=e?.serverDump??null,this.#a=t.getServerDumpPath(this.#e)}static getServerDumpPath(e){return(0,y.join)(e,"server.json")}get databaseDumpPath(){return this.#t}get exports(){return this.#r?.exports}get pgliteDataDirPath(){return this.#o}async[G](){await ee(this.#e),this.debug&&console.debug(`[State] using data directory: ${this.#e}`);try{this.#s=await(0,L.lock)(this.#e,{lockfilePath:(0,y.join)(this.#e,".lock")}),this.debug&&console.debug(`[State] obtained lock on: ${this.#e}`);let e=await b.scan({debug:this.debug,onlyMetadata:!0}),r=await ae({debug:this.debug,name:this.name,requestedPorts:{databasePort:this.databasePort,port:this.port,shadowDatabasePort:this.shadowDatabasePort},servers:e});this._databasePort=r.databasePort,this._port=r.port,this._shadowDatabasePort=r.shadowDatabasePort,await this.writeServerDump()}catch(e){throw e instanceof Error&&"code"in e&&e.code==="ELOCKED"?new M(this):e}}async close(){if(this.#s!=null)try{await this.#s(),this.#s=null,this.debug&&console.debug(`[State] released lock on: ${this.#e}`)}catch(e){throw console.error(`[State] failed to release lock on: ${this.#e}`),e}}async writeServerDump(e){this.#r={name:this.name,version:"1",pid:K.process.pid,port:this.port,databasePort:this.databasePort,shadowDatabasePort:this.shadowDatabasePort,exports:e},await(0,ue.writeFile)(this.#a,`${JSON.stringify(this.#r,null,2)} `,{encoding:"utf-8"})}};async function Te(t,e){let{debug:r,onlyMetadata:n}=e||{},a={databasePort:-1,exports:void 0,name:t,pid:void 0,port:-1,shadowDatabasePort:-1,version:"1"};try{let s=await b.fromServerDump({debug:r,name:t});if(!s)return r&&console.debug(`[State] no server state found for name: ${t}`),{...a,status:"no_such_server"};a.databasePort=s.databasePort,a.exports=s.exports,a.pid=s.pid,a.port=s.port,a.shadowDatabasePort=s.shadowDatabasePort;let{exports:c,pid:d}=s;if(n)return{...a,status:"unknown"};if(!se(d,r))return r&&console.debug(`[State] server state for "${t}" has no running process with PID: ${d}`),{...a,status:"not_running"};let i=S(t);try{if(!await(0,L.check)(i,{lockfilePath:(0,y.join)(i,".lock")}))return r&&console.debug(`[State] server state for "${t}" is not locked, indicating it is not running.`),{...a,status:"starting_up"}}catch(ce){r&&console.error(`[State] server state for "${t}" failed to check lock:`,ce)}if(!c)return{...a,status:"starting_up"};let{http:p}=c,{hc:f}=await import("hono/client"),g=await f(p.url).health.$get();if(!g.ok)return r&&console.debug(`[State] server state for "${t}" is not live: ${JSON.stringify(g)}`),{...a,status:"not_running"};let U=await g.json();return U.name!==t?(r&&console.debug(`[State] server state for "${t}" has mismatched health response: ${JSON.stringify(U)}`),{...a,status:"unknown"}):(r&&console.debug(`[State] server state for "${t}" is live: ${JSON.stringify(U)}`),{...a,status:"running"})}catch(s){return r&&console.error(`[State] failed to get server status for "${t}":`,s),{...a,status:"error"}}}var j=class extends Error{name="ServerStateAlreadyExistsError";constructor(e){super(`A Prisma Dev server with the name "${e}" is already running.`)}},M=class extends j{#t;name="ServerAlreadyRunningError";constructor(e){super(e.name),this.#t=e}get server(){return b.fromServerDump({debug:this.#t.debug,name:this.#t.name})}};0&&(module.exports={ServerAlreadyRunningError,ServerState,ServerStateAlreadyExistsError});