palgres 0.22.1 → 0.23.1
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.
- package/dist/chunks/4MS5ORKD.js +5 -0
- package/dist/chunks/{K7RJSXAR.js → CLLVMVAG.js} +2 -2
- package/dist/chunks/{LVZ3WEGH.js → PFAMQ2WN.js} +2 -2
- package/dist/dsl.js +2 -2
- package/dist/index.js +147 -114
- package/dist/testing.js +2 -2
- package/package.json +1 -1
- package/types/palgres/src/dsl/columns.d.ts +18 -4
- package/types/palgres/src/forget.d.ts +16 -0
- package/types/palgres/src/index.d.ts +1 -1
- package/types/palgres/src/migrate.d.ts +1 -1
- package/types/palgres/src/model.d.ts +3 -1
- package/types/palgres/src/plan.d.ts +15 -0
- package/types/palgres/src/query/driver.d.ts +27 -0
- package/types/palgres/src/query/index.d.ts +10 -0
- package/types/palgres/src/query/prepared.d.ts +58 -0
- package/types/palgres/src/query/request.d.ts +3 -0
- package/dist/chunks/TXGOBOPK.js +0 -5
package/dist/testing.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! palgres 0.
|
|
2
|
-
import{$ as wn,D as U,E as fn,F as Y,J as Z,K as V,L as z,M as dn,O as P,W as _,aa as $n,e as cn,g as ln}from"./chunks/K7RJSXAR.js";import"./chunks/TXGOBOPK.js";import{randomUUID as Rn}from"node:crypto";var On=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?)?(Z|[+-]\d{2}(:?\d{2})?)?$/,bn=/^([+-]?)(\d*)(?:\.(\d*))?$/;function q(e){if(e instanceof Date)return e.getTime();if(typeof e=="string"&&On.test(e)){let t=Date.parse(e);return Number.isNaN(t)?null:t}return null}function B(e){if(typeof e=="number")return Number.isFinite(e)?B(String(e)):null;if(typeof e!="string"||/e/i.test(e))return null;let t=bn.exec(e.trim());if(t===null||t[2]===""&&(t[3]??"")==="")return null;let a=t[3]??"";return{unit:BigInt(`${t[1]==="-"?"-":""}${t[2]===""?"0":t[2]}${a}`),scale:a.length}}function K(e,t){return e.unit*10n**BigInt(t-e.scale)}function tn(e,t){if(e==null||t===null||t===void 0)return null;let a=q(e),c=q(t);if(a!==null&&c!==null)return Math.sign(a-c);let w=B(e),f=B(t);if(w!==null&&f!==null){let d=Math.max(w.scale,f.scale),h=K(w,d)-K(f,d);return h===0n?0:h<0n?-1:1}return typeof e==typeof t&&(typeof e=="string"||typeof e=="boolean"||typeof e=="number")?e<t?-1:e>t?1:0:null}function N(e,t){return e==null||t===null||t===void 0?!1:e instanceof Date||t instanceof Date?q(e)!==null&&q(e)===q(t):Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((a,c)=>N(a,t[c])):e===t}function H(e){let t=((e?.days??0)*86400+(e?.hours??0)*3600+(e?.minutes??0)*60+(e?.seconds??0))*1e3;return new Date(Date.now()-t).toISOString()}function v(e,t,a){if(e==null)return null;if(typeof e=="number"&&typeof t=="number")return e+a*t;let c=B(e),w=B(t);if(c===null||w===null)throw new Error(`increment()/decrement() on a value that is not a number (${JSON.stringify(e)}): Postgres refuses it ("operator does not exist")`);let f=Math.max(c.scale,w.scale),d=K(c,f)+BigInt(a)*K(w,f);if(f===0)return typeof e=="number"?Number(d):d.toString();let h=d<0n,R=(h?-d:d).toString().padStart(f+1,"0"),k=`${h?"-":""}${R.slice(0,-f)}.${R.slice(-f)}`;return typeof e=="number"?Number(k):k}function kn(e,t){let a=A=>{let C=(A.unit<0n?-A.unit:A.unit).toString().padStart(A.scale+1,"0"),J=C.slice(0,C.length-A.scale),n=C.slice(C.length-A.scale),r=J.padStart(Math.ceil(J.length/4)*4,"0").match(/.{4}/g)??[],s=n.padEnd(Math.ceil(n.length/4)*4,"0").match(/.{4}/g)??[],i=[...r.map((u,l)=>[r.length-1-l,Number(u)]),...s.map((u,l)=>[-1-l,Number(u)])].find(([,u])=>u!==0);return i?[i[0],i[1]]:[0,0]},[c,w]=a(e),[f,d]=a(t),h=c-f-(w<=d?1:0),R=Math.min(Math.max(16-h*4,e.scale,t.scale,0),1e3),k=e.unit*10n**BigInt(R+t.scale),E=t.unit*10n**BigInt(e.scale),T=k/E,x=k%E;2n*(x<0n?-x:x)>=(E<0n?-E:E)&&(T+=k<0n!=E<0n?-1n:1n);let I=T<0n,W=(I?-T:T).toString().padStart(R+1,"0");return`${I?"-":""}${R===0?W:`${W.slice(0,-R)}.${W.slice(-R)}`}`}var $=e=>structuredClone(e);function rn(e,t,a){if(fn(a))throw new Error(`${e}(${t}): the fake database runs no SQL, so a sqlFragment cannot be evaluated \u2014 write the filter in the filter language (gt, gte, lt, lte, neq, in, contains, isNull, OR, AND, NOT), or test this against Postgres`);if(!(a===null||typeof a!="object"))for(let[c,w]of Object.entries(a))if(c==="OR"||c==="AND")for(let f of Array.isArray(w)?w:[])rn(e,t,f);else c==="NOT"&&rn(e,t,w)}function D(e,t,a){return cn(new ln({fields:[{field:t,message:a}]},`${e}: ${t} ${a}`))}function on(e,t,a="where"){for(let[c,w]of Object.entries(t))if(c==="OR"||c==="AND"){let f=w;if(c==="AND"&&f.length===0)throw D(e,`${a}.AND`,"is an empty list \u2014 no condition at all. Leave it out, or name a condition in it.");f.forEach((d,h)=>{if(Object.keys(d).length===0)throw D(e,`${a}.${c}[${h}]`,"holds no condition \u2014 a branch that matches every row. Name a condition in it, or leave it out.");on(e,d,`${a}.${c}[${h}]`)})}else if(c==="NOT"){if(Array.isArray(w))throw D(e,`${a}.NOT`,"is a list \u2014 NOT takes one filter object. For several conditions, NOT: { AND: [...] } or NOT: { OR: [...] }.");if(Object.keys(w).length===0)throw D(e,`${a}.NOT`,"holds no condition \u2014 NOT of nothing is not a filter. Name the condition to exclude.");on(e,w,`${a}.NOT`)}else if(c==="has"&&Object.keys(w??{}).length===0)throw D(e,`${a}.has`,"names no relation \u2014 no condition. Name one: has: { <relation>: { \u2026 } }.")}function F(e,t){if(Object.keys(t).length===0)throw D(e,"where",`holds no condition, so ${e} would reach EVERY row of the table. Name a condition.`)}function sn(e){let t=!1;for(let a of e){if(a===!1)return!1;a===null&&(t=!0)}return t?null:!0}function mn(e){let t=!1;for(let a of e){if(a===!0)return!0;a===null&&(t=!0)}return t?null:!1}var j=e=>e==null;function L(e,t,a,c){return sn(Object.entries(c).map(([w,f])=>{if(w==="OR")return mn(f.map(d=>L(e,t,a,d)));if(w==="AND")return sn(f.map(d=>L(e,t,a,d)));if(w==="NOT"){let d=L(e,t,a,f);return d===null?null:!d}if(w==="has")throw new Error(`${e}(${t}): the fake database has no relations, so a \`has\` filter cannot be decided for a row \u2014 read the related table first and filter by its ids ({ id: { in: [\u2026] } }), or test this against Postgres`);return xn(e,t,a,w,f)}))}var gn=["neq","gt","gte","lt","lte"];function Q(e,t){return j(e)||j(t)?null:N(e,t)}function xn(e,t,a,c,w){let f=a[c];return w===null?j(f):U(w)?Q(f,a[w.$col]):V(w)?Q(f,H(_(w)?.minus)):typeof w=="object"&&!Array.isArray(w)&&!(w instanceof Date)?sn(Object.entries(w).map(([d,h])=>{if(V(h)){if(!gn.includes(d))throw new Error(`${e}(${t}): where.${c}.${d} cannot take now()`);return G(d,f,H(_(h)?.minus))}if(Z(h))throw new Error(`${e}(${t}): where.${c}.${d} was given increment()/decrement() \u2014 a write, not a value to compare with`);if(U(h)){if(!gn.includes(d))throw new Error(`${e}(${t}): where.${c}.${d} cannot take col()`);return G(d,f,a[h.$col])}switch(d){case"in":{if(!Array.isArray(h))throw new Error(`${e}(${t}): where.${c}.in is a list`);return mn(h.map(R=>Q(f,R)))}case"neq":return h===null?!j(f):G(d,f,h);case"gt":case"gte":case"lt":case"lte":return G(d,f,h);case"isNull":if(typeof h!="boolean")throw new Error(`${e}(${t}): where.${c}.isNull is true or false`);return h?j(f):!j(f);case"overlaps":if(!Array.isArray(h))throw new Error(`${e}(${t}): where.${c}.overlaps is a list`);if(f!=null&&!Array.isArray(f))throw new Error(`${e}(${t}): where.${c}: "overlaps" is an array column's operator`);return j(f)?null:h.some(R=>f.some(k=>N(R,k)));case"contains":case"icontains":case"startsWith":case"endsWith":{if(d==="contains"&&Array.isArray(h))return j(f)?null:Array.isArray(f)&&h.every(R=>f.some(k=>N(R,k)));if(typeof h!="string")throw new Error(`${e}(${t}): where.${c}.${d} is text`);return j(f)?null:typeof f!="string"?!1:d==="contains"?f.includes(h):d==="icontains"?f.toLowerCase().includes(h.toLowerCase()):d==="startsWith"?f.startsWith(h):f.endsWith(h)}default:throw new Error(`${e}(${t}): where.${c} has no operator "${d}"`)}})):Q(f,w)}function G(e,t,a){if(j(t)||j(a))return null;if(e==="neq")return!N(t,a);let c=tn(t,a);return c===null?!1:e==="gt"?c>0:e==="gte"?c>=0:e==="lt"?c<0:c<=0}function En(){let e=new Map,t=new Map,a=new Map,c=new Map,w=[],f=n=>{let r=e.get(n);return r||e.set(n,r=[]),r},d=(n,r,s)=>{let o=n.get(r);o?o.push(s):n.set(r,[s])},h=n=>n.id===void 0?{id:Rn(),...n}:{...n},R=()=>{let n={store:new Map([...e].map(([r,s])=>[r,s.map($)])),inserted:hn(t),updated:hn(a),deleted:new Map([...c].map(([r,s])=>[r,[...s]]))};return()=>{e=n.store,t=n.inserted,a=n.updated,c=n.deleted}};function k(n,r,s){let o={};for(let[i,u]of Object.entries(s)){let l=_(u);if(l!==null&&l.fn==="now")o[i]=H(l.minus);else{if(l!==null)throw new Error(`${n}(${r}): "${i}" was given ${l.fn==="inc"?"increment()":"decrement()"} \u2014 the row does not exist yet, so there is no current value to change`);o[i]=u}}return P(n,r,Object.keys(o),o),z(n,r,Object.keys(o),o),o}function E(n,r,s,o){P(n,r,Object.keys(o),o);let i={};for(let[u,l]of Object.entries(o)){let g=_(l);g!==null&&g.fn==="now"?i[u]=H(g.minus):g!==null?i[u]=v(s[u],g.by,g.fn==="inc"?1:-1):i[u]=l}return z(n,r,Object.keys(i),i),i}function T(n,r,s){let o=h(k(n,r,s));return f(r).push(o),d(t,r,$(o)),$(o)}function x(n,r,s){return dn(n,r,s),rn(n,r,s),s!==void 0&&on(n,s),(e.get(r)??[]).filter(o=>s===void 0||L(n,r,o,s)===!0)}let I={diagnostics:()=>null,async query(n,r=[]){return w.push({sql:n,params:r}),[]},async findUnique(){throw new Error("the fake database knows no table's declared unique keys \u2014 find the row with findMany, or test this against Postgres")},async page(){throw new Error("the fake database cannot reproduce Postgres' cursor ordering, collation or row-level security \u2014 test pagination against Postgres")},async search(n){throw new Error(`search(${n}): the fake database has neither embeddings nor full text \u2014 test search against Postgres`)},async findById(n,r){let s=(e.get(n)??[]).find(o=>o.id===r);return s?$(s):null},async findMany(n,r,s){let{limit:o,offset:i}=s??{};if(o!==void 0&&(!Number.isInteger(o)||o<0))throw new Error(`findMany(${n}): limit is a whole number, 0 or more (got ${String(o)})`);if(i!==void 0){if(!Number.isInteger(i)||i<0)throw new Error(`findMany(${n}): offset is a whole number, 0 or more (got ${String(i)})`);if(o===void 0)throw new Error(`findMany(${n}): offset comes with limit \u2014 an offset into an unbounded result is not a page`)}if(s?.with!==void 0&&Object.keys(s.with).length>0)throw new Error(`findMany(${n}): the fake database has no relations, so \`with\` cannot be read \u2014 seed and query the related table on its own, or test this against Postgres`);let u=x("findMany",n,r),l=s?.orderBy===void 0?[]:Array.isArray(s.orderBy)?s.orderBy:[s.orderBy];l.length>0&&(u=[...u].sort((y,p)=>{for(let O of l){let b=O.direction==="desc"?-1:1,M=y[O.column],S=p[O.column],X=M==null,an=S==null;if(X||an){if(X&&an)continue;let pn=O.nulls===void 0?b===-1:O.nulls==="first";return(X?1:-1)*(pn?-1:1)}let un=tn(M,S)??(String(M)<String(S)?-1:String(M)>String(S)?1:0);if(un!==0)return un*b}return 0}));let g=o===void 0?u:u.slice(i??0,(i??0)+o),m=s?.select;return g.map(y=>m===void 0||m.length===0?$(y):Object.fromEntries(m.map(p=>[p,structuredClone(y[p])])))},async count(n,r={}){return x("count",n,r).length},async aggregate(n,r){let s=x("aggregate",n,r.where),o=r.groupBy??[],i=l=>{let g={},m=y=>l.map(p=>p[y]).filter(p=>p!=null);for(let y of r.sum??[]){let p=m(y);(g.sum??={})[y]=p.length===0?null:String(p.reduce((O,b)=>v(O,b,1),"0"))}for(let y of r.avg??[]){let p=m(y),O=p.some(b=>typeof b=="number"&&!Number.isInteger(b));(g.avg??={})[y]=p.length===0?null:O?p.reduce((b,M)=>b+M,0)/p.length:kn(B(p.reduce((b,M)=>v(b,M,1),"0")),{unit:BigInt(p.length),scale:0})}for(let[y,p]of[["min",-1],["max",1]])for(let O of r[y]??[]){let b=m(O);(g[y]??={})[O]=b.length===0?null:b.reduce((M,S)=>(tn(S,M)??0)*p>0?S:M)}return r.count===!0?g.count=l.length:r.count!==void 0&&r.count!==!1&&(g.count=Object.fromEntries(Object.entries(r.count).map(([y,p])=>[y,l.filter(O=>L("aggregate",n,O,p)===!0).length]))),g};if(o.length===0)return i(s);let u=new Map;for(let l of s){let g=JSON.stringify(o.map(m=>l[m]));u.set(g,[...u.get(g)??[],l])}return[...u.values()].map(l=>({...i(l),...Object.fromEntries(o.map(g=>[g,l[0][g]]))}))},async insert(n,r){return T("insert",n,r)},insertMany:(async(n,r,s)=>{if(wn(s),s?.onConflict?.length||s?.action!==void 0)throw new Error(`insertMany(${n}): the fake database knows no unique constraint to collide with \u2014 test onConflict against Postgres`);if(r.length===0)return s?.returning===!1?0:[];let o=Object.keys(r[0]);r.forEach((u,l)=>{let g=o.filter(y=>!(y in u)),m=Object.keys(u).filter(y=>!o.includes(y));if(g.length>0||m.length>0)throw new Error(`insertMany(${n}): row ${l} has not the first row's columns${g.length?` (missing ${g.join(", ")})`:""}${m.length?` (extra ${m.join(", ")})`:""} \u2014 write null for an empty one, or insert rows of another shape in a call of their own`)});let i=r.map(u=>T("insertMany",n,u));return s?.returning===!1?i.length:i}),async put(n,r,s){if(!s?.onConflict?.length)throw new Error(`put(${n}): onConflict names at least one column`);let o=k("put",n,r),i=f(n).find(u=>s.onConflict.every(l=>N(u[l],o[l])));if(!i)return T("put",n,r);for(let[u,l]of Object.entries(o))s.onConflict.includes(u)||(i[u]=l);return d(a,n,$(i)),$(i)},async update(n,r,s){let o=e.get(n)??[],i=o.findIndex(u=>u.id===r);return i<0?(P("update",n,Object.keys(s),s),null):(o[i]={...o[i],...E("update",n,o[i],s)},d(a,n,$(o[i])),$(o[i]))},async updateMany(n,r,s,o){F("updateMany",r);let i=x("updateMany",n,r),u=f(n),l=[];for(let g of i){let m=u.indexOf(g);u[m]={...g,...E("updateMany",n,g,s)},d(a,n,$(u[m])),l.push($(u[m]))}return i.length===0&&P("updateMany",n,Object.keys(s),s),o?.returning===!1?l.length:l},async delete(n,r){let s=e.get(n)??[],o=s.findIndex(i=>i.id===r);o<0||(s.splice(o,1),d(c,n,r))},async deleteMany(n,r){F("deleteMany",r);let s=x("deleteMany",n,r);e.set(n,(e.get(n)??[]).filter(o=>!s.includes(o)));for(let o of s)d(c,n,String(o.id));return s.length},async claim(n,r,s={}){let o=Object.keys(r);if(o.length===0)throw new Error(`claim(${n}): no unique field \u2014 claim takes a key; without one, insert(${n}, \u2026)`);P("claim",n,o,r);let i=(e.get(n)??[]).find(u=>o.every(l=>N(u[l],r[l])));return i?{inserted:!1,row:$(i)}:{inserted:!0,row:T("claim",n,{...r,...s})}},async lockRows(n,r){if(r.length===0)return;let s=e.get(n)??[],o=[...new Set(r)].filter(i=>!s.some(u=>u.id===i));if(o.length>0&&s.length>0)throw new Error(`lockRows(${n}): no row with id ${o.join(", ")}`)},async lockRowsWhere(n,r,s){let o=s?.mode??"update";if(!["update","share","noKeyUpdate"].includes(o))throw new Error(`lockRowsWhere(${n}): mode is "update", "share" or "noKeyUpdate" (got "${String(o)}")`);F("lockRowsWhere",r),x("lockRowsWhere",n,r)},async advisoryXactLock(){}};function W(n){let r=R(),s=[];try{for(let o of n.ops){let i=A(o,s);s.push(i);let u=Tn(o.guard,i.rows.length);if(u)throw u}}catch(o){throw r(),o}return{results:s}}function A(n,r){let s=i=>Object.fromEntries(Object.entries(i??{}).map(([u,l])=>[u,yn(l,r)])),o=`transaction plan ${n.op}`;switch(n.op){case"insert":return nn(T(o,n.table,s(n.values)));case"insertMany":{let i=(n.rows??[]).map(g=>s(g)),u=n.onConflict??[];if(n.action!==void 0&&u.length===0)throw new Error(`${o}(${n.table}): the fake database knows no unique key to collide with \u2014 name onConflict columns, or test this against Postgres`);let l=[];for(let g of i){let m=u.length>0?f(n.table).find(y=>u.every(p=>N(y[p],g[p]))):void 0;if(!(m&&n.action==="ignore")){if(m&&n.action==="update"){Object.assign(m,E(o,n.table,m,Object.fromEntries(Object.entries(g).filter(([y])=>!u.includes(y))))),d(a,n.table,$(m)),l.push($(m));continue}l.push(T(o,n.table,g))}}return{rows:l,rows_affected:l.length}}case"upsert":{let i=s(n.values),u=n.onConflict??[];if(Object.keys(i).length===0||u.length===0)throw Object.assign(new Error(`put on ${n.table} needs columns and onConflict`),{error_code:"tx_bad_upsert"});let l=f(n.table).find(g=>u.every(m=>N(g[m],i[m])));return l?(Object.assign(l,E(o,n.table,l,Object.fromEntries(Object.entries(i).filter(([g])=>!u.includes(g))))),d(a,n.table,$(l)),nn($(l))):nn(T(o,n.table,i))}case"update":{let i=s(n.set);if(Object.keys(i).length===0)throw new Error(`update ${n.table}: nothing to set`);let u=en(n.where,r);F(o,u);let l=f(n.table),g=[];for(let m of x(o,n.table,u)){let y=l.indexOf(m);l[y]={...m,...E(o,n.table,m,i)},d(a,n.table,$(l[y])),g.push($(l[y]))}return{rows:g,rows_affected:g.length}}case"delete":{let i=en(n.where,r);F(o,i);let u=x(o,n.table,i);e.set(n.table,f(n.table).filter(l=>!u.includes(l)));for(let l of u)d(c,n.table,String(l.id));return{rows:u.map($),rows_affected:u.length}}case"select":{let i=x(o,n.table,en(n.where,r));return n.limit!==void 0&&(i=i.slice(0,n.limit)),{rows:i.map($),rows_affected:i.length}}default:throw new Error(`unknown operation "${String(n.op)}" in a transaction plan`)}}let C={...I,async txPlan(n){return W(n)},async attempt(n){let r=R();try{return await n(I)}catch(s){throw r(),s}},async atomic(){throw new Error("the fake database cannot reproduce a transaction's isolation, COMMIT or retry \u2014 test $atomic against Postgres")}};return{...C,asService:()=>C,inserted:n=>(t.get(n)??[]).map($),updated:n=>(a.get(n)??[]).map($),deleted:n=>[...c.get(n)??[]],seed(n,r){e.set(n,r.map($))},queries:w}}function hn(e){return new Map([...e].map(([t,a])=>[t,a.map($)]))}function nn(e){return{rows:[e],rows_affected:1}}function yn(e,t){if(!Y(e))return e;let a=t[e.$ref.op]?.rows[0];if(!a)throw Object.assign(new Error(`transaction plan: operation ${e.$ref.op} produced no row to reference`),{error_code:"tx_ref_unresolved"});return a[e.$ref.field]}function en(e,t){let a=c=>{if(Y(c)){let w=yn(c,t);if(w==null)throw Object.assign(new Error(`transaction plan: a filter's reference to operation ${c.$ref.op}'s "${c.$ref.field}" is NULL`),{error_code:"tx_ref_null"});return w}return Array.isArray(c)?c.map(a):c!==null&&typeof c=="object"&&!U(c)&&!Z(c)&&!V(c)&&!(c instanceof Date)?Object.fromEntries(Object.entries(c).map(([w,f])=>[w,a(f)])):c};return a(e??{})}function Tn(e,t){return e?(e.kind==="one"?t===1:e.kind==="none"?t===0:e.kind==="atLeast"?t>=e.n:t<=e.n)?null:Object.assign(new Error(`transaction expectation failed: ${e.kind} (${t} row(s))`),{error_code:"tx_guard_failed",slot:e.slot}):null}export{En as createFakeClient,$n as typedDatabase};
|
|
1
|
+
/*! palgres 0.23.1 — Palbase SDK License (see LICENSE) */
|
|
2
|
+
import{$ as wn,D as U,E as fn,F as Y,J as Z,K as V,L as z,M as dn,O as P,W as _,aa as $n,e as cn,g as ln}from"./chunks/CLLVMVAG.js";import"./chunks/4MS5ORKD.js";import{randomUUID as Rn}from"node:crypto";var On=/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?)?(Z|[+-]\d{2}(:?\d{2})?)?$/,bn=/^([+-]?)(\d*)(?:\.(\d*))?$/;function q(e){if(e instanceof Date)return e.getTime();if(typeof e=="string"&&On.test(e)){let t=Date.parse(e);return Number.isNaN(t)?null:t}return null}function B(e){if(typeof e=="number")return Number.isFinite(e)?B(String(e)):null;if(typeof e!="string"||/e/i.test(e))return null;let t=bn.exec(e.trim());if(t===null||t[2]===""&&(t[3]??"")==="")return null;let a=t[3]??"";return{unit:BigInt(`${t[1]==="-"?"-":""}${t[2]===""?"0":t[2]}${a}`),scale:a.length}}function K(e,t){return e.unit*10n**BigInt(t-e.scale)}function tn(e,t){if(e==null||t===null||t===void 0)return null;let a=q(e),c=q(t);if(a!==null&&c!==null)return Math.sign(a-c);let w=B(e),f=B(t);if(w!==null&&f!==null){let d=Math.max(w.scale,f.scale),h=K(w,d)-K(f,d);return h===0n?0:h<0n?-1:1}return typeof e==typeof t&&(typeof e=="string"||typeof e=="boolean"||typeof e=="number")?e<t?-1:e>t?1:0:null}function N(e,t){return e==null||t===null||t===void 0?!1:e instanceof Date||t instanceof Date?q(e)!==null&&q(e)===q(t):Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((a,c)=>N(a,t[c])):e===t}function H(e){let t=((e?.days??0)*86400+(e?.hours??0)*3600+(e?.minutes??0)*60+(e?.seconds??0))*1e3;return new Date(Date.now()-t).toISOString()}function v(e,t,a){if(e==null)return null;if(typeof e=="number"&&typeof t=="number")return e+a*t;let c=B(e),w=B(t);if(c===null||w===null)throw new Error(`increment()/decrement() on a value that is not a number (${JSON.stringify(e)}): Postgres refuses it ("operator does not exist")`);let f=Math.max(c.scale,w.scale),d=K(c,f)+BigInt(a)*K(w,f);if(f===0)return typeof e=="number"?Number(d):d.toString();let h=d<0n,R=(h?-d:d).toString().padStart(f+1,"0"),k=`${h?"-":""}${R.slice(0,-f)}.${R.slice(-f)}`;return typeof e=="number"?Number(k):k}function kn(e,t){let a=A=>{let C=(A.unit<0n?-A.unit:A.unit).toString().padStart(A.scale+1,"0"),J=C.slice(0,C.length-A.scale),n=C.slice(C.length-A.scale),r=J.padStart(Math.ceil(J.length/4)*4,"0").match(/.{4}/g)??[],s=n.padEnd(Math.ceil(n.length/4)*4,"0").match(/.{4}/g)??[],i=[...r.map((u,l)=>[r.length-1-l,Number(u)]),...s.map((u,l)=>[-1-l,Number(u)])].find(([,u])=>u!==0);return i?[i[0],i[1]]:[0,0]},[c,w]=a(e),[f,d]=a(t),h=c-f-(w<=d?1:0),R=Math.min(Math.max(16-h*4,e.scale,t.scale,0),1e3),k=e.unit*10n**BigInt(R+t.scale),E=t.unit*10n**BigInt(e.scale),T=k/E,x=k%E;2n*(x<0n?-x:x)>=(E<0n?-E:E)&&(T+=k<0n!=E<0n?-1n:1n);let I=T<0n,W=(I?-T:T).toString().padStart(R+1,"0");return`${I?"-":""}${R===0?W:`${W.slice(0,-R)}.${W.slice(-R)}`}`}var $=e=>structuredClone(e);function rn(e,t,a){if(fn(a))throw new Error(`${e}(${t}): the fake database runs no SQL, so a sqlFragment cannot be evaluated \u2014 write the filter in the filter language (gt, gte, lt, lte, neq, in, contains, isNull, OR, AND, NOT), or test this against Postgres`);if(!(a===null||typeof a!="object"))for(let[c,w]of Object.entries(a))if(c==="OR"||c==="AND")for(let f of Array.isArray(w)?w:[])rn(e,t,f);else c==="NOT"&&rn(e,t,w)}function D(e,t,a){return cn(new ln({fields:[{field:t,message:a}]},`${e}: ${t} ${a}`))}function on(e,t,a="where"){for(let[c,w]of Object.entries(t))if(c==="OR"||c==="AND"){let f=w;if(c==="AND"&&f.length===0)throw D(e,`${a}.AND`,"is an empty list \u2014 no condition at all. Leave it out, or name a condition in it.");f.forEach((d,h)=>{if(Object.keys(d).length===0)throw D(e,`${a}.${c}[${h}]`,"holds no condition \u2014 a branch that matches every row. Name a condition in it, or leave it out.");on(e,d,`${a}.${c}[${h}]`)})}else if(c==="NOT"){if(Array.isArray(w))throw D(e,`${a}.NOT`,"is a list \u2014 NOT takes one filter object. For several conditions, NOT: { AND: [...] } or NOT: { OR: [...] }.");if(Object.keys(w).length===0)throw D(e,`${a}.NOT`,"holds no condition \u2014 NOT of nothing is not a filter. Name the condition to exclude.");on(e,w,`${a}.NOT`)}else if(c==="has"&&Object.keys(w??{}).length===0)throw D(e,`${a}.has`,"names no relation \u2014 no condition. Name one: has: { <relation>: { \u2026 } }.")}function F(e,t){if(Object.keys(t).length===0)throw D(e,"where",`holds no condition, so ${e} would reach EVERY row of the table. Name a condition.`)}function sn(e){let t=!1;for(let a of e){if(a===!1)return!1;a===null&&(t=!0)}return t?null:!0}function mn(e){let t=!1;for(let a of e){if(a===!0)return!0;a===null&&(t=!0)}return t?null:!1}var j=e=>e==null;function L(e,t,a,c){return sn(Object.entries(c).map(([w,f])=>{if(w==="OR")return mn(f.map(d=>L(e,t,a,d)));if(w==="AND")return sn(f.map(d=>L(e,t,a,d)));if(w==="NOT"){let d=L(e,t,a,f);return d===null?null:!d}if(w==="has")throw new Error(`${e}(${t}): the fake database has no relations, so a \`has\` filter cannot be decided for a row \u2014 read the related table first and filter by its ids ({ id: { in: [\u2026] } }), or test this against Postgres`);return xn(e,t,a,w,f)}))}var gn=["neq","gt","gte","lt","lte"];function Q(e,t){return j(e)||j(t)?null:N(e,t)}function xn(e,t,a,c,w){let f=a[c];return w===null?j(f):U(w)?Q(f,a[w.$col]):V(w)?Q(f,H(_(w)?.minus)):typeof w=="object"&&!Array.isArray(w)&&!(w instanceof Date)?sn(Object.entries(w).map(([d,h])=>{if(V(h)){if(!gn.includes(d))throw new Error(`${e}(${t}): where.${c}.${d} cannot take now()`);return G(d,f,H(_(h)?.minus))}if(Z(h))throw new Error(`${e}(${t}): where.${c}.${d} was given increment()/decrement() \u2014 a write, not a value to compare with`);if(U(h)){if(!gn.includes(d))throw new Error(`${e}(${t}): where.${c}.${d} cannot take col()`);return G(d,f,a[h.$col])}switch(d){case"in":{if(!Array.isArray(h))throw new Error(`${e}(${t}): where.${c}.in is a list`);return mn(h.map(R=>Q(f,R)))}case"neq":return h===null?!j(f):G(d,f,h);case"gt":case"gte":case"lt":case"lte":return G(d,f,h);case"isNull":if(typeof h!="boolean")throw new Error(`${e}(${t}): where.${c}.isNull is true or false`);return h?j(f):!j(f);case"overlaps":if(!Array.isArray(h))throw new Error(`${e}(${t}): where.${c}.overlaps is a list`);if(f!=null&&!Array.isArray(f))throw new Error(`${e}(${t}): where.${c}: "overlaps" is an array column's operator`);return j(f)?null:h.some(R=>f.some(k=>N(R,k)));case"contains":case"icontains":case"startsWith":case"endsWith":{if(d==="contains"&&Array.isArray(h))return j(f)?null:Array.isArray(f)&&h.every(R=>f.some(k=>N(R,k)));if(typeof h!="string")throw new Error(`${e}(${t}): where.${c}.${d} is text`);return j(f)?null:typeof f!="string"?!1:d==="contains"?f.includes(h):d==="icontains"?f.toLowerCase().includes(h.toLowerCase()):d==="startsWith"?f.startsWith(h):f.endsWith(h)}default:throw new Error(`${e}(${t}): where.${c} has no operator "${d}"`)}})):Q(f,w)}function G(e,t,a){if(j(t)||j(a))return null;if(e==="neq")return!N(t,a);let c=tn(t,a);return c===null?!1:e==="gt"?c>0:e==="gte"?c>=0:e==="lt"?c<0:c<=0}function En(){let e=new Map,t=new Map,a=new Map,c=new Map,w=[],f=n=>{let r=e.get(n);return r||e.set(n,r=[]),r},d=(n,r,s)=>{let o=n.get(r);o?o.push(s):n.set(r,[s])},h=n=>n.id===void 0?{id:Rn(),...n}:{...n},R=()=>{let n={store:new Map([...e].map(([r,s])=>[r,s.map($)])),inserted:hn(t),updated:hn(a),deleted:new Map([...c].map(([r,s])=>[r,[...s]]))};return()=>{e=n.store,t=n.inserted,a=n.updated,c=n.deleted}};function k(n,r,s){let o={};for(let[i,u]of Object.entries(s)){let l=_(u);if(l!==null&&l.fn==="now")o[i]=H(l.minus);else{if(l!==null)throw new Error(`${n}(${r}): "${i}" was given ${l.fn==="inc"?"increment()":"decrement()"} \u2014 the row does not exist yet, so there is no current value to change`);o[i]=u}}return P(n,r,Object.keys(o),o),z(n,r,Object.keys(o),o),o}function E(n,r,s,o){P(n,r,Object.keys(o),o);let i={};for(let[u,l]of Object.entries(o)){let g=_(l);g!==null&&g.fn==="now"?i[u]=H(g.minus):g!==null?i[u]=v(s[u],g.by,g.fn==="inc"?1:-1):i[u]=l}return z(n,r,Object.keys(i),i),i}function T(n,r,s){let o=h(k(n,r,s));return f(r).push(o),d(t,r,$(o)),$(o)}function x(n,r,s){return dn(n,r,s),rn(n,r,s),s!==void 0&&on(n,s),(e.get(r)??[]).filter(o=>s===void 0||L(n,r,o,s)===!0)}let I={diagnostics:()=>null,async query(n,r=[]){return w.push({sql:n,params:r}),[]},async findUnique(){throw new Error("the fake database knows no table's declared unique keys \u2014 find the row with findMany, or test this against Postgres")},async page(){throw new Error("the fake database cannot reproduce Postgres' cursor ordering, collation or row-level security \u2014 test pagination against Postgres")},async search(n){throw new Error(`search(${n}): the fake database has neither embeddings nor full text \u2014 test search against Postgres`)},async findById(n,r){let s=(e.get(n)??[]).find(o=>o.id===r);return s?$(s):null},async findMany(n,r,s){let{limit:o,offset:i}=s??{};if(o!==void 0&&(!Number.isInteger(o)||o<0))throw new Error(`findMany(${n}): limit is a whole number, 0 or more (got ${String(o)})`);if(i!==void 0){if(!Number.isInteger(i)||i<0)throw new Error(`findMany(${n}): offset is a whole number, 0 or more (got ${String(i)})`);if(o===void 0)throw new Error(`findMany(${n}): offset comes with limit \u2014 an offset into an unbounded result is not a page`)}if(s?.with!==void 0&&Object.keys(s.with).length>0)throw new Error(`findMany(${n}): the fake database has no relations, so \`with\` cannot be read \u2014 seed and query the related table on its own, or test this against Postgres`);let u=x("findMany",n,r),l=s?.orderBy===void 0?[]:Array.isArray(s.orderBy)?s.orderBy:[s.orderBy];l.length>0&&(u=[...u].sort((y,p)=>{for(let O of l){let b=O.direction==="desc"?-1:1,M=y[O.column],S=p[O.column],X=M==null,an=S==null;if(X||an){if(X&&an)continue;let pn=O.nulls===void 0?b===-1:O.nulls==="first";return(X?1:-1)*(pn?-1:1)}let un=tn(M,S)??(String(M)<String(S)?-1:String(M)>String(S)?1:0);if(un!==0)return un*b}return 0}));let g=o===void 0?u:u.slice(i??0,(i??0)+o),m=s?.select;return g.map(y=>m===void 0||m.length===0?$(y):Object.fromEntries(m.map(p=>[p,structuredClone(y[p])])))},async count(n,r={}){return x("count",n,r).length},async aggregate(n,r){let s=x("aggregate",n,r.where),o=r.groupBy??[],i=l=>{let g={},m=y=>l.map(p=>p[y]).filter(p=>p!=null);for(let y of r.sum??[]){let p=m(y);(g.sum??={})[y]=p.length===0?null:String(p.reduce((O,b)=>v(O,b,1),"0"))}for(let y of r.avg??[]){let p=m(y),O=p.some(b=>typeof b=="number"&&!Number.isInteger(b));(g.avg??={})[y]=p.length===0?null:O?p.reduce((b,M)=>b+M,0)/p.length:kn(B(p.reduce((b,M)=>v(b,M,1),"0")),{unit:BigInt(p.length),scale:0})}for(let[y,p]of[["min",-1],["max",1]])for(let O of r[y]??[]){let b=m(O);(g[y]??={})[O]=b.length===0?null:b.reduce((M,S)=>(tn(S,M)??0)*p>0?S:M)}return r.count===!0?g.count=l.length:r.count!==void 0&&r.count!==!1&&(g.count=Object.fromEntries(Object.entries(r.count).map(([y,p])=>[y,l.filter(O=>L("aggregate",n,O,p)===!0).length]))),g};if(o.length===0)return i(s);let u=new Map;for(let l of s){let g=JSON.stringify(o.map(m=>l[m]));u.set(g,[...u.get(g)??[],l])}return[...u.values()].map(l=>({...i(l),...Object.fromEntries(o.map(g=>[g,l[0][g]]))}))},async insert(n,r){return T("insert",n,r)},insertMany:(async(n,r,s)=>{if(wn(s),s?.onConflict?.length||s?.action!==void 0)throw new Error(`insertMany(${n}): the fake database knows no unique constraint to collide with \u2014 test onConflict against Postgres`);if(r.length===0)return s?.returning===!1?0:[];let o=Object.keys(r[0]);r.forEach((u,l)=>{let g=o.filter(y=>!(y in u)),m=Object.keys(u).filter(y=>!o.includes(y));if(g.length>0||m.length>0)throw new Error(`insertMany(${n}): row ${l} has not the first row's columns${g.length?` (missing ${g.join(", ")})`:""}${m.length?` (extra ${m.join(", ")})`:""} \u2014 write null for an empty one, or insert rows of another shape in a call of their own`)});let i=r.map(u=>T("insertMany",n,u));return s?.returning===!1?i.length:i}),async put(n,r,s){if(!s?.onConflict?.length)throw new Error(`put(${n}): onConflict names at least one column`);let o=k("put",n,r),i=f(n).find(u=>s.onConflict.every(l=>N(u[l],o[l])));if(!i)return T("put",n,r);for(let[u,l]of Object.entries(o))s.onConflict.includes(u)||(i[u]=l);return d(a,n,$(i)),$(i)},async update(n,r,s){let o=e.get(n)??[],i=o.findIndex(u=>u.id===r);return i<0?(P("update",n,Object.keys(s),s),null):(o[i]={...o[i],...E("update",n,o[i],s)},d(a,n,$(o[i])),$(o[i]))},async updateMany(n,r,s,o){F("updateMany",r);let i=x("updateMany",n,r),u=f(n),l=[];for(let g of i){let m=u.indexOf(g);u[m]={...g,...E("updateMany",n,g,s)},d(a,n,$(u[m])),l.push($(u[m]))}return i.length===0&&P("updateMany",n,Object.keys(s),s),o?.returning===!1?l.length:l},async delete(n,r){let s=e.get(n)??[],o=s.findIndex(i=>i.id===r);o<0||(s.splice(o,1),d(c,n,r))},async deleteMany(n,r){F("deleteMany",r);let s=x("deleteMany",n,r);e.set(n,(e.get(n)??[]).filter(o=>!s.includes(o)));for(let o of s)d(c,n,String(o.id));return s.length},async claim(n,r,s={}){let o=Object.keys(r);if(o.length===0)throw new Error(`claim(${n}): no unique field \u2014 claim takes a key; without one, insert(${n}, \u2026)`);P("claim",n,o,r);let i=(e.get(n)??[]).find(u=>o.every(l=>N(u[l],r[l])));return i?{inserted:!1,row:$(i)}:{inserted:!0,row:T("claim",n,{...r,...s})}},async lockRows(n,r){if(r.length===0)return;let s=e.get(n)??[],o=[...new Set(r)].filter(i=>!s.some(u=>u.id===i));if(o.length>0&&s.length>0)throw new Error(`lockRows(${n}): no row with id ${o.join(", ")}`)},async lockRowsWhere(n,r,s){let o=s?.mode??"update";if(!["update","share","noKeyUpdate"].includes(o))throw new Error(`lockRowsWhere(${n}): mode is "update", "share" or "noKeyUpdate" (got "${String(o)}")`);F("lockRowsWhere",r),x("lockRowsWhere",n,r)},async advisoryXactLock(){}};function W(n){let r=R(),s=[];try{for(let o of n.ops){let i=A(o,s);s.push(i);let u=Tn(o.guard,i.rows.length);if(u)throw u}}catch(o){throw r(),o}return{results:s}}function A(n,r){let s=i=>Object.fromEntries(Object.entries(i??{}).map(([u,l])=>[u,yn(l,r)])),o=`transaction plan ${n.op}`;switch(n.op){case"insert":return nn(T(o,n.table,s(n.values)));case"insertMany":{let i=(n.rows??[]).map(g=>s(g)),u=n.onConflict??[];if(n.action!==void 0&&u.length===0)throw new Error(`${o}(${n.table}): the fake database knows no unique key to collide with \u2014 name onConflict columns, or test this against Postgres`);let l=[];for(let g of i){let m=u.length>0?f(n.table).find(y=>u.every(p=>N(y[p],g[p]))):void 0;if(!(m&&n.action==="ignore")){if(m&&n.action==="update"){Object.assign(m,E(o,n.table,m,Object.fromEntries(Object.entries(g).filter(([y])=>!u.includes(y))))),d(a,n.table,$(m)),l.push($(m));continue}l.push(T(o,n.table,g))}}return{rows:l,rows_affected:l.length}}case"upsert":{let i=s(n.values),u=n.onConflict??[];if(Object.keys(i).length===0||u.length===0)throw Object.assign(new Error(`put on ${n.table} needs columns and onConflict`),{error_code:"tx_bad_upsert"});let l=f(n.table).find(g=>u.every(m=>N(g[m],i[m])));return l?(Object.assign(l,E(o,n.table,l,Object.fromEntries(Object.entries(i).filter(([g])=>!u.includes(g))))),d(a,n.table,$(l)),nn($(l))):nn(T(o,n.table,i))}case"update":{let i=s(n.set);if(Object.keys(i).length===0)throw new Error(`update ${n.table}: nothing to set`);let u=en(n.where,r);F(o,u);let l=f(n.table),g=[];for(let m of x(o,n.table,u)){let y=l.indexOf(m);l[y]={...m,...E(o,n.table,m,i)},d(a,n.table,$(l[y])),g.push($(l[y]))}return{rows:g,rows_affected:g.length}}case"delete":{let i=en(n.where,r);F(o,i);let u=x(o,n.table,i);e.set(n.table,f(n.table).filter(l=>!u.includes(l)));for(let l of u)d(c,n.table,String(l.id));return{rows:u.map($),rows_affected:u.length}}case"select":{let i=x(o,n.table,en(n.where,r));return n.limit!==void 0&&(i=i.slice(0,n.limit)),{rows:i.map($),rows_affected:i.length}}default:throw new Error(`unknown operation "${String(n.op)}" in a transaction plan`)}}let C={...I,async txPlan(n){return W(n)},async attempt(n){let r=R();try{return await n(I)}catch(s){throw r(),s}},async atomic(){throw new Error("the fake database cannot reproduce a transaction's isolation, COMMIT or retry \u2014 test $atomic against Postgres")}};return{...C,asService:()=>C,inserted:n=>(t.get(n)??[]).map($),updated:n=>(a.get(n)??[]).map($),deleted:n=>[...c.get(n)??[]],seed(n,r){e.set(n,r.map($))},queries:w}}function hn(e){return new Map([...e].map(([t,a])=>[t,a.map($)]))}function nn(e){return{rows:[e],rows_affected:1}}function yn(e,t){if(!Y(e))return e;let a=t[e.$ref.op]?.rows[0];if(!a)throw Object.assign(new Error(`transaction plan: operation ${e.$ref.op} produced no row to reference`),{error_code:"tx_ref_unresolved"});return a[e.$ref.field]}function en(e,t){let a=c=>{if(Y(c)){let w=yn(c,t);if(w==null)throw Object.assign(new Error(`transaction plan: a filter's reference to operation ${c.$ref.op}'s "${c.$ref.field}" is NULL`),{error_code:"tx_ref_null"});return w}return Array.isArray(c)?c.map(a):c!==null&&typeof c=="object"&&!U(c)&&!Z(c)&&!V(c)&&!(c instanceof Date)?Object.fromEntries(Object.entries(c).map(([w,f])=>[w,a(f)])):c};return a(e??{})}function Tn(e,t){return e?(e.kind==="one"?t===1:e.kind==="none"?t===0:e.kind==="atLeast"?t>=e.n:t<=e.n)?null:Object.assign(new Error(`transaction expectation failed: ${e.kind} (${t} row(s))`),{error_code:"tx_guard_failed",slot:e.slot}):null}export{En as createFakeClient,$n as typedDatabase};
|
package/package.json
CHANGED
|
@@ -99,7 +99,9 @@ export interface ColumnDef {
|
|
|
99
99
|
* expression over this row's columns. The deploy fills every row that does not
|
|
100
100
|
* supply the column with it — the rows already in the table and every write of
|
|
101
101
|
* a release that does not know the column yet — and only then makes it NOT
|
|
102
|
-
* NULL.
|
|
102
|
+
* NULL. A type change reads it for a value that does not convert, and an
|
|
103
|
+
* ignored column for every row inserted without it. Absent when the column
|
|
104
|
+
* declares none, so the wire stays byte-identical.
|
|
103
105
|
*/
|
|
104
106
|
fill?: string;
|
|
105
107
|
onDeleteAction?: OnDeleteAction;
|
|
@@ -346,9 +348,21 @@ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends
|
|
|
346
348
|
* runs with a pinned search_path. It reads BYPASSING row-level security, and
|
|
347
349
|
* what it writes is visible to anyone who can read the row.
|
|
348
350
|
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
351
|
+
* When the column's type changes by a cast that may fail on a value the
|
|
352
|
+
* serving release writes (text to integer, 'A-17'), `expr` is the value for
|
|
353
|
+
* one that does not convert, over the row as that release wrote it — the
|
|
354
|
+
* column read as its old value. A value neither converts is left NULL in the
|
|
355
|
+
* new column, and kept in the old one at complete. On a `.nullable()`
|
|
356
|
+
* column, that is all it does: the deploy refuses one declared where the
|
|
357
|
+
* type does not change so.
|
|
358
|
+
*
|
|
359
|
+
* On an `.ignored()` NOT NULL column with no default — the step before its
|
|
360
|
+
* removal — it is the value every row inserted without it gets, as long as
|
|
361
|
+
* it is ignored: the release still serving reads a real value in the rows
|
|
362
|
+
* this one writes, and the column stays NOT NULL until it goes.
|
|
363
|
+
*
|
|
364
|
+
* Only on a column with no default; `defineTable` refuses any other column
|
|
365
|
+
* and names it. For a constant value, use `.default()`.
|
|
352
366
|
*/
|
|
353
367
|
fillWith(expr: string): ColumnBuilder<K, N, D, E, P, T, W, Q>;
|
|
354
368
|
/**
|
|
@@ -5,8 +5,24 @@ interface Begin {
|
|
|
5
5
|
unsafe(sql: string, params?: unknown[]): PromiseLike<unknown>;
|
|
6
6
|
}) => Promise<T>): Promise<T>;
|
|
7
7
|
}
|
|
8
|
+
/** What forgetting a user runs a statement on: a transaction as service_role. */
|
|
9
|
+
interface Runs {
|
|
10
|
+
query(sql: string, params?: unknown[]): PromiseLike<unknown>;
|
|
11
|
+
}
|
|
8
12
|
export declare function forgetUser(sql: Begin, binding: Binding, schema: Schema, userId: string, serviceRole?: string): Promise<{
|
|
9
13
|
deleted: number;
|
|
10
14
|
detached: number;
|
|
11
15
|
}>;
|
|
16
|
+
/**
|
|
17
|
+
* The same, in a transaction already open as service_role — a delivery's own ($asService(): all its handlers' work, or
|
|
18
|
+
* none, within the request's share of connections): in a savepoint of it, so a failure leaves the rest of the
|
|
19
|
+
* transaction as it was. Only the schema's own deferrable keys wait (palgres.defer_keys, as the engine's delete), and
|
|
20
|
+
* are checked again before it is given back: the transaction's other constraints stay as its other handlers set them.
|
|
21
|
+
*/
|
|
22
|
+
export declare function forgetUserIn(service: {
|
|
23
|
+
attempt<T>(fn: (tx: Runs) => Promise<T>): Promise<T>;
|
|
24
|
+
}, binding: Binding, schema: Schema, userId: string): Promise<{
|
|
25
|
+
deleted: number;
|
|
26
|
+
detached: number;
|
|
27
|
+
}>;
|
|
12
28
|
export {};
|
|
@@ -6,7 +6,7 @@ export { applicationName, cutOff, DEFAULT_ROLES, isDirect, MigrationOpen, Migrat
|
|
|
6
6
|
export type { Adoption, MigratorOptions, Pool, Reserved, StartResult, Status, Version } from './migrate.js';
|
|
7
7
|
export { bind, binding, bindStatement, VersionNotMigrated } from './connect.js';
|
|
8
8
|
export type { Binding } from './connect.js';
|
|
9
|
-
export { forgetUser } from './forget.js';
|
|
9
|
+
export { forgetUser, forgetUserIn } from './forget.js';
|
|
10
10
|
export { adoptionBaseline, drift, type Baseline } from './adopt.js';
|
|
11
11
|
export { introspect, type Introspected } from './introspect.js';
|
|
12
12
|
export { pullSchema, type Pulled } from './pull.js';
|
|
@@ -147,7 +147,6 @@ export declare class Migrator {
|
|
|
147
147
|
* that stopped, or answers that it is in sync.
|
|
148
148
|
*/
|
|
149
149
|
start(schema: Schema): Promise<StartResult>;
|
|
150
|
-
/** Ends the open migration once nothing of the old version is connected: what only it used goes. */
|
|
151
150
|
complete(): Promise<{
|
|
152
151
|
state: 'completed';
|
|
153
152
|
version: number;
|
|
@@ -155,6 +154,7 @@ export declare class Migrator {
|
|
|
155
154
|
} | {
|
|
156
155
|
state: 'none';
|
|
157
156
|
}>;
|
|
157
|
+
private unconverted;
|
|
158
158
|
private kept;
|
|
159
159
|
/** Abandons the open migration once nothing of the new version is connected: what start added goes. */
|
|
160
160
|
rollback(): Promise<{
|
|
@@ -53,7 +53,9 @@ export interface Column {
|
|
|
53
53
|
/**
|
|
54
54
|
* An SQL expression over the row's columns for what is NULL — the rows
|
|
55
55
|
* already there and whatever an older version still writes — when the
|
|
56
|
-
* column becomes NOT NULL
|
|
56
|
+
* column becomes NOT NULL; and, when its type changes by a cast that may
|
|
57
|
+
* fail, the value for one of the old type that does not convert (over the
|
|
58
|
+
* row as the older version wrote it). Nullable, that is all it does.
|
|
57
59
|
*/
|
|
58
60
|
fill?: string;
|
|
59
61
|
identity?: 'always' | 'by default';
|
|
@@ -88,6 +88,14 @@ export interface Plan {
|
|
|
88
88
|
guarded?: {
|
|
89
89
|
tables: [string, string][];
|
|
90
90
|
columns: [string, string, string][];
|
|
91
|
+
retyped?: {
|
|
92
|
+
schema: string;
|
|
93
|
+
table: string;
|
|
94
|
+
column: string;
|
|
95
|
+
kept: string;
|
|
96
|
+
type: string;
|
|
97
|
+
key: string[];
|
|
98
|
+
}[];
|
|
91
99
|
};
|
|
92
100
|
}
|
|
93
101
|
/** A database taken over: what adoption changed itself, and what it left as it is. */
|
|
@@ -138,6 +146,13 @@ export interface PlanInput {
|
|
|
138
146
|
vectorSchema?: string;
|
|
139
147
|
}
|
|
140
148
|
export declare const versionSchema: (n: number, schema: string) => string;
|
|
149
|
+
/**
|
|
150
|
+
* Whether every value of one type becomes a value of the other, and no two
|
|
151
|
+
* become one: a wider number, a longer text. Any other cast may fail on a
|
|
152
|
+
* value the old version validly writes — text to integer fails on 'A-17' —
|
|
153
|
+
* or lose one (numeric to integer).
|
|
154
|
+
*/
|
|
155
|
+
export declare function widens(from: string, to: string): boolean;
|
|
141
156
|
export declare function plan(input: PlanInput): Plan;
|
|
142
157
|
/**
|
|
143
158
|
* The sequences a default takes its values from — `nextval('public.items_id_seq'::regclass)`,
|
|
@@ -13,7 +13,34 @@ export interface StatementHint {
|
|
|
13
13
|
* engine calls writes. A transaction of nothing else has nothing to commit.
|
|
14
14
|
*/
|
|
15
15
|
read?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The statement as one its backend keeps prepared (prepared.ts): sent as
|
|
18
|
+
* that, in place of the text and its parameters. A driver that does not
|
|
19
|
+
* know it sends the text, as it would without it.
|
|
20
|
+
*/
|
|
21
|
+
prepared?: PreparedRun;
|
|
16
22
|
}
|
|
23
|
+
/** A statement as one its backend keeps prepared: what the engine's PreparedStatements made of it. */
|
|
24
|
+
export interface PreparedRun {
|
|
25
|
+
/**
|
|
26
|
+
* `PREPARE name (unknown, …) AS text`, after the DEALLOCATEs that make
|
|
27
|
+
* room — when the backend does not hold it yet. A round trip of its own
|
|
28
|
+
* before `execute`, so whether it prepared is known.
|
|
29
|
+
*/
|
|
30
|
+
prepare?: string;
|
|
31
|
+
/** `EXECUTE name(…)`, the values literals: the statement's answer. */
|
|
32
|
+
execute: string;
|
|
33
|
+
/** Told of a failure of either, and whether it was the prepare's. */
|
|
34
|
+
failed(error: unknown, preparing: boolean): void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sends a prepared statement through `simple` (a query of no parameters):
|
|
38
|
+
* its prepare, then its execute, both asked for at once — a statement asked
|
|
39
|
+
* for after this one still runs after it. A prepare refused leaves its
|
|
40
|
+
* execute refused too (no statement, or an aborted transaction): the
|
|
41
|
+
* prepare's is the failure said.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runPrepared(run: PreparedRun, simple: (sql: string) => PromiseLike<unknown>): Promise<unknown>;
|
|
17
44
|
export interface SqlTx {
|
|
18
45
|
unsafe(sql: string, params?: unknown[], hint?: StatementHint): Statement;
|
|
19
46
|
savepoint<T>(fn: (sp: SqlTx) => Promise<T>): Promise<T>;
|
|
@@ -43,6 +43,16 @@ export interface DatabaseOptions {
|
|
|
43
43
|
* encryption off, is refused without it. A loader is called only then.
|
|
44
44
|
*/
|
|
45
45
|
fields?: FieldsSource;
|
|
46
|
+
/**
|
|
47
|
+
* Whether a statement the process has sent before goes, after its
|
|
48
|
+
* transaction's first, as EXECUTE of one its connection keeps prepared
|
|
49
|
+
* (default true): Postgres then neither parses nor analyzes it anew, and
|
|
50
|
+
* plans it for each EXECUTE as for a statement sent as it is. Up to 16 a
|
|
51
|
+
* connection (PREPARED_PER_BACKEND), in SQL — PREPARE and EXECUTE, the
|
|
52
|
+
* values literals — so neither driver's own preparing is involved
|
|
53
|
+
* (prepared.ts). false sends every statement as it is.
|
|
54
|
+
*/
|
|
55
|
+
prepare?: boolean;
|
|
46
56
|
}
|
|
47
57
|
/** One request's — or one job's — database. `T`, `S`: the project's tables (the framework's `Tables`, `Schemas`). */
|
|
48
58
|
export interface RequestDatabase<T, S> {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PreparedRun } from './driver.js';
|
|
2
|
+
/**
|
|
3
|
+
* Statements one session keeps prepared. Each holds ~0.1 MB of the backend's
|
|
4
|
+
* memory — its parse tree, its analyzed query and its generic plan (measured
|
|
5
|
+
* on the tests' schema, Postgres 18: 110–125 kB) — and the platform gives a
|
|
6
|
+
* connection 8 MiB of the database's (a free database: 512 MiB, 64
|
|
7
|
+
* connections): 16 hold ~2 MB of it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PREPARED_PER_BACKEND = 16;
|
|
10
|
+
/** A statement is prepared the second time the process sends it: one sent once (a shape the code makes only now and then) costs nothing. */
|
|
11
|
+
export declare const PREPARE_AT_SENDING = 2;
|
|
12
|
+
/** What a transaction that may EXECUTE runs under: every EXECUTE planned with its own values and its caller (above). */
|
|
13
|
+
export declare const PLAN_CACHE_MODE = "force_custom_plan";
|
|
14
|
+
/** A statement larger — a bulk write's — is sent as it is: parsing it is little of its time, and its plan would hold much of the backend's memory. */
|
|
15
|
+
export declare const PREPARED_MAX_TEXT = 16384;
|
|
16
|
+
export declare const PREPARED_MAX_PARAMS = 256;
|
|
17
|
+
/** Statement texts counted, and sessions known: well past a pool's connections, the gone ones dropped first. */
|
|
18
|
+
export declare const KNOWN_TEXTS = 2000;
|
|
19
|
+
/** Whether the code's own statement may DEALLOCATE what the session keeps: what it kept is then forgotten. */
|
|
20
|
+
export declare function deallocates(text: string): boolean;
|
|
21
|
+
/** The engine's prepared statements, for one database: which statements each session of its pool holds. */
|
|
22
|
+
export declare class PreparedStatements {
|
|
23
|
+
private readonly texts;
|
|
24
|
+
private readonly sessions;
|
|
25
|
+
/** A mark for a session that has none: the transaction's opening sets it, and it stays once that transaction commits. Never given twice. */
|
|
26
|
+
mark(): string;
|
|
27
|
+
/** What the session marked `mark` keeps: asked once a transaction, when its opening answers. */
|
|
28
|
+
on(mark: string): SessionStatements;
|
|
29
|
+
/** A statement sent where no session is known to keep it — before the opening answered, or in a session not marked yet: counted, sent as it is. */
|
|
30
|
+
count(text: string, params: readonly unknown[], written: boolean): void;
|
|
31
|
+
/** `text`, counted once more: how many times it has been sent. */
|
|
32
|
+
sent(text: string): number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One session's prepared statements, by their text — so a text the process
|
|
36
|
+
* stopped counting still finds the statement it has here. Its share counts
|
|
37
|
+
* those it can no longer EXECUTE too: never DEALLOCATEd (a DEALLOCATE of a
|
|
38
|
+
* name a pooler or the code took first would abort the caller's transaction),
|
|
39
|
+
* they hold their place, and the backend's memory, until the session ends.
|
|
40
|
+
*/
|
|
41
|
+
export declare class SessionStatements {
|
|
42
|
+
private readonly statements;
|
|
43
|
+
private refused;
|
|
44
|
+
private readonly all;
|
|
45
|
+
constructor(all: PreparedStatements);
|
|
46
|
+
/** How many it holds, as far as the engine knows. */
|
|
47
|
+
get size(): number;
|
|
48
|
+
/**
|
|
49
|
+
* `text` with its `params` as a statement this session keeps prepared, or
|
|
50
|
+
* undefined — to send as it is: a statement PREPARE cannot hold, one too
|
|
51
|
+
* large, one sent for the first time, or a value no literal can say.
|
|
52
|
+
* `written`: the engine wrote it (StatementHint.inline).
|
|
53
|
+
*/
|
|
54
|
+
run(text: string, params: readonly unknown[], written: boolean): PreparedRun | undefined;
|
|
55
|
+
/** Nothing is known to be held: what is, is left to the session. */
|
|
56
|
+
forget(): void;
|
|
57
|
+
private failed;
|
|
58
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { Catalog } from './catalog.js';
|
|
|
5
5
|
import type { DBClient } from './client.js';
|
|
6
6
|
import { type DatabaseDiagnosticsOptions } from './diagnostics.js';
|
|
7
7
|
import type { SqlDriver } from './driver.js';
|
|
8
|
+
import type { PreparedStatements } from './prepared.js';
|
|
8
9
|
import type { SearchRuntime } from './search.js';
|
|
9
10
|
/**
|
|
10
11
|
* Who a request is, as Postgres will see it: a signed-in caller is
|
|
@@ -50,5 +51,7 @@ export interface EngineContext {
|
|
|
50
51
|
readonly admission: DatabaseAdmission;
|
|
51
52
|
readonly cursorKey: Buffer | null;
|
|
52
53
|
readonly search: SearchRuntime;
|
|
54
|
+
/** What the pool's backends keep prepared: none when the database was made with prepare: false. */
|
|
55
|
+
readonly prepared?: PreparedStatements;
|
|
53
56
|
}
|
|
54
57
|
export declare function createRequestTransactions(sql: SqlDriver, ctx: EngineContext, identity: Identity, options?: RequestOptions): RequestTransactions;
|
package/dist/chunks/TXGOBOPK.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/*! palgres 0.22.1 — Palbase SDK License (see LICENSE) */
|
|
2
|
-
import{createHash as z}from"node:crypto";function de(n){return`${n}_lookup`}function me(n,e){let t=r=>r==="cascade"||r==="set null"||r==="set default";return t(n)||t(e)}var L=class extends Error{problems;constructor(e){super(`the schema is not valid:
|
|
3
|
-
- ${e.join(`
|
|
4
|
-
- `)}`),this.name="SchemaError",this.problems=e}},pe=()=>({tables:[],enums:[],extensions:[],raw:[]}),b=n=>`${n.schema}.${n.name}`,J={int:"integer",int4:"integer",int2:"smallint",int8:"bigint",float4:"real",float8:"double precision",bool:"boolean",timestamptz:"timestamp with time zone",timestamp:"timestamp without time zone","timestamp without time zone":"timestamp without time zone",timetz:"time with time zone",time:"time without time zone",varchar:"character varying",char:"character",decimal:"numeric"};function D(n){let e=n.trim().replace(/\s+/g," "),t="";for(;e.endsWith("[]");)t+="[]",e=e.slice(0,-2).trim();let r=/^((?:"(?:[^"]|"")+"|[A-Za-z_][A-Za-z0-9_ ]*?)(?:\.(?:"(?:[^"]|"")+"|[A-Za-z_][A-Za-z0-9_]*))?)\s*(\(.*\))?$/.exec(e);if(!r)return n.trim()+t;let a=r[2]?r[2].replace(/\s+/g,""):"",o=Q(r[1]);return o===void 0?n.trim()+t:o.length===1&&o[0].quoted?`"${o[0].name.replace(/"/g,'""')}"${a}${t}`:o.length===1?`${J[o[0].name]??o[0].name}${a}${t}`:`${o.map(l=>N(l.name)).join(".")}${a}${t}`}var N=n=>/^[a-z_][a-z0-9_$]*$/.test(n)?n:`"${n.replace(/"/g,'""')}"`,he=(n,e)=>`${N(n)}.${N(e)}`;function Q(n){let e=[],t=0;for(;t<n.length;){if(n[t]==='"'){let r=t+1,a="";for(;;){if(r>=n.length)return;if(n[r]==='"'){if(n[r+1]==='"'){a+='"',r+=2;continue}break}a+=n[r++]}e.push({name:a,quoted:!0}),t=r+1}else{let r=/^[A-Za-z_][A-Za-z0-9_ ]*/.exec(n.slice(t));if(!r)return;e.push({name:r[0].trim().toLowerCase(),quoted:!1}),t+=r[0].length}if(t<n.length){if(n[t]!==".")return;t++}}return e}var W=/^[A-Za-z_][A-Za-z0-9_$]*$/,Z=63;function g(n,e,t=!1){if(typeof e!="string"||e==="")return`${n} has no name`;if(t?e.includes("\0"):!W.test(e))return`${n} "${e}": a name is letters, digits and _ (starting with a letter or _)`;if(Buffer.byteLength(e)>Z)return`${n} "${e}": longer than Postgres' 63 bytes, it would be cut`;if(e.startsWith("palgres_")||e.startsWith("_palgres"))return`${n} "${e}": names starting with palgres_ are Palgres' own`}var V="vector",F=["cosine","euclidean","inner_product"],q=1536,G=2e3,R=4e3,X=/^(text|citext|public\.citext|character varying(\(\d+\))?)$/;function v(n,e,t){let r=[];e.primaryKey.length!==1&&r.push(`${n} declares search and has ${e.primaryKey.length===0?"no primary key":`a primary key of ${e.primaryKey.length} columns`}: a row's vector is kept by its key \u2014 give the table a single-column primary key (uuid().primaryKey())`),(!Array.isArray(t.from)||t.from.length===0)&&r.push(`${n}: search.from is empty \u2014 name the text columns a row is found by (search: { from: ['title', 'body'], model })`);let a=new Set;for(let o of Array.isArray(t.from)?t.from:[]){a.has(o)&&r.push(`${n}: search.from names "${o}" twice \u2014 name each column once`),a.add(o);let l=e.columns.find(i=>i.name===o);l?l.ignored?r.push(`${n}: search.from names "${o}", which is ignored \u2014 take it out of search.from, or keep the column`):X.test(D(l.type))?l.generated&&r.push(`${n}: search.from names "${o}", a generated column \u2014 no write names it, so nothing would say when to embed the row again: name the columns it is computed from`):r.push(`${n}: search.from names "${o}", which is ${l.type} \u2014 search reads text (text, varchar, citext): name a text column, or keep the text in one`):r.push(`${n}: search.from names "${o}", which the table does not have \u2014 name its text columns`)}return(typeof t.model!="string"||t.model.trim()==="")&&r.push(`${n}: search.model is empty \u2014 name the embedding model (search: { from, model: 'embedding' })`),F.includes(t.metric)||r.push(`${n}: search.metric is ${JSON.stringify(t.metric)} \u2014 it is ${F.map(o=>`'${o}'`).join(", ")}`),(!Number.isInteger(t.dimensions)||t.dimensions<1||t.dimensions>R)&&r.push(`${n}: search.dimensions is ${String(t.dimensions)} \u2014 it is 1 to ${R}: pgvector indexes a vector of at most ${G} dimensions and a half-precision one of at most ${R}; ask the model for fewer (search: { dimensions: ${q} })`),t.staleness!=="null"&&t.staleness!=="keep"&&r.push(`${n}: search.staleness is ${JSON.stringify(t.staleness)} \u2014 it is 'null' (a changed row is not found by its old meaning) or 'keep' (it is, until embedded again)`),e.columns.some(o=>o.name==="_score"&&!o.ignored)&&r.push(`${n} declares search and has a column "_score": search() answers each row with its _score \u2014 rename the column`),r}function ye(n){let e=[],t=i=>i&&e.push(i),r=new Map,a=new Map,o=(i,c,f)=>{let u=`${i}.${c}`,d=a.get(u);d?e.push(`${f} "${c}" has the name of ${d} in schema ${i}`):a.set(u,f)};for(let i of n.enums)t(g("an enum",i.name)),i.values.length===0&&e.push(`enum ${i.schema}.${i.name} has no values`),new Set(i.values).size!==i.values.length&&e.push(`enum ${i.schema}.${i.name} names a value twice`);for(let i of n.tables){let c=b(i);t(g("a schema",i.schema)),t(g("a table",i.name)),r.has(c)&&e.push(`table ${c} is declared twice`),r.set(c,i),o(i.schema,i.name,`table ${c}`);let f=new Map,u=(s,m)=>{let C=f.get(s);C?e.push(`${m} "${s}" has the name of a ${C} of table ${c}`):f.set(s,m)},d=new Map;for(let s of i.columns)t(g(`column ${c}.${s.name}`,s.name)),d.has(s.name)&&e.push(`column ${c}.${s.name} is declared twice`),d.set(s.name,s),s.fill!==void 0&&s.nullable&&e.push(`column ${c}.${s.name}: fillWith is for a column that is NOT NULL`),s.identity&&s.default!==void 0&&e.push(`column ${c}.${s.name}: an identity column has no default`),s.generated&&(s.default!==void 0||s.identity||s.fill!==void 0)&&e.push(`column ${c}.${s.name}: a generated column has no default, identity or fill`),s.generated&&!s.generated.expr.trim()&&e.push(`column ${c}.${s.name}: a generated column's expression is empty`),s.identity&&!["smallint","integer","bigint"].includes(D(s.type))&&e.push(`column ${c}.${s.name}: an identity column is smallint, integer or bigint`),s.identity&&s.nullable&&e.push(`column ${c}.${s.name}: an identity column is NOT NULL`);let h=(s,m)=>{let C=d.get(s);C?C.ignored&&e.push(`${m} names column "${s}", which is ignored`):e.push(`${m} names column "${s}", which ${c} does not have`)};for(let s of i.primaryKey)h(s,`the primary key of ${c}`);for(let s of i.primaryKey)d.get(s)?.nullable&&e.push(`column ${c}.${s} is in the primary key, so it is not nullable`);for(let s of i.uniques){t(g(`a unique constraint of ${c}`,s.name,!0)),o(i.schema,s.name,`unique constraint ${c}.${s.name}`),u(s.name,"unique constraint"),s.columns.length===0&&e.push(`unique constraint ${s.name} names no column`);for(let m of s.columns)h(m,`unique constraint ${s.name}`)}for(let s of i.indexes){t(g(`an index of ${c}`,s.name,!0)),o(i.schema,s.name,`index ${c}.${s.name}`),s.keys.length===0&&e.push(`index ${s.name} has no key`);for(let m of s.keys)"column"in m&&h(m.column,`index ${s.name}`);for(let m of s.include??[])h(m,`index ${s.name}`)}for(let s of i.checks)t(g(`a check of ${c}`,s.name,!0)),u(s.name,"check"),s.expr.trim()||e.push(`check ${s.name} has no expression`);for(let s of i.foreignKeys){t(g(`a foreign key of ${c}`,s.name,!0)),u(s.name,"foreign key"),(s.columns.length===0||s.columns.length!==s.refColumns.length)&&e.push(`foreign key ${s.name} names ${s.columns.length} column(s) and references ${s.refColumns.length}`);for(let m of s.columns)h(m,`foreign key ${s.name}`)}let E=new Set;for(let s of i.policies)t(g(`a policy of ${c}`,s.name,!0)),E.has(s.name)&&e.push(`policy ${c}.${s.name} is declared twice`),E.add(s.name),s.to.length===0&&e.push(`policy ${c}.${s.name} names no role`),s.for==="insert"&&s.using!==void 0&&e.push(`policy ${c}.${s.name}: an insert policy has only withCheck`),(s.for==="select"||s.for==="delete")&&s.withCheck!==void 0&&e.push(`policy ${c}.${s.name}: a ${s.for} policy has only using`);i.policies.length>0&&!i.rls&&e.push(`table ${c} has policies but row-level security off: they would do nothing`),i.search!==void 0&&e.push(...v(`table ${c}`,i,i.search))}for(let i of n.tables)for(let c of i.foreignKeys){let f=r.get(`${c.refSchema}.${c.refTable}`);if(!f){e.push(`foreign key ${c.name} of ${b(i)} references ${c.refSchema}.${c.refTable}, which is not declared`);continue}f.ignored&&e.push(`foreign key ${c.name} of ${b(i)} references ${b(f)}, which is ignored`);for(let u of c.refColumns)f.columns.some(d=>d.name===u&&!d.ignored)||e.push(`foreign key ${c.name} references ${b(f)}.${u}, which it does not have`)}let l=new Set;for(let i of n.raw)t(g("a raw unit",i.name)),l.has(i.name)&&e.push(`raw unit ${i.name} is declared twice`),l.add(i.name),i.sql.trim()||e.push(`raw unit ${i.name} has no SQL`),i.before&&i.table!==void 0&&e.push(`raw unit ${i.name}: before is for a schema's unit, not a table's \u2014 a table's is made after its table`);return e}function j(n){let e=t=>[...t].sort((r,a)=>r.name<a.name?-1:r.name>a.name?1:0);return{tables:[...n.tables].map(t=>({...t,schema:t.schema||"public",columns:t.columns.map(r=>({...r,type:D(r.type)})),uniques:e(t.uniques),indexes:e(t.indexes),checks:e(t.checks),foreignKeys:e(t.foreignKeys),policies:e(t.policies).map(r=>({...r,to:[...r.to].sort()}))})).sort((t,r)=>b(t)<b(r)?-1:b(t)>b(r)?1:0),enums:[...n.enums].sort((t,r)=>`${t.schema}.${t.name}`<`${r.schema}.${r.name}`?-1:1),extensions:[...new Set([...n.extensions,...n.tables.some(t=>t.search!==void 0)?[V]:[]])].sort(),raw:e(n.raw)}}function ge(n){let e=j(n),t=new Map(n.raw.map((r,a)=>[r.name,a]));return e.raw.sort((r,a)=>(t.get(r.name)??0)-(t.get(a.name)??0)),e}function O(n){return Array.isArray(n)?`[${n.map(O).join(",")}]`:n&&typeof n=="object"?`{${Object.entries(n).filter(([,t])=>t!==void 0).sort(([t],[r])=>t<r?-1:1).map(([t,r])=>`${JSON.stringify(t)}:${O(r)}`).join(",")}}`:JSON.stringify(n)}function be(n){return z("sha256").update(O(j(n))).digest("hex").slice(0,32)}var A=Symbol.for("palbase.table.meta"),x=Symbol.for("palbase.ref.target"),P=class{_def;constructor(e){if(e.trim()==="")throw new Error("foreignKey(name): a foreign key's name cannot be empty \u2014 Palbase reconciles constraints by name.");this._def={name:e,columns:[],refTable:"",refColumns:[]}}on(...e){return this._def.columns=e.map(t=>{let r=t.expr;if(r.kind!=="col")throw new Error(`foreignKey(${this._def.name}).on(): takes column references only \u2014 an expression cannot be part of a foreign key.`);return r.name}),this}references(e,...t){if(t.length===0)throw new Error(`foreignKey(${this._def.name}).references(): needs at least one target column.`);return this._def.refColumns=[...t],typeof e=="function"?(Object.defineProperty(this._def,x,{value:()=>e()[A],enumerable:!1,configurable:!0}),this):(this._def.refTable=e[A].name,Object.defineProperty(this._def,x,{value:e[A],enumerable:!1,configurable:!0}),this)}onUpdate(e){return this._def.onUpdate=e,this}onDelete(e){return this._def.onDelete=e,this}match(e){return this._def.match=e,this}index(e){if(e!==!1)throw new Error(`foreignKey(${this._def.name}).index(): only index(false) \u2014 an index is made unasked`);return this._def.index=!1,this}deferrable(e){if(e!==!1)throw new Error(`foreignKey(${this._def.name}).deferrable(): only deferrable(false) \u2014 a key with an action is deferrable unasked`);return this._def.deferrable=!1,this}};function Y(n){return new P(n)}function B(n){let e=n instanceof P?n._def:n;if(e.columns.length===0)throw new Error(`foreignKey(${e.name}): needs at least one local column \u2014 call .on(...).`);let t=typeof e[x]=="function";if(e.refColumns.length===0||e.refTable===""&&!t)throw new Error(`foreignKey(${e.name}): needs the target table and its columns \u2014 call .references(...).`);if(e.columns.length!==e.refColumns.length)throw new Error(`foreignKey(${e.name}): ${e.columns.length} local column(s) (${e.columns.join(", ")}) but ${e.refColumns.length} target column(s) (${e.refColumns.join(", ")}) \u2014 the two lists must be the same length.`);return e}var y=class n{expr;constructor(e){this.expr=e}bin(e,t){return new n({kind:"binop",op:e,left:this.expr,right:ee(t)})}eq(e){return this.bin("eq",e)}neq(e){return this.bin("neq",e)}gt(e){return this.bin("gt",e)}gte(e){return this.bin("gte",e)}lt(e){return this.bin("lt",e)}lte(e){return this.bin("lte",e)}isNull(e=!1){return new n({kind:"isNull",expr:this.expr,negated:e})}and(...e){return new n({kind:"and",parts:[this.expr,...e.map(t=>t.expr)]})}or(...e){return new n({kind:"or",parts:[this.expr,...e.map(t=>t.expr)]})}not(){return new n({kind:"not",expr:this.expr})}};function ee(n){return n instanceof y?n.expr:{kind:"lit",value:n}}function k(n,e=0,t=""){let r=`pb_e${e}`;return{col:a=>new y(e===0?{kind:"col",name:a}:{kind:"col",name:a,alias:r}),auth:{uid:()=>new y({kind:"authUid"})},lit:a=>new y({kind:"lit",value:a}),and:(...a)=>new y({kind:"and",parts:a.map(o=>o.expr)}),or:(...a)=>new y({kind:"or",parts:a.map(o=>o.expr)}),not:a=>new y({kind:"not",expr:a.expr}),existsIn:(a,o)=>{let l=a[_],i=k(Object.keys(l.columns),e+1,t),c={kind:"exists",table:l.name,alias:`pb_e${e+1}`,outer:t,predicate:o(i).expr};return Object.defineProperty(c,x,{value:l,enumerable:!1,configurable:!0}),new y(c)}}}function p(n){return`"${n.replace(/"/g,'""')}"`}var S=class{_def;constructor(e){this._def={name:e,command:"all",roles:["authenticated"],using:null,withCheck:null,permissive:!0}}for(e){return this._def.command=e,this}to(...e){return this._def.roles=e,this}using(e){return this._def.using=e instanceof y?e.expr:e,this}memberOf(e,t,r={}){if(this._def.using!==null)throw new Error(`policy(${this._def.name}).memberOf(): this policy already has a using() expression. Write one or the other \u2014 memberOf IS the using expression.`);let a=r.column??"id",o=r.userColumn??"user_id";return this._def.using=`${p(a)} IN (SELECT ${p(t)} FROM ${p(e)} WHERE ${p(o)} = (select auth.uid()))`,this}withCheck(e){return this._def.withCheck=e instanceof y?e.expr:e,this}withoutIndexes(){return this._def.indexes=!1,this}as(e){return this._def.permissive=e==="permissive",this}};function Ee(n){return new S(n)}function T(n,e){switch(n.kind){case"exists":throw new Error(`check(${e}): \`existsIn(...)\` is a subquery and PostgreSQL forbids subqueries in a CHECK constraint \u2014 express a cross-table rule as a composite FOREIGN KEY (foreignKeys), or as a guard.`);case"authUid":throw new Error(`check(${e}): \`auth.uid()\` renders as a subquery \`(SELECT auth.uid())\` and PostgreSQL forbids subqueries in a CHECK constraint \u2014 a caller-dependent rule belongs in an RLS policy, not a CHECK.`);case"binop":T(n.left,e),T(n.right,e);return;case"and":case"or":for(let t of n.parts)T(t,e);return;case"not":case"isNull":T(n.expr,e);return;default:return}}var ne={eq:"=",neq:"<>",gt:">",gte:">=",lt:"<",lte:"<="};function U(n){if(n===null)return"NULL";if(typeof n=="boolean")return n?"true":"false";if(typeof n=="number"){if(!Number.isFinite(n))throw new Error(`check(): ${n} is not an SQL number literal.`);return String(n)}return`'${n.replace(/'/g,"''")}'`}function $(n,e){switch(n.kind){case"col":return p(n.name);case"lit":return U(n.value);case"binop":{let t=ne[n.op];if(t===void 0)throw new Error(`check(${e}): \`${n.op}\` is not supported in a CHECK expression.`);return`(${$(n.left,e)} ${t} ${$(n.right,e)})`}case"and":case"or":{if(n.parts.length===0)return n.kind==="and"?"(true)":"(false)";let t=n.kind==="and"?" AND ":" OR ";return`(${n.parts.map(r=>$(r,e)).join(t)})`}case"not":return`(NOT ${$(n.expr,e)})`;case"isNull":return`(${$(n.expr,e)} IS ${n.negated?"NOT NULL":"NULL"})`;default:{let t=n.kind;throw new Error(`check(${e}): a \`${t}\` node is not one a CHECK expression takes.`)}}}function te(n,e){if(n.trim()==="")throw new Error("check(): the constraint's name cannot be empty \u2014 Palbase reconciles CHECKs by name.");return T(e.expr,n),{name:n,expr:$(e.expr,n)}}function Ce(n){if(n!==null)return typeof n=="string"?n:w(n,"")}var re={eq:"=",neq:"<>",gt:">",gte:">=",lt:"<",lte:"<="};function H(n){let e=n.indexOf(".");return e<0?`${p("public")}.${p(n)}`:`${p(n.slice(0,e))}.${p(n.slice(e+1))}`}function w(n,e){switch(n.kind){case"col":return n.alias!==void 0?`${p(n.alias)}.${p(n.name)}`:e===""?p(n.name):`${H(e)}.${p(n.name)}`;case"authUid":return"(SELECT auth.uid())";case"lit":return U(n.value);case"binop":{let t=re[n.op];if(t===void 0)throw new Error(`policy expression: \`${n.op}\` is not an operator a policy can use`);return`(${w(n.left,e)} ${t} ${w(n.right,e)})`}case"and":case"or":return n.parts.length===0?n.kind==="and"?"(true)":"(false)":`(${n.parts.map(t=>w(t,e)).join(n.kind==="and"?" AND ":" OR ")})`;case"not":return`(NOT ${w(n.expr,e)})`;case"isNull":return`(${w(n.expr,e)} IS ${n.negated?"NOT NULL":"NULL"})`;case"exists":if(n.outer==="")throw new Error("policy expression: an existsIn carries no outer table \u2014 its correlation would collapse to x = x and filter nothing");return`EXISTS (SELECT 1 FROM ${H(n.table)} ${p(n.alias)} WHERE (${w(n.predicate,n.outer)}))`}}function M(n,e){return n===""||n==="public"?e:`${n}.${e}`}var K=class{_def;constructor(e){if(e.trim()==="")throw new Error("index(name): an index's name cannot be empty \u2014 Palbase reconciles indexes by name.");this._def={name:e}}unique(){return this._def.unique=!0,this}on(...e){return this._def.columns=e.map(t=>{let r=t.expr;if(r.kind!=="col")throw new Error(`index(${this._def.name}).on(): takes column references only \u2014 for an expression, use onExpression("\u2026").`);return r.name}),this}onExpression(e){return this._def.expression=e,this}where(e){return this._def.where=e.expr,this}desc(){return this._def.sort="desc",this}asc(){return this._def.sort="asc",this}nulls(e){return this._def.nulls=e,this}include(e){return this._def.include=e,this}};function Re(n){return new K(n)}function ie(n){return n instanceof S?n._def:n}var _=Symbol.for("palbase.table.meta");function Ne(n,e){if(n.trim()==="")throw new Error("defineTable(name, \u2026): name must not be empty");let t=(e.policies?.(k(Object.keys(e.columns),0,n))??[]).map(ie),r=t.length>0||e.rls!==!1,a={};for(let[u,d]of Object.entries(e.columns)){let h=d;ce(n,u,h._def),ae(n,u,h._def,e.columns);let E=Object.create(Object.getPrototypeOf(h));Object.defineProperty(E,"_def",{value:{...h._def},enumerable:!0,writable:!0}),a[u]=E}let o={name:n,columns:a,rls:r,policies:t};if(e.primaryKey!==void 0&&(o.primaryKey=[...e.primaryKey]),e.search!==void 0&&(o.search=se(n,e.search,a,o.primaryKey)),e.unique!==void 0&&(o.unique=e.unique.map(u=>({name:u.name,columns:[...u.columns]}))),e.raw!==void 0&&e.raw.length>0){for(let u of e.raw)if(u.drop!==void 0)throw new Error(`defineTable(${JSON.stringify(n)}): raw ${JSON.stringify(u.name)} carries a drop, but a table-level raw() has no teardown \u2014 a drop is run only for a schema-level unit: defineSchema(name, { tables, raw: [raw(name, up, { drop })] })`);o.raw=e.raw.slice()}let l=typeof e.checks=="function"?e.checks(k(Object.keys(e.columns),0,n)):e.checks?.slice()??[];l.length>0&&(o.checks=l);let i=(e.indexes?.(k(Object.keys(e.columns),0,n))??[]).map(u=>u instanceof K?u._def:u);i.length>0&&(o.indexes=i);let c=(e.foreignKeys?.(k(Object.keys(e.columns),0,n))??[]).map(B);c.length>0&&(o.foreignKeys=c),e.ignored===!0&&(o.ignored=!0),le(n,o);for(let u of Object.values(a))u._def.ownerTable=o;let f={...a};return Object.defineProperty(f,_,{value:o,enumerable:!1}),f}function se(n,e,t,r){if(e===null||typeof e!="object")throw new Error(`defineTable(${JSON.stringify(n)}): search is { from: [...columns], model } \u2014 got ${e===null?"null":typeof e}`);let a={from:Array.isArray(e.from)?[...e.from]:e.from,model:e.model,dimensions:e.dimensions??q,metric:e.metric??"cosine",staleness:e.staleness??"null"},o=Object.entries(t).map(([i,c])=>({name:i,def:c._def})),l=v(`defineTable(${JSON.stringify(n)})`,{columns:o.map(({name:i,def:c})=>({name:i,type:c.type==="sql"?c.sqlType??"sql":c.type,...c.ignored?{ignored:!0}:{},...c.generated?{generated:c.generated}:{}})),primaryKey:r??o.filter(({def:i})=>i.primaryKey===!0).map(({name:i})=>i)},a);if(l.length>0)throw new Error(l.join(`
|
|
5
|
-
`));return a}var I=new WeakMap;function oe(n,e){for(let t of Object.values(n))ue(t,`table "${t.name}"`,e);for(let t of Object.values(n))for(let[r,a]of Object.entries(t.columns)){let o=a._def;if(o.selfRefColumn!==void 0){if(!(o.selfRefColumn in t.columns))throw new Error(`table "${t.name}" column "${r}": selfReferences("${o.selfRefColumn}") \u2014 no such column on this table`);o.references={table:M(e,t.name),column:o.selfRefColumn};continue}if(o.referencesThunk===void 0)continue;let l=o.referencesThunk(),i=l?._def?.ownerTable;if(i===void 0)throw new Error(`table "${t.name}" column "${r}": references(() => \u2026) must point at a column of a table declared with defineTable(...)`);let c=Object.entries(i.columns).find(([,u])=>u._def===l._def)?.[0];if(c===void 0)throw new Error(`table "${t.name}" column "${r}": the referenced column was not found on table "${i.name}"`);let f=I.get(i);if(f===void 0)throw new Error(`table "${t.name}" column "${r}": references(() => \u2026) points at table "${i.name}", which no defineSchema(...) has claimed yet \u2014 a foreign key cannot be written without its target's schema. Declare that table's schema first (one file per schema: db/public.ts, db/billing.ts \u2026, imported by this one).`);o.references={table:M(f,i.name),column:c}}}function ae(n,e,t,r){if(t.encrypted===void 0)return;let a=`${e}_lookup`,o=t.defaultValue!==void 0||t.defaultSql!==void 0||t.defaultRandom===!0||t.defaultNow===!0?"it has a default \u2014 the database would write it in plain text; give the value in the write":t.fill!==void 0?".fillWith() is SQL over the row in plain text; give the value in the write":t.primaryKey===!0?"it is the primary key \u2014 a key the database matches rows by cannot be ciphertext; keep an id, and the encrypted value beside it":t.references!==void 0||t.referencesThunk!==void 0?"it references another table \u2014 a foreign key compares values the database cannot read":t.identity!==void 0||t.generated!==void 0?"the database makes its value":t.user!==void 0?"it holds a user's id, which the platform matches rows by":t.codec!==void 0||t.transform!==void 0?"it has a codec or a transform \u2014 encryption is its transform":t.unique===!0&&!t.encrypted.lookup?".unique() needs { lookup: true } \u2014 what equality is kept by is the lookup":a in r?`the table has a column "${a}" of its own, where the lookup is kept`:null;if(o!==null)throw new Error(`defineTable(${JSON.stringify(n)}): column "${e}" \u2014 .encrypted(): ${o}`)}function le(n,e){let t=a=>e.columns[a]?._def.encrypted,r=a=>{throw new Error(`defineTable(${JSON.stringify(n)}): ${a}`)};for(let a of e.primaryKey??[])t(a)&&r(`the primary key names "${a}", which is .encrypted() \u2014 keep an id, and the encrypted value beside it`);for(let a of e.unique??[])for(let o of a.columns){let l=t(o);l&&!l.lookup&&r(`unique ${JSON.stringify(a.name)} names "${o}", which is .encrypted() without { lookup: true } \u2014 what equality is kept by is the lookup`)}for(let a of e.indexes??[]){let o=JSON.stringify([a.keys??[],a.columns??[]]);for(let l of Object.keys(e.columns))t(l)&&o.includes(JSON.stringify(l))&&r(`index ${JSON.stringify(a.name)} names "${l}", which is .encrypted() \u2014 an index of ciphertext orders nothing; equality is found by { lookup: true }, which is indexed`)}for(let a of e.search?.from??[])t(a)&&r(`search reads "${a}", which is .encrypted() \u2014 its text is not in the database to embed`)}function ce(n,e,t){if(t.fill===void 0)return;let r=t.nullable===!0?"the column is .nullable() \u2014 .fillWith() fills a NOT NULL column; keep one of the two":t.ignored===!0?"the column is .ignored() \u2014 this release never writes it, so there is nothing to fill for it":t.defaultValue!==void 0||t.defaultRandom===!0||t.defaultNow===!0||t.defaultSql!==void 0?"the column has a default \u2014 the default already fills every row; keep one of the two":null;if(r!==null)throw new Error(`defineTable(${JSON.stringify(n)}): column "${e}" \u2014 .fillWith(): ${r}`)}function Oe(n,e){if(typeof n!="string")throw new Error(`defineSchema(...) takes the schema NAME first: defineSchema("public", { tables: [todos, lists] }). The one-argument form defineSchema({ tables: { \u2026 } }) is gone \u2014 a schema that does not say its own name cannot be told apart from another schema's table of the same name, which is what a cross-schema foreign key has to do. Migrate db/public.ts (one file per schema: db/public.ts, db/billing.ts \u2026): name the schema, and lift each table key onto the table itself with defineTable("todos", { columns: { \u2026 } }), then list the tables in the array.`);if(n.trim()==="")throw new Error("defineSchema(name, \u2026): name must not be empty");let t={};for(let l of e.tables){let i=l[_];if(t[i.name]!==void 0)throw new Error(`defineSchema("${n}"): two tables declare the name "${i.name}" \u2014 table names must be unique within a schema`);t[i.name]=i,I.set(i,n)}oe(t,n);let r=[...new Set(e.extensions??[])],a=(e.raw??[]).slice(),o=new Set;for(let l of a){if(typeof l.name!="string"||l.name.trim()==="")throw new Error(`defineSchema("${n}"): a raw unit needs a name`);if(o.has(l.name))throw new Error(`defineSchema("${n}"): two raw units are named "${l.name}" \u2014 a unit's name identifies it`);o.add(l.name)}return{name:n,tables:t,extensions:r,raw:a}}function ue(n,e,t){let r=(l,i)=>{let c=l[x];if(c===void 0)return;let f;try{f=typeof c=="function"?c():c}catch(h){throw new Error(`${e}: ${i} names its target through a callback that failed: ${h.message}`)}if(f==null||typeof f.name!="string")throw new Error(`${e}: ${i}'s callback does not answer a table \u2014 write references(() => otherTable, \u2026)`);let u=I.get(f);if(u===void 0)throw new Error(`${e}: ${i} points at table "${f.name}", which no defineSchema(...) has claimed yet \u2014 a cross-table reference cannot be written without its target's schema. Declare that table's schema first (one file per schema: db/public.ts, db/billing.ts \u2026, imported by this one).`);let d=M(u,f.name);l.refTable!==void 0&&(l.refTable=d),l.table!==void 0&&(l.table=d)};for(let l of n.foreignKeys??[])r(l,`foreignKey("${l.name}")`);let a=`${t}.${n.name}`,o=(l,i)=>{if(!(l==null||typeof l=="string"))switch(l.kind){case"exists":r(l,i),l.table.includes(".")||(l.table=`public.${l.table}`),l.outer=a,o(l.predicate,i);return;case"binop":o(l.left,i),o(l.right,i);return;case"and":case"or":for(let c of l.parts)o(c,i);return;case"not":case"isNull":o(l.expr,i);return;default:return}};for(let l of n.policies)o(l.using,`policy("${l.name}")`),o(l.withCheck,`policy("${l.name}")`);for(let l of n.indexes??[])o(l.where,`index("${l.name}")`)}export{de as a,me as b,L as c,pe as d,b as e,D as f,he as g,G as h,v as i,ye as j,j as k,ge as l,be as m,P as n,Y as o,M as p,K as q,Re as r,_ as s,Ne as t,Oe as u,y as v,k as w,S as x,Ee as y,te as z,Ce as A};
|