palgres 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +2 -2
  3. package/dist/chunks/JJVO5DTA.js +2 -0
  4. package/dist/chunks/JZLQMXDV.js +2 -0
  5. package/dist/chunks/XZNL22V6.js +5 -0
  6. package/dist/dsl.js +2 -0
  7. package/dist/index.js +372 -0
  8. package/dist/testing.js +2 -0
  9. package/package.json +32 -4
  10. package/types/internal/src/data.d.ts +15 -0
  11. package/types/palgres/src/adopt.d.ts +17 -0
  12. package/types/palgres/src/connect.d.ts +37 -0
  13. package/types/palgres/src/dsl/columns.d.ts +388 -0
  14. package/types/palgres/src/dsl/foreign-key.d.ts +33 -0
  15. package/types/palgres/src/dsl/index.d.ts +11 -0
  16. package/types/palgres/src/dsl/policy.d.ts +181 -0
  17. package/types/palgres/src/dsl/raw.d.ts +43 -0
  18. package/types/palgres/src/dsl/schema.d.ts +288 -0
  19. package/types/palgres/src/dsl/to-model.d.ts +6 -0
  20. package/types/palgres/src/forget.d.ts +12 -0
  21. package/types/palgres/src/index.d.ts +15 -0
  22. package/types/palgres/src/introspect.d.ts +17 -0
  23. package/types/palgres/src/migrate.d.ts +189 -0
  24. package/types/palgres/src/model.d.ts +181 -0
  25. package/types/palgres/src/plan.d.ts +107 -0
  26. package/types/palgres/src/pull.d.ts +13 -0
  27. package/types/palgres/src/query/admission.d.ts +80 -0
  28. package/types/palgres/src/query/catalog.d.ts +50 -0
  29. package/types/palgres/src/query/client.d.ts +154 -0
  30. package/types/palgres/src/query/data-mark.d.ts +7 -0
  31. package/types/palgres/src/query/diagnostics.d.ts +76 -0
  32. package/types/palgres/src/query/driver.d.ts +299 -0
  33. package/types/palgres/src/query/env-gen.d.ts +14 -0
  34. package/types/palgres/src/query/errors.d.ts +168 -0
  35. package/types/palgres/src/query/guards.d.ts +62 -0
  36. package/types/palgres/src/query/index.d.ts +76 -0
  37. package/types/palgres/src/query/options.d.ts +75 -0
  38. package/types/palgres/src/query/relations.d.ts +15 -0
  39. package/types/palgres/src/query/repository.d.ts +42 -0
  40. package/types/palgres/src/query/request.d.ts +54 -0
  41. package/types/palgres/src/query/search.d.ts +55 -0
  42. package/types/palgres/src/query/surface.d.ts +9 -0
  43. package/types/palgres/src/query/tx-plan.d.ts +229 -0
  44. package/types/palgres/src/query/typed.d.ts +537 -0
  45. package/types/palgres/src/query/unique.d.ts +16 -0
  46. package/types/palgres/src/search-worker.d.ts +36 -0
  47. package/types/palgres/src/search.d.ts +107 -0
  48. package/types/palgres/src/testing/fake.d.ts +22 -0
  49. package/types/palgres/src/testing/index.d.ts +2 -0
  50. package/types/palgres/src/units.d.ts +24 -0
@@ -0,0 +1,2 @@
1
+ /*! palgres 0.2.0 — 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/JJVO5DTA.js";import"./chunks/XZNL22V6.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
@@ -1,6 +1,34 @@
1
1
  {
2
2
  "name": "palgres",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Palgres: the typed Postgres layer of Palbase's DB machines — schema, migrations and queries.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "homepage": "https://palbase.studio",
7
+ "author": "Palbase",
8
+ "type": "module",
9
+ "engines": {
10
+ "node": ">=26"
11
+ },
12
+ "sideEffects": false,
13
+ "exports": {
14
+ ".": {
15
+ "types": "./types/palgres/src/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./dsl": {
19
+ "types": "./types/palgres/src/dsl/index.d.ts",
20
+ "default": "./dist/dsl.js"
21
+ },
22
+ "./testing": {
23
+ "types": "./types/palgres/src/testing/index.d.ts",
24
+ "default": "./dist/testing.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "types",
31
+ "LICENSE",
32
+ "README.md"
33
+ ]
34
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The objects that are one value: a Date and binary data, which a filter
3
+ * compares and a column stores whole. Every other object is marked — the
4
+ * list says what is a value, never what is data, so an object a validator
5
+ * or a transform built in a shape nobody listed (a class's instance, a
6
+ * foreign prototype) is data, not a way around the mark.
7
+ */
8
+ export declare function isValue(v: object): boolean;
9
+ /** Marks every object and array in `value` as data, but a Date and binary data; returns it. */
10
+ export declare function markData<T>(value: T): T;
11
+ export declare function isData(v: unknown): boolean;
12
+ /** The values of each row marked — not the row: its keys are the columns the code selected. */
13
+ export declare function markRows<T>(rows: T): T;
14
+ /** A copy the engine makes of a marked object stays data: a plan's filter is copied twice before it compiles. */
15
+ export declare function carryMark<T extends object>(from: unknown, to: T): T;
@@ -0,0 +1,17 @@
1
+ import type { Introspected } from './introspect.js';
2
+ import { type Schema } from './model.js';
3
+ export interface Baseline {
4
+ schema: Schema;
5
+ /** What adoption itself changes before the migration: tables whose RLS is FORCEd on their owner. */
6
+ unforce: string[];
7
+ refusals: string[];
8
+ /** Left as it is: the database's tables outside the schema and what the model cannot say. */
9
+ unmanaged: string[];
10
+ }
11
+ export declare function adoptionBaseline(real: Introspected, shadow: Introspected, declaredSchema: Schema): Baseline;
12
+ /**
13
+ * Where a database differs from the declared schema, both read the same way:
14
+ * one line per table, column, constraint, index or policy of a declared table
15
+ * that the database does not hold as the shadow does. [] when it is the schema.
16
+ */
17
+ export declare function drift(real: Introspected, shadow: Introspected): string[];
@@ -0,0 +1,37 @@
1
+ import { type Schema } from './model.js';
2
+ /** What an app connection says about itself in every transaction. */
3
+ export interface Binding {
4
+ version: number;
5
+ /** Reads the tables directly (true), or through the version's views. */
6
+ direct: boolean;
7
+ searchPath: string;
8
+ applicationName: string;
9
+ /** Where a table of Postgres schema `name` is read by this version: the schema itself, or its version's views. */
10
+ schema(name: string): string;
11
+ }
12
+ export declare function binding(version: number, direct: boolean): Binding;
13
+ export declare class VersionNotMigrated extends Error {
14
+ readonly digest: string;
15
+ constructor(d: string);
16
+ }
17
+ interface Query {
18
+ unsafe(sql: string, params?: unknown[]): PromiseLike<unknown>;
19
+ }
20
+ /**
21
+ * The version of `schema`, once it can serve: active, its views made. Waits
22
+ * up to `waitMs` (default 0) for a start still running.
23
+ */
24
+ export declare function bind(sql: Query, schema: Schema, options?: {
25
+ waitMs?: number;
26
+ }): Promise<Binding>;
27
+ /**
28
+ * The statement a transaction of the app starts with, as text the driver
29
+ * sends in BEGIN's own round trip: role and claims for this transaction
30
+ * alone, the version's name for the connection (it outlives the transaction:
31
+ * an idle connection still says which version holds it). A SELECT list, so
32
+ * what a transaction adds (its timeouts, its cancel ticket) is more columns.
33
+ * The values are literals (sql.ts' textLiteral): a statement sent with BEGIN
34
+ * takes no parameters.
35
+ */
36
+ export declare function bindStatement(b: Binding, role: string, claims: Record<string, unknown>): string;
37
+ export {};
@@ -0,0 +1,388 @@
1
+ /** On delete action for foreign key references. */
2
+ export type OnDeleteAction = 'cascade' | 'set null' | 'set default' | 'restrict' | 'no action';
3
+ /** What a foreign key does when the row it points at changes its key. */
4
+ export type OnUpdateAction = OnDeleteAction;
5
+ /**
6
+ * What happens to a user's rows when the user is deleted: they go (`cascade`)
7
+ * or forget the user (`set null`). Nothing else: a deletion is never blocked.
8
+ */
9
+ export type AuthUserOnDelete = Extract<OnDeleteAction, 'cascade' | 'set null'>;
10
+ export type ArrayElementType = 'uuid' | 'text' | 'integer' | 'bigint' | 'numeric' | 'boolean' | 'timestamp' | 'date' | 'enum';
11
+ /** Column type identifiers. */
12
+ export type ColumnType = 'uuid' | 'text' | 'integer' | 'bigint' | 'numeric' | 'boolean' | 'timestamp' | 'date' | 'jsonb' | 'enum' | 'sql' | `${ArrayElementType}[]`;
13
+ export type ColumnWrite = 'write' | 'readonly';
14
+ /** Base column definition shared by all column types. */
15
+ export interface ColumnDef {
16
+ type: ColumnType;
17
+ nullable: boolean;
18
+ primaryKey: boolean;
19
+ defaultValue?: unknown;
20
+ defaultRandom?: boolean;
21
+ defaultNow?: boolean;
22
+ /** A default in SQL, written as it is: what a database taken over has (nextval(…), CURRENT_DATE + 1). */
23
+ defaultSql?: string;
24
+ references?: {
25
+ table: string;
26
+ column: string;
27
+ };
28
+ /** Pending FK target, resolved by `defineSchema` once every binding exists. */
29
+ referencesThunk?: () => ColumnBuilder;
30
+ /** FK onto THIS table — no thunk needed, the target is the declaring table. */
31
+ selfRefColumn?: string;
32
+ /**
33
+ * The column holds a user's id (ownedByUser(), userRef()). Users live in
34
+ * the platform, not in this database, so there is no foreign key: when a
35
+ * user is deleted, the app's rows of them go (`cascade`) or forget them
36
+ * (`set null`) — the backend does it on the platform's user.deleted.
37
+ */
38
+ user?: {
39
+ onDelete: AuthUserOnDelete;
40
+ owns: boolean;
41
+ };
42
+ /** Explicit name for the FORWARD relation (child → parent), when the one
43
+ * derived from the column would be ambiguous or unclear. */
44
+ refAs?: string;
45
+ /**
46
+ * Explicit name for the REVERSE relation (parent → children).
47
+ *
48
+ * Separate from {@link refAs} because the two directions are different facts:
49
+ * the forward name describes the parent this row points at (`author`), the
50
+ * reverse one describes the rows hanging off the parent (`posts`). One option
51
+ * naming both made an ordinary schema undeclarable — `posts.author_id` and
52
+ * `comments.author_id` both named `{ as: "author" }` collided on `users`, and
53
+ * the refusal asked for the `{ as }` they had both already written.
54
+ */
55
+ reverseAs?: string;
56
+ /** The table this column belongs to; set by `defineTable`. */
57
+ ownerTable?: {
58
+ name: string;
59
+ columns: Record<string, ColumnBuilder>;
60
+ };
61
+ /**
62
+ * The name this column used to have. A diff cannot tell a rename from a drop and
63
+ * an add — both leave one name gone and another present — so the intent has to be
64
+ * declared. Without it, renaming a column loses its data.
65
+ */
66
+ renamedFrom?: string;
67
+ ignored?: boolean;
68
+ /**
69
+ * The per-row fill of a NOT NULL column (`.fillWith()`, D-1): one SQL scalar
70
+ * expression over this row's columns. The deploy fills every row that does not
71
+ * supply the column with it — the rows already in the table and every write of
72
+ * a release that does not know the column yet — and only then makes it NOT
73
+ * NULL. Absent when the column declares none, so the wire stays byte-identical.
74
+ */
75
+ fill?: string;
76
+ onDeleteAction?: OnDeleteAction;
77
+ onUpdateAction?: OnUpdateAction;
78
+ index?: boolean;
79
+ enumName?: string;
80
+ enumValues?: string[];
81
+ unique?: boolean;
82
+ /**
83
+ * The value is written by the DATABASE — a trigger, a rule, an identity — not by
84
+ * the author and not by a DEFAULT this schema declares. It makes the column
85
+ * optional on INSERT without putting a DEFAULT in the DDL.
86
+ *
87
+ * Before this existed the only way to keep a trigger-filled column off the
88
+ * INSERT type was to give it a fake `default()`: a value the schema claimed to
89
+ * write and the trigger immediately overwrote. That made the schema lie about
90
+ * its own data.
91
+ */
92
+ dbAssigned?: boolean;
93
+ precision?: number;
94
+ scale?: number;
95
+ sqlType?: string;
96
+ identity?: 'always' | 'byDefault';
97
+ /** .generated(expr): the column is `expr` over the row — computed when read (virtual) or on write (stored). */
98
+ generated?: {
99
+ expr: string;
100
+ stored: boolean;
101
+ };
102
+ codec?: CodecName;
103
+ transform?: ColumnTransform;
104
+ }
105
+ interface ColumnTransform<T = unknown> {
106
+ fromDb: (value: unknown) => T;
107
+ toDb: (value: T) => unknown;
108
+ }
109
+ export type CodecName = 'number' | 'decimal';
110
+ export declare const CODECS: Record<CodecName, {
111
+ fromDb: (value: unknown) => unknown;
112
+ toDb: (value: unknown) => unknown;
113
+ tsType: 'number' | 'string';
114
+ }>;
115
+ declare const __colKind: unique symbol;
116
+ declare const __colNullable: unique symbol;
117
+ declare const __colHasDefault: unique symbol;
118
+ declare const __colEnumValues: unique symbol;
119
+ declare const __colPayload: unique symbol;
120
+ declare const __colTransform: unique symbol;
121
+ declare const __colWrite: unique symbol;
122
+ /**
123
+ * Fluent column builder with phantom type params:
124
+ * K — ColumnType literal (e.g. "text", "integer")
125
+ * N — boolean: true when nullable() has been called last (false = NOT NULL)
126
+ * D — boolean: true when a default has been set
127
+ * E — enum value union (never for non-enum columns)
128
+ * P — jsonb payload shape (unknown unless jsonb<T>() supplied one)
129
+ * T — transform target type (`never` when the column declares no transform;
130
+ * `never` is the sentinel because it is the only type that survives
131
+ * `[T] extends [never]` and never collides with a real target type)
132
+ *
133
+ * All six params have defaults so bare `ColumnBuilder` (no args) still
134
+ * satisfies `Record<string, ColumnBuilder>` in schema.ts without modification.
135
+ *
136
+ * The six `declare readonly` brand fields carry the phantom types into the
137
+ * structural shape so that conditional types like ColValue<C> can discriminate
138
+ * on K without requiring runtime values on those fields.
139
+ */
140
+ export declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends boolean = boolean, D extends boolean = boolean, E = unknown, P = unknown, T = unknown, W extends ColumnWrite = ColumnWrite> {
141
+ readonly [__colKind]: K;
142
+ readonly [__colNullable]: N;
143
+ readonly [__colHasDefault]: D;
144
+ readonly [__colEnumValues]: E;
145
+ readonly [__colPayload]: P;
146
+ readonly [__colTransform]: T;
147
+ readonly [__colWrite]: W;
148
+ readonly _def: ColumnDef;
149
+ constructor(type: K, existingDef?: ColumnDef);
150
+ /** Mark this column as the primary key. */
151
+ primaryKey(): ColumnBuilder<K, N, D, E, P, T, W>;
152
+ /** Mark this column as NOT NULL (default). */
153
+ notNull(): ColumnBuilder<K, false, D, E, P, T, W>;
154
+ /** Allow NULL values. */
155
+ nullable(): ColumnBuilder<K, true, D, E, P, T, W>;
156
+ /** Set a default value. */
157
+ default(value: unknown): ColumnBuilder<K, N, true, E, P, T, W>;
158
+ /** UUID: generate a random default (gen_random_uuid()). */
159
+ defaultRandom(): ColumnBuilder<K, N, true, E, P, T, W>;
160
+ /** Timestamp: default to now(). */
161
+ defaultNow(): ColumnBuilder<K, N, true, E, P, T, W>;
162
+ /**
163
+ * A default the database computes, in SQL as it is: `nextval('items_id_seq'::regclass)`,
164
+ * `CURRENT_DATE + 30`. What `palgres pull` writes for a default the other
165
+ * builders cannot say; a value the schema itself gives is `default()`.
166
+ */
167
+ defaultSql(expr: string): ColumnBuilder<K, N, true, E, P, T, W>;
168
+ /**
169
+ * The DATABASE assigns this column's value — a trigger, a rule, an identity.
170
+ *
171
+ * The column becomes optional on INSERT (the author has nothing to send) while
172
+ * the DDL stays free of a DEFAULT this schema would not honour. It is NOT
173
+ * `default()`: that declares a value the schema promises to write.
174
+ *
175
+ * This is for a value something ELSE writes — a trigger in a raw() unit.
176
+ */
177
+ dbAssigned(): ColumnBuilder<K, N, true, E, P, T, W>;
178
+ array(): ColumnBuilder<`${K & ArrayElementType}[]`, N, D, K extends 'enum' ? E : never, P, T, W>;
179
+ identity(): ColumnBuilder<K, false, true, E, P, T, 'readonly'>;
180
+ identity(opts: {
181
+ always: true;
182
+ }): ColumnBuilder<K, false, true, E, P, T, 'readonly'>;
183
+ identity(opts: {
184
+ always: false;
185
+ }): ColumnBuilder<K, false, true, E, P, T, W>;
186
+ /**
187
+ * The column is `expr`, an SQL expression over this row's other columns —
188
+ * `GENERATED ALWAYS AS (expr)`. Off every insert and update shape: Postgres
189
+ * refuses a value the application sends. VIRTUAL by default (Postgres 18):
190
+ * computed when read, and added to a table that holds rows at once.
191
+ * `{ stored: true }` keeps the value on disk, written with the row — what a
192
+ * search vector an index reads takes; added to a table with rows, Postgres
193
+ * would rewrite the table, which a deploy refuses.
194
+ */
195
+ generated(expr: string, opts?: {
196
+ stored?: boolean;
197
+ }): ColumnBuilder<K, N, true, E, P, T, 'readonly'>;
198
+ fillWith(expr: string): ColumnBuilder<K, N, D, E, P, T, W>;
199
+ /** Add a foreign key reference. */
200
+ /**
201
+ * Declares that this column used to be called `previous`.
202
+ *
203
+ * A schema diff sees one name gone and another present; it cannot know whether
204
+ * you renamed a column or dropped one and added another, and the two are very
205
+ * different — the second loses every value. Saying so here turns the plan into
206
+ * `ALTER TABLE … RENAME COLUMN` instead.
207
+ *
208
+ * Once the rename has been applied the annotation is inert (the old name is no
209
+ * longer there to rename), so it can be deleted at your leisure.
210
+ */
211
+ renamedFrom(previous: string): ColumnBuilder<K, N, D, E, P, T, W>;
212
+ /**
213
+ * See {@link ColumnDef.ignored}.
214
+ *
215
+ * COPIES the def rather than mutating it. The constructor takes an existing
216
+ * def BY REFERENCE, so every builder derived from another shares one object —
217
+ * `const a = slug.unique()` leaves `a._def === slug._def`. An in-place
218
+ * `ignored = true` therefore marks every column sharing that def, including
219
+ * one another table actively reads, and the gate would let THAT column be
220
+ * dropped. Measured before this copy existed.
221
+ *
222
+ * The aliasing is older than this method and other fields leak through it too.
223
+ * The reason this one cannot wait: every other leak produces a VISIBLE schema
224
+ * difference — the plan shows it, the DDL shows it. This one is invisible by
225
+ * design (no DDL, no diff, no plan line), so its only effect is to disarm a
226
+ * safety gate in silence.
227
+ */
228
+ ignored(): ColumnBuilder<K, N, D, E, P, T, W>;
229
+ /**
230
+ * Foreign key onto another table's column.
231
+ *
232
+ * The target is a THUNK, not a direct reference. In a cycle (`x → y`, `y → x`)
233
+ * the second table does not exist yet when the first is built; a direct
234
+ * reference makes TypeScript chase its own tail (TS7022 — measured, and making
235
+ * the return type independent of the target does NOT help). The thunk is
236
+ * invoked in `defineSchema`, where every binding exists and every table
237
+ * already knows its name.
238
+ *
239
+ * In a cycle, ONE side needs an explicit return type:
240
+ * `references((): AnyColumn => y.id)`. One side is enough — measured.
241
+ * For a self-reference use `selfReferences(column)`: no thunk, no annotation.
242
+ *
243
+ * `as` names the FORWARD relation (`author_id` → `author` by default);
244
+ * `reverseAs` names the REVERSE one on the parent, whose default is this
245
+ * table's own name (`users.posts`). Two foreign keys from one table onto one
246
+ * parent therefore need a `reverseAs` on at least one of them — the reverse
247
+ * names would otherwise both be this table's name.
248
+ */
249
+ references(target: () => AnyColumn, opts?: {
250
+ as?: string;
251
+ reverseAs?: string;
252
+ onDelete?: OnDeleteAction;
253
+ /** ON UPDATE — what Prisma writes for every relation (cascade). Default: no action. */
254
+ onUpdate?: OnUpdateAction;
255
+ index?: boolean;
256
+ }): ColumnBuilder<K, N, D, E, P, T, W>;
257
+ /**
258
+ * Foreign key onto THIS table (`parent_id → id`) — category trees, comment
259
+ * replies, org charts.
260
+ *
261
+ * No thunk and no type annotation: the target table is the one being declared,
262
+ * so there is nothing to defer and nothing for TypeScript to chase in a circle.
263
+ * Drizzle forces an explicit `(): AnyPgColumn =>` here because its reference
264
+ * always goes through a callback; measured, we do not need one.
265
+ */
266
+ selfReferences(column: string, opts?: {
267
+ as?: string;
268
+ onDelete?: OnDeleteAction;
269
+ }): ColumnBuilder<K, N, D, E, P, T, W>;
270
+ /** Set the ON DELETE action for a foreign key reference. */
271
+ onDelete(action: OnDeleteAction): ColumnBuilder<K, N, D, E, P, T, W>;
272
+ /** Add a single-column UNIQUE constraint. */
273
+ unique(): ColumnBuilder<K, N, D, E, P, T, W>;
274
+ /**
275
+ * Declare how this column's value is projected in and out of the process.
276
+ *
277
+ * The DDL does not move: `numeric` stays `numeric`, and the driver still hands
278
+ * back what Postgres sent. What changes is the type the row surface exposes —
279
+ * it becomes `Target`:
280
+ *
281
+ * amount: numeric().transform<number>({ fromDb: Number, toDb: String })
282
+ *
283
+ * `numeric` surfacing as `string` is CORRECT (a JS number cannot hold
284
+ * arbitrary precision), and that is exactly why this exists: application code
285
+ * that does arithmetic on the column otherwise rewrites the same
286
+ * `Number(row.amount)` / `String(x)` pair in every controller that touches it,
287
+ * and each rewrite is a place the two directions can drift apart.
288
+ *
289
+ * A transform is a PROJECTION, never a constraint: it lives only in this
290
+ * process, so it can neither validate nor migrate what is stored.
291
+ */
292
+ asNumber(): ColumnBuilder<K, N, D, E, P, number, W>;
293
+ asDecimal(): ColumnBuilder<K, N, D, E, P, string, W>;
294
+ private withCodec;
295
+ transform<Target>(fns: ColumnTransform<Target>): ColumnBuilder<K, N, D, E, P, Target, W>;
296
+ }
297
+ export type ColValue<C> = C extends ColumnBuilder<ColumnType, infer N, boolean, unknown, unknown, infer T> ? [unknown] extends [T] ? ColStoredValue<C> : N extends true ? T | null : T : never;
298
+ type ArrayElementValue<K, E> = K extends 'integer[]' ? number : K extends 'boolean[]' ? boolean : K extends 'enum[]' ? E : string;
299
+ /** The value as the DATABASE hands it over — the branch table above, before any
300
+ * transform. This is what a column's `fromDb` receives. */
301
+ type ColStoredValue<C> = C extends ColumnBuilder<infer K extends `${ArrayElementType}[]`, infer N, infer _D, infer E, infer _P> ? N extends true ? ArrayElementValue<K, E>[] | null : ArrayElementValue<K, E>[] : C extends ColumnBuilder<'sql', infer N, infer _D, infer _E, infer P> ? N extends true ? P | null : P : C extends ColumnBuilder<'uuid' | 'text' | 'timestamp' | 'bigint' | 'numeric' | 'date', infer N, infer _D, infer _E, infer _P> ? N extends true ? string | null : string : C extends ColumnBuilder<'integer', infer N, infer _D, infer _E, infer _P> ? N extends true ? number | null : number : C extends ColumnBuilder<'boolean', infer N, infer _D, infer _E, infer _P> ? N extends true ? boolean | null : boolean : C extends ColumnBuilder<'jsonb', infer N, infer _D, infer _E, infer P> ? N extends true ? P | null : P : C extends ColumnBuilder<'enum', infer N, infer _D, infer E, infer _P> ? N extends true ? E | null : E : never;
302
+ /**
303
+ * True when a column is optional on INSERT:
304
+ * - nullable columns (N = true) — the DB allows NULL so the field may be omitted
305
+ * - columns with a default (D = true) — the DB fills in the value when absent
306
+ */
307
+ export type ColIsOptionalOnInsert<C> = C extends ColumnBuilder<ColumnType, true, boolean, unknown, unknown, unknown> ? true : C extends ColumnBuilder<ColumnType, boolean, true, unknown, unknown, unknown> ? true : false;
308
+ /** Create a UUID column. */
309
+ export declare function uuid(): ColumnBuilder<'uuid', false, false, never>;
310
+ /** Create a TEXT column. */
311
+ export declare function text(): ColumnBuilder<'text', false, false, never>;
312
+ /** Create an INTEGER column. Emits int4 (max ~2.1B). */
313
+ export declare function integer(): ColumnBuilder<'integer', false, false, never>;
314
+ /**
315
+ * Create a BIGINT column (Postgres int8, max ~9.2×10^18).
316
+ * Surfaces as `string` in row/insert types — JS number loses precision past 2^53
317
+ * and pgx/PostgREST serialize int8 as a JSON string. Use BigInt(row.column) in app code.
318
+ */
319
+ export declare function bigint(): ColumnBuilder<'bigint', false, false, never>;
320
+ /**
321
+ * Create a NUMERIC column (Postgres `numeric`/`decimal`, arbitrary precision).
322
+ * For exact fractional values (money with cents as a decimal, rates, weights)
323
+ * where int4/int8 don't fit. Surfaces as `string` in row/insert types — JS
324
+ * number can't hold arbitrary-precision decimals without rounding, and
325
+ * pgx/PostgREST serialize numeric as a JSON string. Parse with a decimal lib
326
+ * (or BigInt for scaled integers) in app code.
327
+ */
328
+ export declare function numeric(opts?: {
329
+ precision: number;
330
+ scale?: number;
331
+ }): ColumnBuilder<'numeric', false, false, never>;
332
+ export declare function date(): ColumnBuilder<'date', false, false, never>;
333
+ export declare function sqlType<const S extends string>(type: S): ColumnBuilder<'sql', false, false, never, SqlTypeValue<S>>;
334
+ export declare const SQL_NUMBER_TYPES: ReadonlySet<string>;
335
+ export declare const SQL_JSON_TYPES: ReadonlySet<string>;
336
+ /** What an sqlType() column surfaces as, from its Postgres type. */
337
+ export type SqlTypeValue<S extends string> = S extends 'smallint' | 'real' | 'double precision' ? number : S extends 'json' ? unknown : string;
338
+ /** Create a BOOLEAN column. */
339
+ export declare function boolean(): ColumnBuilder<'boolean', false, false, never>;
340
+ /** Create a TIMESTAMP column. */
341
+ export declare function timestamp(): ColumnBuilder<'timestamp', false, false, never>;
342
+ /**
343
+ * Create a JSONB column. Pass a payload type to make the generated row/insert
344
+ * type concrete instead of `unknown`:
345
+ *
346
+ * tags: jsonb<string[]>() // row.tags: string[]
347
+ * meta: jsonb<{ tier: string }>() // row.meta: { tier: string }
348
+ * raw: jsonb() // row.raw: unknown (back-compat)
349
+ *
350
+ * The runtime accepts a plain JS object/array directly (no JSON.stringify); the
351
+ * generic only refines the TYPE the env codegen emits.
352
+ */
353
+ export declare function jsonb<T = unknown>(): ColumnBuilder<'jsonb', false, false, never, T>;
354
+ /**
355
+ * Create an ENUM column.
356
+ * @param name The PostgreSQL enum type name (used in DDL).
357
+ * @param values A readonly tuple of valid string values — kept `const` so the
358
+ * union `V[number]` is as narrow as possible.
359
+ */
360
+ export declare function enumType<const V extends readonly string[]>(name: string, values: V): ColumnBuilder<'enum', false, false, V[number]>;
361
+ /**
362
+ * Any column, whatever its type parameters.
363
+ *
364
+ * Exported so a cycle can be broken from ONE side:
365
+ * `references((): AnyColumn => y.id)`.
366
+ */
367
+ export type AnyColumn = ColumnBuilder;
368
+ /**
369
+ * The column that OWNS this row: the id of the user it belongs to — text, NOT
370
+ * NULL. When the user is deleted, their rows go.
371
+ *
372
+ * At most ONE per table: which column's user takes the row with them must not
373
+ * depend on the order columns are declared in.
374
+ *
375
+ * For a column that merely POINTS at a user without owning the row
376
+ * (`created_by`, `edited_by`), use `userRef({ onDelete })`.
377
+ */
378
+ export declare function ownedByUser(): ColumnBuilder<'text', false, false, never>;
379
+ /**
380
+ * The id of a user the row points at without belonging to them
381
+ * (`created_by`, `edited_by`). When that user is deleted the row goes
382
+ * (`cascade`) or forgets them (`set null`, on a nullable column).
383
+ */
384
+ export declare function userRef(opts: {
385
+ onDelete: AuthUserOnDelete;
386
+ as?: string;
387
+ }): ColumnBuilder<'text', boolean, false, never>;
388
+ export {};
@@ -0,0 +1,33 @@
1
+ import type { PolicyExprRef } from "./policy.js";
2
+ import type { ColumnMap, TableHandle } from "./schema.js";
3
+ export declare const REF_TARGET: unique symbol;
4
+ export type FkAction = "cascade" | "restrict" | "set null" | "set default" | "no action";
5
+ export type FkMatch = "simple" | "full";
6
+ export interface ForeignKeyDef {
7
+ name: string;
8
+ columns: string[];
9
+ refTable: string;
10
+ refColumns: string[];
11
+ onUpdate?: FkAction;
12
+ onDelete?: FkAction;
13
+ match?: FkMatch;
14
+ /** Only `false`: no index derived for a one-column key (the column's references() has the same option). */
15
+ index?: false;
16
+ }
17
+ export declare class ForeignKeyBuilder {
18
+ readonly _def: ForeignKeyDef;
19
+ constructor(name: string);
20
+ on(...cols: PolicyExprRef[]): this;
21
+ references<C extends ColumnMap, N extends string>(table: TableHandle<C, N> | (() => TableHandle<C, N>), ...cols: (Extract<keyof C, string>)[]): this;
22
+ onUpdate(action: FkAction): this;
23
+ onDelete(action: FkAction): this;
24
+ match(m: FkMatch): this;
25
+ /**
26
+ * `.index(false)`: Palgres indexes a one-column foreign key's column unasked
27
+ * (as <table>_<column>_idx); this one stays without. What a database taken
28
+ * over had: its key unindexed, or indexed by an index of its own.
29
+ */
30
+ index(on: false): this;
31
+ }
32
+ export declare function foreignKey(name: string): ForeignKeyBuilder;
33
+ export declare function toForeignKeyDef(b: ForeignKeyBuilder | ForeignKeyDef): ForeignKeyDef;
@@ -0,0 +1,11 @@
1
+ export { bigint, boolean, ColumnBuilder, date, enumType, integer, jsonb, numeric, ownedByUser, sqlType, text, timestamp, userRef, uuid } from './columns.js';
2
+ export type { AnyColumn, ArrayElementType, AuthUserOnDelete, ColumnDef, ColumnType, ColumnWrite, ColValue, OnDeleteAction } from './columns.js';
3
+ export { foreignKey, ForeignKeyBuilder } from './foreign-key.js';
4
+ export type { FkAction, ForeignKeyDef } from './foreign-key.js';
5
+ export { check, exprCtx, policy, PolicyBuilder, PolicyExprRef, policySQL } from './policy.js';
6
+ export type { CheckDef, PolicyCommand, PolicyDef, PolicyExpr, PolicyExprCtx, PolicyMode, PolicyRole, TableRef } from './policy.js';
7
+ export { raw } from './raw.js';
8
+ export type { RawConstraintDef, RawUnitDef } from './raw.js';
9
+ export { defineSchema, defineTable, index, IndexBuilder, qualifiedTableKey, TABLE_META } from './schema.js';
10
+ export type { ColumnMap, IndexDef, SchemaDef, SearchInput, TableDef, TableHandle, TableInput } from './schema.js';
11
+ export { toModel } from './to-model.js';