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