UNPKG

@prisma/dev

Version:

A local Prisma Postgres server for development and testing

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