@stoker-platform/node-client 0.5.84 → 0.5.86
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/bundle.cjs +2 -2
- package/dist/bundle.js +5 -5
- package/package.json +3 -3
package/dist/bundle.cjs
CHANGED
|
@@ -8,7 +8,7 @@ ${i}`)}})(),(async()=>{try{await n.deleteUser(t.User_ID)}catch(i){if(i.code==="a
|
|
|
8
8
|
${i}`)}})()]),r.length>0)throw new Error(r.join(", "))},ir=async(t,e,n,s,r,i,o,a,l)=>{const c=le(),d=er(),m=process.env.ADMIN_EMAIL;if(d==="development"||process.env.FIRESTORE_EMULATOR_HOST)if(m)t=m;else throw new Error("Admin email not set");const u=ms(t),f=zc(e),g=n?ai(n):void 0,v=s?ai(s):void 0,y=r?ms(r):void 0,w=i?ms(i):void 0,R=o?jn(o):void 0,O=l?jn(l):void 0,I=Array.isArray(u)?u:[u];if(I.length===0||I.length===1&&!I[0])throw new Error("No valid email addresses provided");const E={to:u,message:{subject:f}};if(g&&(E.message.text=g),v&&(E.message.html=v),y){const C=Array.isArray(y)?y:[y];C.length>0&&C[0]&&(E.cc=y)}if(w){const C=Array.isArray(w)?w:[w];C.length>0&&C[0]&&(E.bcc=w)}R&&(E.replyTo=R),a&&(E.message.attachments=a),O&&(E.from=O),await c.collection("system_mail").add(E)},Kc=["tenant","role","collection","doc"],Vo=new Set(Kc);function or(t){return Vo.has(t)}function Xc(t){if(!t)return{};const e={};for(const n of Object.keys(t))Vo.has(n)||(e[n]=t[n]);return e}const ar=async(t,e,n,s,r,i,o)=>{const a=Ot.getAuth(),l=le(),{labels:c,fields:d}=s,m="USER_ERROR",u=async(y,w)=>{try{await qt(y)}catch{throw new Error(`ROLLBACK_FAILED: ${w}`)}};let f;try{f=await a.createUser({email:r.Email,emailVerified:!1,password:o,displayName:r.Name,photoURL:r.Photo_URL,disabled:!r.Enabled})}catch{throw new Error(m)}const g={};for(const y of d)y.saveToAuthToken&&!or(y.name)&&r[y.name]!==void 0&&y.type!=="Timestamp"&&y.type!=="Computed"&&y.type!=="Embedding"&&(Te(r[y.name])?g[y.name]=null:g[y.name]=r[y.name]);try{await a.setCustomUserClaims(f.uid,{...g,tenant:t,role:r.Role,collection:c.collection,doc:e})}catch{throw await u(r,m),new Error(m)}try{await l.collection("tenants").doc(t).collection("system_user_permissions").doc(f.uid).set({...i,Role:r.Role,Enabled:r.Enabled??!1,Collection:c.collection,Doc_ID:e})}catch{throw await u(r,m),new Error(m)}const v=n.auth.enableMultiFactorAuth;if(v===!0||typeof v=="object"&&v.includes(r.Role)){let y;try{y=await a.generateEmailVerificationLink(r.Email)}catch{throw await u(r,m),new Error(m)}if(y.includes("apiKey=&")){const w=JSON.parse(process.env.STOKER_FB_WEB_APP_CONFIG);y=y.replace("apiKey=&",`apiKey=${w.apiKey}&`)}if(y){const w=`https://${a.app.options.projectId}.firebaseapp.com/__/auth/action${y}`,R=await oe(n.appName),O=n?.mail?.emailVerification&&n.mail.emailVerification(w,R);try{await ir(r.Email,O?.subject||"Please verify your email address",void 0,O?.html||`Please verify your email address by clicking the link:
|
|
9
9
|
</br>
|
|
10
10
|
</br>
|
|
11
|
-
<a href="${w}">${w}</a>`)}catch{throw await u(r,m),new Error(m)}}}return f.uid},lr=async(t,e)=>{const{labels:n,parentCollection:s}=e;if(s){const r=t.at(-2);if(!r)throw new Error("PERMISSION_DENIED");const i=[...t].slice(0,-2),o=await ze(i,r).catch(()=>{throw new Error("PERMISSION_DENIED")}),a=o.Collection_Path;if(a.push(o.id),a.push(n.collection),!Qe(t,a))throw new Error("PERMISSION_DENIED")}else if(t.length>1)throw new Error("PERMISSION_DENIED")},zn=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),eu=t=>{const e=n=>{Object.entries(n).forEach(([s,r])=>{zn(r)&&Object.keys(r).length===2&&typeof r.seconds=="number"&&typeof r.nanoseconds=="number"?n[s]=new ie.Timestamp(r.seconds,r.nanoseconds):zn(r)&&e(r)})};e(t)},cr=t=>{const e=n=>{Object.entries(n).forEach(([s,r])=>{zn(r)&&Object.keys(r).length===2&&typeof r._seconds=="number"&&typeof r._nanoseconds=="number"?n[s]=new ie.Timestamp(r._seconds,r._nanoseconds):zn(r)&&e(r)})};e(t)},Kn=t=>{if(typeof t=="object")for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(t[e]==="_DELETE_FIELD"?t[e]=ie.FieldValue.delete():Kn(t[e]))},tu=(t,e,n)=>{if(X(n)&&n.includeFields){const s={};return n.includeFields.forEach(r=>{if(e[r]!==void 0){s[r]=e[r];const i=U(t.fields,r);kt(t,[i]).size===1&&(s[`${r}_Lowercase`]=e[`${r}_Lowercase`])}}),s}return{}},ci=async(t,e,n,s,r,i,o)=>{if(!X(n)||!n.enforceHierarchy)return;const{fields:a}=e,l=le(),c=Object.entries(s[n.name]);if(!c.length)throw new Error(`VALIDATION_ERROR: relation ${n.name} is required`);const[d,m]=c[0];if(!m)throw new Error(`VALIDATION_ERROR: Relation ${n.name} is required`);const u=xe(l,m.Collection_Path,t);if(i.size++,!o&&i.size>500)throw new Error("VALIDATION ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks, entity restrictions (in permissions when dealing with user collections) or relation hierarchy checks.");const f=await r.get(u.doc(d));if(!f.exists)throw new Error(`VALIDATION_ERROR: Record ${d} not found in collection ${n.collection}`);const g=f.data(),v=U(a,n.enforceHierarchy.field),y=n.enforceHierarchy;if(!g[y.recordLinkField]||!s[v.name]||!Object.keys(s[v.name]).every(w=>Object.keys(g[y.recordLinkField]).includes(w)))throw new Error("VALIDATION_ERROR: Invalid relation hierarchy")},En=async(t,e,n)=>{delete e[t.name][n],e[`${t.name}_Array`]=e[`${t.name}_Array`]?.filter(s=>s!==n),delete e[`${t.name}_Single`]},gs=async(t,e,n,s,r)=>{e[t.name]||={},e[t.name][s]=n,e[`${t.name}_Array`]||=[],e[`${t.name}_Array`].push(s),r&&(e[`${t.name}_Single`]={id:s,...n})},nu=(t,e,n,s,r,i)=>{const o=[],a=[];for(const l of s.fields)X(l)&&l.twoWay&&l.collection===n&&((t==="Create"||!i?.[l.name]?.[e])&&r[l.name]?.[e]&&o.push(l),t==="Update"&&i?.[l.name]?.[e]&&!r[l.name]?.[e]&&a.push(l));return{addedFields:o,removedFields:a}},ur=async(t,e,n,s,r,i,o,a,l,c,d,m)=>{const{fields:u}=i,f=le(),g=new Map,v=new Map,y=new Map;for(const w of u)X(w)&&["OneToOne","OneToMany"].includes(w.type)&&w.enforceHierarchy&&s[w.name]&&(t==="Create"||t==="Update"&&!Qe(m?.[w.name],s[w.name]))&&await ci(e,i,w,s,a,l);for(const w of u)if(X(w)&&w.twoWay){const R=o.collections[w.collection],O=U(R.fields,w.twoWay),I=mt(R,[O]);if(s[w.name]){const E=Object.entries(s[w.name]);for(const C of E){const[_,Z]=C;if(t==="Create"||!m?.[w.name]?.[_]){let k;const p=y.get(w.collection)?.[_];if(p)k=p;else{l.size++,l&&l.size>500&&console.error(new Error(`VALIDATION_ERROR: ${l.size} operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of two way updates, unique field checks, entity restrictions (in permissions when dealing with user collections) or relation hierarchy checks.`));const A=xe(f,Z.Collection_Path,e),j=await a.get(A.doc(_));if(!j.exists){En(w,r,_);continue}const h=j.data();v.set(w.collection,{...v.get(w.collection)||{},[_]:h}),k=he({...h,...In(s)})}const F={...k[O.name],[n]:{Collection_Path:s.Collection_Path,...tu(i,s,O)}};k[O.name]=F,k[`${O.name}_Array`]||=[],k[`${O.name}_Array`].push(n),I.size===1&&(k[`${O.name}_Single`]={id:n,...F}),y.set(w.collection,{...y.get(w.collection)||{},[_]:k})}}}if(t==="Update"){const E=Object.entries(m?.[w.name]||{});for(const C of E){const[_,Z]=C;if(!s[w.name]?.[_]){let k;const p=y.get(w.collection)?.[_];if(p)k=p;else{l.size++,l&&l.size>500&&console.error(new Error(`VALIDATION_ERROR: ${l.size} operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of two way updates, unique field checks, entity restrictions (in permissions when dealing with user collections) or relation hierarchy checks.`));const F=xe(f,Z.Collection_Path,e),A=await a.get(F.doc(_));if(!A.exists){g.set(w.name,[...g.get(w.name)||[],_]);continue}const j=A.data();v.set(w.collection,{...v.get(w.collection)||{},[_]:j}),k=he({...j,...In(s)})}k[O.name][n]&&delete k[O.name][n],Array.isArray(k[`${O.name}_Array`])&&(k[`${O.name}_Array`]=k[`${O.name}_Array`].filter(F=>F!==n)),I.size===1&&delete k[`${O.name}_Single`],y.set(w.collection,{...y.get(w.collection)||{},[_]:k})}}}}for(const[w,R]of y.entries()){const O=o.collections[w],I=He(w,o);for(const[E,C]of Object.entries(R)){const{addedFields:_,removedFields:Z}=nu(t,E,w,i,s,m);if(c&&d){for(const k of _){const p=U(O.fields,k.twoWay);if(!hr(O,p,d)){En(k,r,E);continue}}for(const k of Z){const p=U(O.fields,k.twoWay),F=mt(O,[p]);if(!hr(O,p,d)){gs(k,r,m?.[k.name]?.[E],E,F.size===1);continue}}if(!lt("Update",O,o,c,d,v.get(w)?.[E])){for(const k of _)En(k,r,E);for(const k of Z){const p=U(O.fields,k.twoWay),F=mt(O,[p]);gs(k,r,m?.[k.name]?.[E],E,F.size===1)}}if(!lt("Update",O,o,c,d,C)){for(const k of _)En(k,r,E);for(const k of Z){const p=U(O.fields,k.twoWay),F=mt(O,[p]);gs(k,r,m?.[k.name]?.[E],E,F.size===1)}}}try{await an("update",C,O,I,{operation:"update",record:C,context:{},originalRecord:C},o)}catch(k){throw new Error(`VALIDATION_ERROR: Two way relation update would invalidate record ${E} in ${w}: ${k.message}`)}for(const k of _){const p=U(O.fields,k.twoWay);["OneToOne","OneToMany"].includes(p.type)&&X(p)&&p.enforceHierarchy&&await ci(e,O,p,C,a,l,!0)}}}return g},Uo=(t,e,n)=>{t==="create"&&_t.forEach(s=>{if(!Qe(e[s],n[s]))throw new Error(`Updating system fields in hooks is not allowed: ${s}`)}),t==="update"&&(["id","Collection_Path","Created_At","Saved_At","Created_By"].forEach(s=>{if(s in e)throw new Error(`Cannot update system field: ${s}`)}),_t.forEach(s=>{if(!["id","Collection_Path","Created_At","Saved_At","Created_By"].includes(s)&&!Qe(e[s],n[s]))throw new Error(`Updating system fields in hooks is not allowed: ${s}`)}))},dr=(t,e,n,s)=>{const{softDelete:r}=e;if(!r)return;const i=r?.archivedField,o=r?.timestampField;if(n[i]===!0)if(t==="create")n[o]=ie.FieldValue.serverTimestamp();else{if(!s)throw new Error("Original record is required for update");if(!s[i])n[o]=ie.FieldValue.serverTimestamp();else if(n[o]&&n[o]?.valueOf()!==s[o]?.valueOf())throw new Error("Soft delete timestamp cannot be changed")}},ws=async(t,e,n,s,r)=>{if(!Re("Read",r.collections?.[e.labels.collection]))return!1;try{await ze([e.labels.collection],n,{userId:s,providedTransaction:t})}catch{return!1}return!0},su=10,ru=async(t,e)=>{if(t.length===0)return;const n=Math.min(e,t.length);let s=0;const r=async()=>{for(;s<t.length;){const i=s;s+=1,await t[i]()}};await Promise.all(Array.from({length:n},r))},Wo=async(t,e,n,s,r,i,o)=>{for(const[a,l]of Object.entries(e?.collections||{})){if(!r.collections[a])throw new Error("PERMISSION_DENIED");const c=r.collections[a].access,d=r.collections[a];if(s.collections?.[a]?.restrictEntities||s?.collections?.[a]?.recordOwner?.active||s?.collections?.[a]?.recordUser?.active||s?.collections?.[a]?.recordProperty?.active){const u=i?.collections?.[a],f=[];if(l.individualEntities){const g=l.individualEntities,v=u?.individualEntities||[],y=i?g.filter(w=>!v.includes(w)):g;for(const w of y){if(o&&o.size++,o&&o.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks or entity restrictions (in permissions when dealing with user collections).");f.push(async()=>{if(!await ws(t,d,w,n,s))throw new Error("PERMISSION_DENIED")})}}if(l.parentEntities){const g=c.entityRestrictions?.restrictions?.find(I=>I.type==="Parent"&&I.roles.some(E=>E.role===e.Role));if(!g)throw new Error("PERMISSION_DENIED");const v=U(d.fields,g.collectionField),y=r.collections[v.collection],w=l.parentEntities,R=u?.parentEntities||[],O=i?w.filter(I=>!R.includes(I)):w;for(const I of O){if(o&&o.size++,o&&o.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks or entity restrictions (in permissions when dealing with user collections).");f.push(async()=>{if(!await ws(t,y,I,n,s))throw new Error("PERMISSION_DENIED")})}}if(l.parentPropertyEntities){const g=c.entityRestrictions?.restrictions?.find(O=>O.type==="Parent_Property"&&O.roles.some(I=>I.role===e.Role));if(!g)throw new Error("PERMISSION_DENIED");const v=U(d.fields,g.collectionField),y=r.collections[v.collection],w=l.parentPropertyEntities,R=u?.parentPropertyEntities||{};for(const[O,I]of Object.entries(w)){const E=R[O]||[],C=i?I.filter(_=>!E.includes(_)):I;for(const _ of C){if(o&&o.size++,o&&o.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks or entity restrictions (in permissions when dealing with user collections).");f.push(async()=>{if(!await ws(t,y,_,n,s))throw new Error("PERMISSION_DENIED")})}}}await ru(f,su)}}},iu=async(t,e,n)=>{const s=n?.userId,r=n?.user;let i=n?.context;const o=Be(),a=St();if(n?.providedTransaction&&s)throw new Error("PERMISSION_DENIED");if(n?.providedSchema&&s)throw new Error("PERMISSION_DENIED");let l=n?.providedSchema||await qe();if(t.length===0)throw new Error("EMPTY_PATH");const c=t.at(-1),d=Object.keys(l.collections).includes(c),m=a.disabledCollections?.includes(c);if(!d||m)throw new Error("COLLECTION_NOT_FOUND");const u=l.collections[c],{labels:f,fields:g,enableWriteLog:v,softDelete:y}=u,w=He(f.collection,l),R=await oe(a.appName),O=le(),I=Ot.getAuth(),C=(s?await I.getUser(s):void 0)?.customClaims?.role;let _;s&&await lr(t,u);const Z=xe(O,t,o),k=n?.id||Z.doc().id;i=i||{},i.collection=f.collection,e.id=k,cr(e),Kn(e);const p=js("create",t,e,l,R,"Online",s||"System",ie.Timestamp.now(),ie.FieldValue.serverTimestamp());n?.createdAt&&(p.Created_At=n.createdAt),n?.createdBy&&(p.Created_By=n.createdBy);const F=he(p);y&&(p[y.archivedField]=!1),Ue(e),pt(u,p,l),v&&!n?.providedTransaction&&await vt("create","started",p,o,t,k,u),await ae("preOperation",a,w,{operation:"create",data:p,recordId:k,context:i}),await ae("preWrite",a,w,{operation:"create",data:p,recordId:k,context:i}),pt(u,p,l),xn(u,p),await fa(p,u,w,C),Ue(p);try{if(u.auth&&r&&!r.password)throw new Error("Password is required");n?.providedTransaction||(await Lo("create",o,k,p,u,l),await an("create",p,u,w,{operation:"create",record:p,context:i},l))}catch(T){throw new Error(`VALIDATION_ERROR: ${T.message}`)}Ue(p),e.id=k,y&&(p[y.archivedField]=!1),pt(u,p,l),xn(u,p);try{Uo("create",p,F),dr("create",u,p),await an("create",p,u,w,{operation:"create",record:p,context:i},l)}catch(T){throw new Error(`VALIDATION_ERROR: ${T.message}`)}if(s){const T=C;if(!T)throw new Error("USER_ERROR");if(!(w.custom?.serverAccess?.create!==void 0?await oe(w.custom.serverAccess.create,[T,p]):!0))throw new Error("PERMISSION_DENIED");for(const q of u.fields){if(!(q.name in p))continue;const M=Ce(q,w);if(M?.custom?.serverAccess?.create!==void 0&&!await oe(M.custom.serverAccess.create,[T,p]))throw new Error("PERMISSION_DENIED")}}r&&(r.permissions||={},r.permissions.Role||=p.Role,r.permissions.Enabled||=p.Enabled);const h=async(T,N)=>{const[q,M,G,z]=await Promise.all([n?.providedTransaction?Promise.resolve({}):T.get(O.collection("system_deployment").doc("latest_deploy")),n?.providedTransaction?Promise.resolve({}):T.get(O.collection("system_deployment").doc("maintenance_mode")),s?T.get(O.collection("tenants").doc(o).collection("system_user_permissions").doc(s)):Promise.resolve({}),n?.providedSchema?Promise.resolve(n.providedSchema):qe()]);if(N&&(N.size+=3),!n?.providedTransaction){if(!q.exists)throw new Error("VERSION_ERROR");const x=q.data();if(x.force&&p.Last_Write_At.valueOf()<x.time.valueOf())throw new Error("VERSION_ERROR");if(!M.exists)throw new Error("MAINTENANCE_MODE");if(M.data().active)throw new Error("MAINTENANCE_MODE")}if(l=z,s){if(!G?.exists)throw new Error("PERMISSION_DENIED");if(_=G.data(),!_.Role)throw new Error("USER_ERROR");if(!_.Enabled)throw new Error("PERMISSION_DENIED")}if(!n?.providedTransaction){const Q=g.filter(B=>"unique"in B&&B.unique).map(async B=>{if(!s||!B.access||B.access.includes(_.Role)){if(!e[B.name])return;const ve=Ce(B,w);if(!(s&&ve?.custom?.serverAccess?.read!==void 0?await oe(ve.custom.serverAccess.read,[_?.Role,p]):!0))throw new Error("PERMISSION_DENIED");const Ge=e[B.name].toString().toLowerCase().replace(/\s/g,"---").replaceAll("/","|||");if(Xs(Ge)){if(N&&N.size++,N&&N.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks.");if((await T.get(O.collection("tenants").doc(o).collection("system_unique").doc(f.collection).collection(`Unique-${f.collection}-${B.name}`).doc(Ge)))?.exists)throw new Error(`VALIDATION_ERROR: ${B.name} "${p[B.name]}" already exists`)}else throw new Error(`VALIDATION_ERROR: ${B.name} "${p[B.name]}" is invalid`)}});await Promise.all(Q)}mr(p,k,u,l,s,_,r?.permissions),r?.permissions&&s&&_&&await Wo(T,r.permissions,s,_,l),mr(p,k,u,l,s,_,r?.permissions)};if(u.auth&&r){await O.runTransaction(async N=>{await h(N)},{maxAttempts:10});const T=await ar(o,k,a,u,p,r.permissions,r.password);p.User_ID=T}const S=async T=>{try{const N={size:1};await h(T,N),n?.noTwoWay||await ur("Create",o,k,p,p,u,l,T,N,s,_);const q=Ws(l);Ks("create",T,t,k,p,l,u,n,q,ie.FieldValue.arrayUnion,ie.FieldValue.arrayRemove,ie.FieldValue.delete,M=>O.collection("tenants").doc(o).collection("system_fields").doc(f.collection).collection(`${f.collection}-${M.name}`).doc(k),(M,G)=>O.collection("tenants").doc(o).collection("system_unique").doc(f.collection).collection(`Unique-${f.collection}-${M.name}`).doc(G),M=>O.collection("tenants").doc(o).collection("system_fields").doc(f.collection).collection(`${f.collection}-${M}`).doc(k),(M,G)=>xe(O,M,o).doc(G),(M,G,z)=>O.collection("tenants").doc(o).collection("system_fields").doc(M.collection).collection(`${M.collection}-${G}`).doc(z),(M,G,z)=>O.collection("tenants").doc(o).collection("system_fields").doc(M.collection).collection(`${M.collection}-${G.replaceAll(" ","-")}`).doc(z),void 0,void 0,N),T.set(Z.doc(k),p)}catch(N){if(n?.providedTransaction)throw N;{const M=await ae("postWriteError",a,w,{operation:"create",data:p,recordId:k,context:i,error:N});if(v&&(await new Promise(G=>{setTimeout(G,250)}),await vt("create",M?.resolved?"success":"failed",p,o,t,k,u,M?.resolved?void 0:N)),!M?.resolved)throw u.auth&&r&&await qt(p),N}}};if(n?.providedTransaction?await S(n.providedTransaction):await O.runTransaction(async T=>{await S(T)},{maxAttempts:10}),!n?.providedTransaction){const T={operation:"create",data:p,recordId:k,context:i},N={...T};await ae("postWrite",a,w,T),await ae("postOperation",a,w,N)}return{id:k,...p}},en=async(t,e,n,s)=>{const r=Be(),i=Ot.getAuth(),o=le(),a=e.customClaims||{};let l=!1;try{await i.updateUser(t,{email:e.email,disabled:e.disabled,displayName:e.displayName,photoURL:e.photoURL,emailVerified:!!e.emailVerified})}catch{l=!0}try{await i.setCustomUserClaims(t,{...a})}catch{l=!0}try{await o.collection("tenants").doc(r).collection("system_user_permissions").doc(t).update({...n,Collection:a.collection,Doc_ID:a.doc,Role:a.role,Enabled:!e.disabled})}catch{l=!0}if(l)throw new Error(`ROLLBACK_FAILED: ${s}`)},jo=async(t,e,n,s,r,i,o,a,l,c,d)=>{const m=Ot.getAuth(),u=le(),f={};for(const y of r.fields)y.saveToAuthToken&&!or(y.name)&&i[y.name]!==void 0&&y.type!=="Timestamp"&&y.type!=="Computed"&&y.type!=="Embedding"&&(Te(i[y.name])?f[y.name]=null:f[y.name]=i[y.name]);const g="USER_ERROR";let v="";if(t==="create"){if(!d)throw new Error("VALIDATION_ERROR: Password is required");if(!l)throw new Error("VALIDATION_ERROR: Permissions are required");try{v=await ar(e,n,s,r,i,l,d)}catch(y){throw new Error(y.message)}}else if(t==="update"){if(!c||!a)throw new Error("USER_ERROR");const y={};if(o.Name!==i.Name&&(y.displayName=i.Name),o.Email!==i.Email&&(y.email=i.Email,y.emailVerified=!1),o.Photo_URL!==i.Photo_URL&&(y.photoURL=i.Photo_URL?i.Photo_URL:void 0),o.Enabled!==i.Enabled&&(y.disabled=!i.Enabled),d&&(y.password=d),Object.keys(y).length)try{await m.updateUser(o.User_ID,y),y.disabled===!0&&await m.revokeRefreshTokens(o.User_ID)}catch{throw await en(o.User_ID,a,c,g),new Error(g)}if(i.Role&&i.Role!==o.Role||Object.keys(f).length)try{const w={...Xc(a.customClaims),...f};await m.setCustomUserClaims(o.User_ID,{...w,tenant:e,role:i.Role,collection:r.labels.collection,doc:n}),await m.revokeRefreshTokens(o.User_ID)}catch{throw await en(o.User_ID,a,c,g),new Error(g)}try{await u.collection("tenants").doc(e).collection("system_user_permissions").doc(o.User_ID).set({...l||c,Collection:r.labels.collection,Doc_ID:n,Role:i.Role,Enabled:i.Enabled??o.Enabled??!1})}catch{throw await en(o.User_ID,a,c,g),new Error(g)}if(o.Email!==i.Email){const w=s.auth.enableMultiFactorAuth;if(w===!0||typeof w=="object"&&w.includes(i.Role)){let R=await m.generateEmailVerificationLink(i.Email).catch(()=>{throw new Error("Error generating email verification link")});if(R.includes("apiKey=&")){const O=JSON.parse(process.env.STOKER_FB_WEB_APP_CONFIG);R=R.replace("apiKey=&",`apiKey=${O.apiKey}&`)}if(R){const O=`https://${m.app.options.projectId}.firebaseapp.com/__/auth/action${R}`,I=await oe(s.appName),E=s?.mail?.emailVerification&&s.mail.emailVerification(O,I);try{await ir(i.Email,E?.subject||"Please verify your email address",void 0,E?.html||`Please verify your email address by clicking the link:
|
|
11
|
+
<a href="${w}">${w}</a>`)}catch{throw await u(r,m),new Error(m)}}}return f.uid},lr=async(t,e)=>{const{labels:n,parentCollection:s}=e;if(s){const r=t.at(-2);if(!r)throw new Error("PERMISSION_DENIED");const i=[...t].slice(0,-2),o=await ze(i,r).catch(()=>{throw new Error("PERMISSION_DENIED")}),a=o.Collection_Path;if(a.push(o.id),a.push(n.collection),!Qe(t,a))throw new Error("PERMISSION_DENIED")}else if(t.length>1)throw new Error("PERMISSION_DENIED")},zn=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),eu=t=>{const e=n=>{Object.entries(n).forEach(([s,r])=>{zn(r)&&Object.keys(r).length===2&&typeof r.seconds=="number"&&typeof r.nanoseconds=="number"?n[s]=new ie.Timestamp(r.seconds,r.nanoseconds):zn(r)&&e(r)})};e(t)},cr=t=>{const e=n=>{Object.entries(n).forEach(([s,r])=>{zn(r)&&Object.keys(r).length===2&&typeof r._seconds=="number"&&typeof r._nanoseconds=="number"?n[s]=new ie.Timestamp(r._seconds,r._nanoseconds):zn(r)&&e(r)})};e(t)},Kn=t=>{if(typeof t=="object")for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(t[e]==="_DELETE_FIELD"?t[e]=ie.FieldValue.delete():Kn(t[e]))},tu=(t,e,n)=>{if(X(n)&&n.includeFields){const s={};return n.includeFields.forEach(r=>{if(e[r]!==void 0){s[r]=e[r];const i=U(t.fields,r);kt(t,[i]).size===1&&(s[`${r}_Lowercase`]=e[`${r}_Lowercase`])}}),s}return{}},ci=async(t,e,n,s,r,i,o)=>{if(!X(n)||!n.enforceHierarchy)return;const{fields:a}=e,l=le(),c=Object.entries(s[n.name]);if(!c.length)throw new Error(`VALIDATION_ERROR: relation ${n.name} is required`);const[d,m]=c[0];if(!m)throw new Error(`VALIDATION_ERROR: Relation ${n.name} is required`);const u=xe(l,m.Collection_Path,t);if(i.size++,!o&&i.size>500)throw new Error("VALIDATION ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks, entity restrictions (in permissions when dealing with user collections) or relation hierarchy checks.");const f=await r.get(u.doc(d));if(!f.exists)throw new Error(`VALIDATION_ERROR: Record ${d} not found in collection ${n.collection}`);const g=f.data(),v=U(a,n.enforceHierarchy.field),y=n.enforceHierarchy;if(!g[y.recordLinkField]||!s[v.name]||!Object.keys(s[v.name]).every(w=>Object.keys(g[y.recordLinkField]).includes(w)))throw new Error("VALIDATION_ERROR: Invalid relation hierarchy")},En=async(t,e,n)=>{delete e[t.name][n],e[`${t.name}_Array`]=e[`${t.name}_Array`]?.filter(s=>s!==n),delete e[`${t.name}_Single`]},gs=async(t,e,n,s,r)=>{e[t.name]||={},e[t.name][s]=n,e[`${t.name}_Array`]||=[],e[`${t.name}_Array`].push(s),r&&(e[`${t.name}_Single`]={id:s,...n})},nu=(t,e,n,s,r,i)=>{const o=[],a=[];for(const l of s.fields)X(l)&&l.twoWay&&l.collection===n&&((t==="Create"||!i?.[l.name]?.[e])&&r[l.name]?.[e]&&o.push(l),t==="Update"&&i?.[l.name]?.[e]&&!r[l.name]?.[e]&&a.push(l));return{addedFields:o,removedFields:a}},ur=async(t,e,n,s,r,i,o,a,l,c,d,m)=>{const{fields:u}=i,f=le(),g=new Map,v=new Map,y=new Map;for(const w of u)X(w)&&["OneToOne","OneToMany"].includes(w.type)&&w.enforceHierarchy&&s[w.name]&&(t==="Create"||t==="Update"&&!Qe(m?.[w.name],s[w.name]))&&await ci(e,i,w,s,a,l);for(const w of u)if(X(w)&&w.twoWay){const R=o.collections[w.collection],O=U(R.fields,w.twoWay),I=mt(R,[O]);if(s[w.name]){const E=Object.entries(s[w.name]);for(const C of E){const[_,Z]=C;if(t==="Create"||!m?.[w.name]?.[_]){let k;const p=y.get(w.collection)?.[_];if(p)k=p;else{l.size++,l&&l.size>500&&console.error(new Error(`VALIDATION_ERROR: ${l.size} operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of two way updates, unique field checks, entity restrictions (in permissions when dealing with user collections) or relation hierarchy checks.`));const A=xe(f,Z.Collection_Path,e),j=await a.get(A.doc(_));if(!j.exists){En(w,r,_);continue}const h=j.data();v.set(w.collection,{...v.get(w.collection)||{},[_]:h}),k=he({...h,...In(s)})}const F={...k[O.name],[n]:{Collection_Path:s.Collection_Path,...tu(i,s,O)}};k[O.name]=F,k[`${O.name}_Array`]||=[],k[`${O.name}_Array`].push(n),I.size===1&&(k[`${O.name}_Single`]={id:n,...F}),y.set(w.collection,{...y.get(w.collection)||{},[_]:k})}}}if(t==="Update"){const E=Object.entries(m?.[w.name]||{});for(const C of E){const[_,Z]=C;if(!s[w.name]?.[_]){let k;const p=y.get(w.collection)?.[_];if(p)k=p;else{l.size++,l&&l.size>500&&console.error(new Error(`VALIDATION_ERROR: ${l.size} operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of two way updates, unique field checks, entity restrictions (in permissions when dealing with user collections) or relation hierarchy checks.`));const F=xe(f,Z.Collection_Path,e),A=await a.get(F.doc(_));if(!A.exists){g.set(w.name,[...g.get(w.name)||[],_]);continue}const j=A.data();v.set(w.collection,{...v.get(w.collection)||{},[_]:j}),k=he({...j,...In(s)})}k[O.name][n]&&delete k[O.name][n],Array.isArray(k[`${O.name}_Array`])&&(k[`${O.name}_Array`]=k[`${O.name}_Array`].filter(F=>F!==n)),I.size===1&&delete k[`${O.name}_Single`],y.set(w.collection,{...y.get(w.collection)||{},[_]:k})}}}}for(const[w,R]of y.entries()){const O=o.collections[w],I=He(w,o);for(const[E,C]of Object.entries(R)){const{addedFields:_,removedFields:Z}=nu(t,E,w,i,s,m);if(c&&d){for(const k of _){const p=U(O.fields,k.twoWay);if(!hr(O,p,d)){En(k,r,E);continue}}for(const k of Z){const p=U(O.fields,k.twoWay),F=mt(O,[p]);if(!hr(O,p,d)){gs(k,r,m?.[k.name]?.[E],E,F.size===1);continue}}if(!lt("Update",O,o,c,d,v.get(w)?.[E])){for(const k of _)En(k,r,E);for(const k of Z){const p=U(O.fields,k.twoWay),F=mt(O,[p]);gs(k,r,m?.[k.name]?.[E],E,F.size===1)}}if(!lt("Update",O,o,c,d,C)){for(const k of _)En(k,r,E);for(const k of Z){const p=U(O.fields,k.twoWay),F=mt(O,[p]);gs(k,r,m?.[k.name]?.[E],E,F.size===1)}}}try{await an("update",C,O,I,{operation:"update",data:C,context:{},originalRecord:C},o)}catch(k){throw new Error(`VALIDATION_ERROR: Two way relation update would invalidate record ${E} in ${w}: ${k.message}`)}for(const k of _){const p=U(O.fields,k.twoWay);["OneToOne","OneToMany"].includes(p.type)&&X(p)&&p.enforceHierarchy&&await ci(e,O,p,C,a,l,!0)}}}return g},Uo=(t,e,n)=>{t==="create"&&_t.forEach(s=>{if(!Qe(e[s],n[s]))throw new Error(`Updating system fields in hooks is not allowed: ${s}`)}),t==="update"&&(["id","Collection_Path","Created_At","Saved_At","Created_By"].forEach(s=>{if(s in e)throw new Error(`Cannot update system field: ${s}`)}),_t.forEach(s=>{if(!["id","Collection_Path","Created_At","Saved_At","Created_By"].includes(s)&&!Qe(e[s],n[s]))throw new Error(`Updating system fields in hooks is not allowed: ${s}`)}))},dr=(t,e,n,s)=>{const{softDelete:r}=e;if(!r)return;const i=r?.archivedField,o=r?.timestampField;if(n[i]===!0)if(t==="create")n[o]=ie.FieldValue.serverTimestamp();else{if(!s)throw new Error("Original record is required for update");if(!s[i])n[o]=ie.FieldValue.serverTimestamp();else if(n[o]&&n[o]?.valueOf()!==s[o]?.valueOf())throw new Error("Soft delete timestamp cannot be changed")}},ws=async(t,e,n,s,r)=>{if(!Re("Read",r.collections?.[e.labels.collection]))return!1;try{await ze([e.labels.collection],n,{userId:s,providedTransaction:t})}catch{return!1}return!0},su=10,ru=async(t,e)=>{if(t.length===0)return;const n=Math.min(e,t.length);let s=0;const r=async()=>{for(;s<t.length;){const i=s;s+=1,await t[i]()}};await Promise.all(Array.from({length:n},r))},Wo=async(t,e,n,s,r,i,o)=>{for(const[a,l]of Object.entries(e?.collections||{})){if(!r.collections[a])throw new Error("PERMISSION_DENIED");const c=r.collections[a].access,d=r.collections[a];if(s.collections?.[a]?.restrictEntities||s?.collections?.[a]?.recordOwner?.active||s?.collections?.[a]?.recordUser?.active||s?.collections?.[a]?.recordProperty?.active){const u=i?.collections?.[a],f=[];if(l.individualEntities){const g=l.individualEntities,v=u?.individualEntities||[],y=i?g.filter(w=>!v.includes(w)):g;for(const w of y){if(o&&o.size++,o&&o.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks or entity restrictions (in permissions when dealing with user collections).");f.push(async()=>{if(!await ws(t,d,w,n,s))throw new Error("PERMISSION_DENIED")})}}if(l.parentEntities){const g=c.entityRestrictions?.restrictions?.find(I=>I.type==="Parent"&&I.roles.some(E=>E.role===e.Role));if(!g)throw new Error("PERMISSION_DENIED");const v=U(d.fields,g.collectionField),y=r.collections[v.collection],w=l.parentEntities,R=u?.parentEntities||[],O=i?w.filter(I=>!R.includes(I)):w;for(const I of O){if(o&&o.size++,o&&o.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks or entity restrictions (in permissions when dealing with user collections).");f.push(async()=>{if(!await ws(t,y,I,n,s))throw new Error("PERMISSION_DENIED")})}}if(l.parentPropertyEntities){const g=c.entityRestrictions?.restrictions?.find(O=>O.type==="Parent_Property"&&O.roles.some(I=>I.role===e.Role));if(!g)throw new Error("PERMISSION_DENIED");const v=U(d.fields,g.collectionField),y=r.collections[v.collection],w=l.parentPropertyEntities,R=u?.parentPropertyEntities||{};for(const[O,I]of Object.entries(w)){const E=R[O]||[],C=i?I.filter(_=>!E.includes(_)):I;for(const _ of C){if(o&&o.size++,o&&o.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks or entity restrictions (in permissions when dealing with user collections).");f.push(async()=>{if(!await ws(t,y,_,n,s))throw new Error("PERMISSION_DENIED")})}}}await ru(f,su)}}},iu=async(t,e,n)=>{const s=n?.userId,r=n?.user;let i=n?.context;const o=Be(),a=St();if(n?.providedTransaction&&s)throw new Error("PERMISSION_DENIED");if(n?.providedSchema&&s)throw new Error("PERMISSION_DENIED");let l=n?.providedSchema||await qe();if(t.length===0)throw new Error("EMPTY_PATH");const c=t.at(-1),d=Object.keys(l.collections).includes(c),m=a.disabledCollections?.includes(c);if(!d||m)throw new Error("COLLECTION_NOT_FOUND");const u=l.collections[c],{labels:f,fields:g,enableWriteLog:v,softDelete:y}=u,w=He(f.collection,l),R=await oe(a.appName),O=le(),I=Ot.getAuth(),C=(s?await I.getUser(s):void 0)?.customClaims?.role;let _;s&&await lr(t,u);const Z=xe(O,t,o),k=n?.id||Z.doc().id;i=i||{},i.collection=f.collection,e.id=k,cr(e),Kn(e);const p=js("create",t,e,l,R,"Online",s||"System",ie.Timestamp.now(),ie.FieldValue.serverTimestamp());n?.createdAt&&(p.Created_At=n.createdAt),n?.createdBy&&(p.Created_By=n.createdBy);const F=he(p);y&&(p[y.archivedField]=!1),Ue(e),pt(u,p,l),v&&!n?.providedTransaction&&await vt("create","started",p,o,t,k,u),await ae("preOperation",a,w,{operation:"create",data:p,recordId:k,context:i}),await ae("preWrite",a,w,{operation:"create",data:p,recordId:k,context:i}),pt(u,p,l),xn(u,p),await fa(p,u,w,C),Ue(p);try{if(u.auth&&r&&!r.password)throw new Error("Password is required");n?.providedTransaction||(await Lo("create",o,k,p,u,l),await an("create",p,u,w,{operation:"create",data:p,context:i},l))}catch(T){throw new Error(`VALIDATION_ERROR: ${T.message}`)}Ue(p),e.id=k,y&&(p[y.archivedField]=!1),pt(u,p,l),xn(u,p);try{Uo("create",p,F),dr("create",u,p),await an("create",p,u,w,{operation:"create",data:p,context:i},l)}catch(T){throw new Error(`VALIDATION_ERROR: ${T.message}`)}if(s){const T=C;if(!T)throw new Error("USER_ERROR");if(!(w.custom?.serverAccess?.create!==void 0?await oe(w.custom.serverAccess.create,[T,p]):!0))throw new Error("PERMISSION_DENIED");for(const q of u.fields){if(!(q.name in p))continue;const M=Ce(q,w);if(M?.custom?.serverAccess?.create!==void 0&&!await oe(M.custom.serverAccess.create,[T,p]))throw new Error("PERMISSION_DENIED")}}r&&(r.permissions||={},r.permissions.Role||=p.Role,r.permissions.Enabled||=p.Enabled);const h=async(T,N)=>{const[q,M,G,z]=await Promise.all([n?.providedTransaction?Promise.resolve({}):T.get(O.collection("system_deployment").doc("latest_deploy")),n?.providedTransaction?Promise.resolve({}):T.get(O.collection("system_deployment").doc("maintenance_mode")),s?T.get(O.collection("tenants").doc(o).collection("system_user_permissions").doc(s)):Promise.resolve({}),n?.providedSchema?Promise.resolve(n.providedSchema):qe()]);if(N&&(N.size+=3),!n?.providedTransaction){if(!q.exists)throw new Error("VERSION_ERROR");const x=q.data();if(x.force&&p.Last_Write_At.valueOf()<x.time.valueOf())throw new Error("VERSION_ERROR");if(!M.exists)throw new Error("MAINTENANCE_MODE");if(M.data().active)throw new Error("MAINTENANCE_MODE")}if(l=z,s){if(!G?.exists)throw new Error("PERMISSION_DENIED");if(_=G.data(),!_.Role)throw new Error("USER_ERROR");if(!_.Enabled)throw new Error("PERMISSION_DENIED")}if(!n?.providedTransaction){const Q=g.filter(B=>"unique"in B&&B.unique).map(async B=>{if(!s||!B.access||B.access.includes(_.Role)){if(!e[B.name])return;const ve=Ce(B,w);if(!(s&&ve?.custom?.serverAccess?.read!==void 0?await oe(ve.custom.serverAccess.read,[_?.Role,p]):!0))throw new Error("PERMISSION_DENIED");const Ge=e[B.name].toString().toLowerCase().replace(/\s/g,"---").replaceAll("/","|||");if(Xs(Ge)){if(N&&N.size++,N&&N.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks.");if((await T.get(O.collection("tenants").doc(o).collection("system_unique").doc(f.collection).collection(`Unique-${f.collection}-${B.name}`).doc(Ge)))?.exists)throw new Error(`VALIDATION_ERROR: ${B.name} "${p[B.name]}" already exists`)}else throw new Error(`VALIDATION_ERROR: ${B.name} "${p[B.name]}" is invalid`)}});await Promise.all(Q)}mr(p,k,u,l,s,_,r?.permissions),r?.permissions&&s&&_&&await Wo(T,r.permissions,s,_,l),mr(p,k,u,l,s,_,r?.permissions)};if(u.auth&&r){await O.runTransaction(async N=>{await h(N)},{maxAttempts:10});const T=await ar(o,k,a,u,p,r.permissions,r.password);p.User_ID=T}const S=async T=>{try{const N={size:1};await h(T,N),n?.noTwoWay||await ur("Create",o,k,p,p,u,l,T,N,s,_);const q=Ws(l);Ks("create",T,t,k,p,l,u,n,q,ie.FieldValue.arrayUnion,ie.FieldValue.arrayRemove,ie.FieldValue.delete,M=>O.collection("tenants").doc(o).collection("system_fields").doc(f.collection).collection(`${f.collection}-${M.name}`).doc(k),(M,G)=>O.collection("tenants").doc(o).collection("system_unique").doc(f.collection).collection(`Unique-${f.collection}-${M.name}`).doc(G),M=>O.collection("tenants").doc(o).collection("system_fields").doc(f.collection).collection(`${f.collection}-${M}`).doc(k),(M,G)=>xe(O,M,o).doc(G),(M,G,z)=>O.collection("tenants").doc(o).collection("system_fields").doc(M.collection).collection(`${M.collection}-${G}`).doc(z),(M,G,z)=>O.collection("tenants").doc(o).collection("system_fields").doc(M.collection).collection(`${M.collection}-${G.replaceAll(" ","-")}`).doc(z),void 0,void 0,N),T.set(Z.doc(k),p)}catch(N){if(n?.providedTransaction)throw N;{const M=await ae("postWriteError",a,w,{operation:"create",data:p,recordId:k,context:i,error:N});if(v&&(await new Promise(G=>{setTimeout(G,250)}),await vt("create",M?.resolved?"success":"failed",p,o,t,k,u,M?.resolved?void 0:N)),!M?.resolved)throw u.auth&&r&&await qt(p),N}}};if(n?.providedTransaction?await S(n.providedTransaction):await O.runTransaction(async T=>{await S(T)},{maxAttempts:10}),!n?.providedTransaction){const T={operation:"create",data:p,recordId:k,context:i},N={...T};await ae("postWrite",a,w,T),await ae("postOperation",a,w,N)}return{id:k,...p}},en=async(t,e,n,s)=>{const r=Be(),i=Ot.getAuth(),o=le(),a=e.customClaims||{};let l=!1;try{await i.updateUser(t,{email:e.email,disabled:e.disabled,displayName:e.displayName,photoURL:e.photoURL,emailVerified:!!e.emailVerified})}catch{l=!0}try{await i.setCustomUserClaims(t,{...a})}catch{l=!0}try{await o.collection("tenants").doc(r).collection("system_user_permissions").doc(t).update({...n,Collection:a.collection,Doc_ID:a.doc,Role:a.role,Enabled:!e.disabled})}catch{l=!0}if(l)throw new Error(`ROLLBACK_FAILED: ${s}`)},jo=async(t,e,n,s,r,i,o,a,l,c,d)=>{const m=Ot.getAuth(),u=le(),f={};for(const y of r.fields)y.saveToAuthToken&&!or(y.name)&&i[y.name]!==void 0&&y.type!=="Timestamp"&&y.type!=="Computed"&&y.type!=="Embedding"&&(Te(i[y.name])?f[y.name]=null:f[y.name]=i[y.name]);const g="USER_ERROR";let v="";if(t==="create"){if(!d)throw new Error("VALIDATION_ERROR: Password is required");if(!l)throw new Error("VALIDATION_ERROR: Permissions are required");try{v=await ar(e,n,s,r,i,l,d)}catch(y){throw new Error(y.message)}}else if(t==="update"){if(!c||!a)throw new Error("USER_ERROR");const y={};if(o.Name!==i.Name&&(y.displayName=i.Name),o.Email!==i.Email&&(y.email=i.Email,y.emailVerified=!1),o.Photo_URL!==i.Photo_URL&&(y.photoURL=i.Photo_URL?i.Photo_URL:void 0),o.Enabled!==i.Enabled&&(y.disabled=!i.Enabled),d&&(y.password=d),Object.keys(y).length)try{await m.updateUser(o.User_ID,y),y.disabled===!0&&await m.revokeRefreshTokens(o.User_ID)}catch{throw await en(o.User_ID,a,c,g),new Error(g)}if(i.Role&&i.Role!==o.Role||Object.keys(f).length)try{const w={...Xc(a.customClaims),...f};await m.setCustomUserClaims(o.User_ID,{...w,tenant:e,role:i.Role,collection:r.labels.collection,doc:n}),await m.revokeRefreshTokens(o.User_ID)}catch{throw await en(o.User_ID,a,c,g),new Error(g)}try{await u.collection("tenants").doc(e).collection("system_user_permissions").doc(o.User_ID).set({...l||c,Collection:r.labels.collection,Doc_ID:n,Role:i.Role,Enabled:i.Enabled??o.Enabled??!1})}catch{throw await en(o.User_ID,a,c,g),new Error(g)}if(o.Email!==i.Email){const w=s.auth.enableMultiFactorAuth;if(w===!0||typeof w=="object"&&w.includes(i.Role)){let R=await m.generateEmailVerificationLink(i.Email).catch(()=>{throw new Error("Error generating email verification link")});if(R.includes("apiKey=&")){const O=JSON.parse(process.env.STOKER_FB_WEB_APP_CONFIG);R=R.replace("apiKey=&",`apiKey=${O.apiKey}&`)}if(R){const O=`https://${m.app.options.projectId}.firebaseapp.com/__/auth/action${R}`,I=await oe(s.appName),E=s?.mail?.emailVerification&&s.mail.emailVerification(O,I);try{await ir(i.Email,E?.subject||"Please verify your email address",void 0,E?.html||`Please verify your email address by clicking the link:
|
|
12
12
|
</br>
|
|
13
13
|
</br>
|
|
14
|
-
<a href="${O}">${O}</a>`)}catch{throw await en(o.User_ID,a,c,g),new Error(g)}}}}}else if(t==="delete")try{await qt(o)}catch(y){throw new Error(y.message)}if(v)return v},ou=async(t,e,n)=>{const s=Be(),r=le(),i=[e];n&&n.trim()!==""&&i.push(n),i.sort();const o=[];for(const a of i){const l=r.collection("tenants").doc(s).collection("system_locks").doc(a);if((await t.get(l)).exists)throw new Error("DOCUMENT_LOCKED");o.push(l)}for(const a of o)t.set(a,{locked:!0})},ui=async(t,e)=>{const n=Be(),s=le(),r=[t];e&&e.trim()!==""&&r.push(e),r.sort();const i=s.batch();for(const o of r){const a=s.collection("tenants").doc(n).collection("system_locks").doc(o);i.delete(a)}await i.commit()},zo=async(t,e,n,s)=>{const r=s?.userId;let i=s?.user,o=s?.context;const a=Be(),l=St();if(s?.providedTransaction&&r)throw new Error("PERMISSION_DENIED");if(s?.providedSchema&&r)throw new Error("PERMISSION_DENIED");if(s?.providedTransaction&&!s?.originalRecord)throw new Error("PERMISSION_DENIED");let c=s?.providedSchema||await qe(!0);if(t.length===0)throw new Error("EMPTY_PATH");const d=t.at(-1),m=Object.keys(c.collections).includes(d),u=l.disabledCollections?.includes(d);if(!m||u)throw new Error("COLLECTION_NOT_FOUND");const f=c.collections[d],{labels:g,fields:v,enableWriteLog:y}=f,w=He(g.collection,c),R=await oe(l.appName),O=Ot.getAuth(),I=le();let E;r&&await lr(t,f);const C=xe(I,t,a);o=o||{},o.collection=g.collection;let _=s?.originalRecord||await ze(t,e,{userId:r,noComputedFields:!0,noEmbeddingFields:!0}),Z=_;r&&(Z=await ze(t,e,{noComputedFields:!0,noEmbeddingFields:!0}));for(const x of v)x.type==="Computed"&&(delete n[x.name],delete _[x.name]);if(c={...c,collections:Object.fromEntries(Object.entries(c.collections).map(([x,Q])=>[x,{...Q,fields:Q.fields.filter(B=>B.type!=="Computed")}]))},i&&!i.operation)throw new Error("VALIDATION_ERROR: User operation is required");if(i&&!f.auth)throw new Error("VALIDATION_ERROR: User operations are only permitted for auth collections");if(i?.operation==="delete"&&i.permissions)throw new Error("VALIDATION_ERROR: Permissions are not allowed for delete operations");const k=f.fields.filter(x=>x.saveToAuthToken&&!or(x.name)),p=f.auth&&i?.operation==="create",F=f.auth&&i?.operation==="delete",A=f.auth&&!p&&!F&&(i?.operation==="update"||n.Role||n.Enabled!==void 0||n.Name||n.Email||n.Photo_URL||k.some(x=>n[x.name]!==void 0)),j=x=>x.User_ID&&(f.auth&&!p&&!F&&i?.operation==="update"||n.Role&&n.Role!==x.Role||n.Enabled!==void 0&&n.Enabled!==x.Enabled||n.Name&&n.Name!==x.Name||n.Email&&n.Email!==x.Email||n.Photo_URL&&n.Photo_URL!==x.Photo_URL||k.some(Q=>n[Q.name]!==void 0&&n[Q.name]!==x[Q.name]));delete n.id,cr(n),Kn(n);const h=js("update",t,n,c,R,"Online",r||"System",ie.Timestamp.now(),ie.FieldValue.serverTimestamp()),S=he(h);for(const x of v)!X(x)&&Te(h[x.name])&&x.nullable&&(h[x.name]=null);Ue(h),Ue(_),pt(f,h,c),pt(f,_,c),y&&!s?.providedTransaction&&await vt("update","started",h,a,t,e,f,void 0,_);const b={operation:"update",data:h,recordId:e,context:o,originalRecord:he(_)};await ae("preOperation",l,w,b);const T={operation:"update",data:h,recordId:e,context:o,originalRecord:he(_)};await ae("preWrite",l,w,T),pt(f,h,c),xn(f,h),Ue(h);try{if(p&&!i?.password)throw new Error("Password is required");if(!s?.providedTransaction){const x={...Z,...h};await Lo("update",a,e,x,f,c),it(x),await an("update",x,f,w,{operation:"update",record:h,context:o,originalRecord:he(_)},c)}}catch(x){throw new Error(`VALIDATION_ERROR: ${x.message}`)}Ue(h),delete n.id,pt(f,h,c),xn(f,h);for(const x of v)!X(x)&&Te(h[x.name])&&x.nullable&&(h[x.name]=null);try{const x={...Z,...h};it(x),dr("update",f,h,_),Uo("update",h,S),await an("update",x,f,w,{operation:"update",record:h,context:o,originalRecord:he(_)},c)}catch(x){throw new Error(`VALIDATION_ERROR: ${x.message}`)}let N;i&&(i.permissions||={});const q=async(x,Q,B)=>{const[ve,Ve,Ge,me,Ye]=await Promise.all([s?.providedTransaction?Promise.resolve({}):x.get(I.collection("system_deployment").doc("latest_deploy")),s?.providedTransaction?Promise.resolve({}):x.get(I.collection("system_deployment").doc("maintenance_mode")),s?.providedTransaction?Promise.resolve(_):ze(t,e,{userId:r,providedTransaction:x,noComputedFields:!0}),r?x.get(I.collection("tenants").doc(a).collection("system_user_permissions").doc(r)):Promise.resolve({}),s?.providedSchema?Promise.resolve(s.providedSchema):qe()]);if(B&&(B.size+=3),!s?.providedTransaction){if(!ve.exists)throw new Error("VERSION_ERROR");const Oe=ve.data();if(Oe.force&&h.Last_Write_At.valueOf()<Oe.time.valueOf())throw new Error("VERSION_ERROR");if(!Ve.exists)throw new Error("MAINTENANCE_MODE");if(Ve.data().active)throw new Error("MAINTENANCE_MODE")}if(!Ge)throw new Error("NOT_FOUND");if(_=Ge,c=Ye,r){if(!me?.exists)throw new Error("PERMISSION_DENIED");if(E=me.data(),!E.Role)throw new Error("USER_ERROR");if(!E.Enabled)throw new Error("PERMISSION_DENIED")}if(B&&(B.size+=nr(a,t,e,c,E).length),p&&_.User_ID||F&&!_.User_ID)throw new Error("USER_ERROR");if(j(_)){const Oe=await x.get(I.collection("tenants").doc(a).collection("system_user_permissions").doc(_.User_ID));if(B&&B.size++,!Oe?.exists)throw new Error("PERMISSION_DENIED");N=Oe.data();const ut=i?.permissions&&Object.keys(i.permissions).length>0?i.permissions:void 0;if(h.Role&&h.Role!==_.Role&&!ut&&(i||={operation:"update",permissions:{}},i.permissions=he(N),i.permissions.Role=h.Role),i?.permissions){if(i.permissions.Role||=h.Role||_.Role,i.permissions.Enabled??=h.Enabled??_.Enabled,Te(i.permissions.Role))throw new Error("VALIDATION_ERROR: Role field is required");if(Te(i.permissions.Enabled))throw new Error("VALIDATION_ERROR: Enabled field is required")}}if(!s?.providedTransaction){const ut=v.filter(we=>"unique"in we&&we.unique).map(async we=>{if(!r||!we.access||we.access.includes(E.Role)){if(h[we.name]===void 0||Te(h[we.name]))return;const Bt=Ce(we,w),hn={..._,...h};if(!(r&&Bt?.custom?.serverAccess?.read!==void 0?await oe(Bt.custom.serverAccess.read,[E?.Role,hn]):!0))throw new Error("PERMISSION_DENIED");const Xn=h[we.name].toString().toLowerCase().replace(/\s/g,"---").replaceAll("/","|||");if(Xs(Xn)){if(B&&B.size++,B&&B.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks.");const fr=await x.get(I.collection("tenants").doc(a).collection("system_unique").doc(g.collection).collection(`Unique-${g.collection}-${we.name}`).doc(Xn));if(fr.exists&&fr.data().id!==e)throw new Error(`VALIDATION_ERROR: ${we.name} "${h[we.name]}" already exists`)}else throw new Error(`VALIDATION_ERROR: ${we.name} "${h[we.name]}" is invalid`)}});await Promise.all(ut)}if(r&&E?.Role){const Oe=E.Role,ut={..._,...h};if(!(w.custom?.serverAccess?.update!==void 0?await oe(w.custom.serverAccess.update,[Oe,ut,_]):!0))throw new Error("PERMISSION_DENIED");for(const Bt of f.fields){const hn=Ce(Bt,w);if(Object.prototype.hasOwnProperty.call(h,Bt.name)&&hn?.custom?.serverAccess?.update!==void 0&&!await oe(hn.custom.serverAccess.update,[Oe,ut,_]))throw new Error("PERMISSION_DENIED")}}pr(h,_,e,f,c,r,E,i?.operation?i.operation:A?"update":void 0,i?.permissions,N),i?.permissions&&r&&E&&await Wo(x,i.permissions,r,E,c,N,B),pr(h,_,e,f,c,r,E,i?.operation?i.operation:A?"update":void 0,i?.permissions,N),Q&&(p||j(_)||F)&&await ou(x,e,_.User_ID)};(p||A||F)&&await I.runTransaction(async x=>{await q(x,!0)},{maxAttempts:10});const M=async(x,Q)=>{if(!_)throw new Error("NOT_FOUND");try{const B={size:1},ve={..._,...h};await q(x,!1,B);let Ve;!s?.noTwoWay&&!s?.providedTransaction&&(Ve=await ur("Update",a,e,ve,h,f,c,x,B,r,E,_));const Ge=Ws(c);Ks("update",x,t,e,h,c,f,s,Ge,ie.FieldValue.arrayUnion,ie.FieldValue.arrayRemove,ie.FieldValue.delete,me=>I.collection("tenants").doc(a).collection("system_fields").doc(g.collection).collection(`${g.collection}-${me.name}`).doc(e),(me,Ye)=>I.collection("tenants").doc(a).collection("system_unique").doc(g.collection).collection(`Unique-${g.collection}-${me.name}`).doc(Ye),me=>I.collection("tenants").doc(a).collection("system_fields").doc(g.collection).collection(`${g.collection}-${me}`).doc(e),(me,Ye)=>xe(I,me,a).doc(Ye),(me,Ye,Oe)=>I.collection("tenants").doc(a).collection("system_fields").doc(me.collection).collection(`${me.collection}-${Ye}`).doc(Oe),(me,Ye,Oe)=>I.collection("tenants").doc(a).collection("system_fields").doc(me.collection).collection(`${me.collection}-${Ye.replaceAll(" ","-")}`).doc(Oe),_,Ve,B),x.update(C.doc(e),h)}catch(B){if(s?.providedTransaction)throw B;{const ve={operation:"update",data:h,recordId:e,context:o,error:B,originalRecord:he(_)},Ve=await ae("postWriteError",l,w,ve);if(y&&(await new Promise(Ge=>{setTimeout(Ge,250)}),await vt("update",Ve?.resolved?"success":"failed",h,a,t,e,f,Ve?.resolved?void 0:B,_)),!Ve?.resolved)throw p&&await qt(_),j(_)&&await en(_.User_ID,Q,N,"USER_ERROR"),F&&await I.collection("tenants").doc(a).collection(g.collection).doc(e).update({User_ID:ie.FieldValue.delete()}),B}}(p||j(_)||F)&&await oi(ui,[e,_.User_ID]).catch(()=>{throw new Error("USER_ERROR")})};try{let x;if(p||j(_)||F){if(j(_)){x=await O.getUser(_.User_ID);const ve=x.customClaims;if(!(ve&&ve.role&&ve.collection&&ve.doc))throw new Error("USER_ERROR")}const Q={..._,...h};it(Q);const B=await jo(i?.operation||"update",a,e,l,f,Q,_,x,i?.permissions,N,i?.password);p&&(h.User_ID=B),F&&(h.User_ID=ie.FieldValue.delete())}s?.providedTransaction?await M(s.providedTransaction,x):await I.runTransaction(async Q=>{await M(Q,x)},{maxAttempts:10})}catch(x){throw(p||j(_)||F)&&await oi(ui,[e,_.User_ID]).catch(()=>{throw new Error("USER_ERROR")}),x}if(!s?.providedTransaction){const x={operation:"update",data:h,recordId:e,context:o,originalRecord:he(_)},Q={...x};await ae("postWrite",l,w,x),await ae("postOperation",l,w,Q)}const G={..._,...h};return it(G),{id:e,...G}},au=async(t,e,n)=>{const s=n?.userId;let r=n?.context;const i=Be(),o=St();let a=await qe();if(t.length===0)throw new Error("EMPTY_PATH");const l=t.at(-1),c=Object.keys(a.collections).includes(l),d=o.disabledCollections?.includes(l);if(!c||d)throw new Error("COLLECTION_NOT_FOUND");const m=a.collections[l],{labels:u,auth:f,enableWriteLog:g,softDelete:v}=m,y=He(u.collection,a),w=await oe(o.appName);if(v&&!f&&!n?.force)return await zo(t,e,{[v.archivedField]:!0,[v.timestampField]:ie.FieldValue.serverTimestamp()},{userId:s});const R=le();let O;s&&await lr(t,m);const I=xe(R,t,i);r=r||{},r.collection=u.collection;const E=await ze(t,e,{userId:s});let C=js("delete",t,E,a,w,"Online",s||"System",ie.Timestamp.now(),ie.FieldValue.serverTimestamp());const _=In(C);Ue(C),g&&await vt("delete","started",C,i,t,e,m),await ae("preOperation",o,y,{operation:"delete",data:C,recordId:e,context:r}),await ae("preWrite",o,y,{operation:"delete",data:C,recordId:e,context:r}),Ue(C);const p=async h=>{const[S,b,T,N]=await Promise.all([h.get(R.collection("system_deployment").doc("maintenance_mode")),ze(t,e,{userId:s,providedTransaction:h}),s?h.get(R.collection("tenants").doc(i).collection("system_user_permissions").doc(s)):Promise.resolve({}),qe()]);if(!S.exists)throw new Error("MAINTENANCE_MODE");if(S.data().active)throw new Error("MAINTENANCE_MODE");if(!b)throw new Error("NOT_FOUND");if(C={...b,..._},a=N,s){if(!T?.exists)throw new Error("PERMISSION_DENIED");if(O=T.data(),!O.Role)throw new Error("USER_ERROR");if(!O.Enabled)throw new Error("PERMISSION_DENIED");ta(C,e,m,a,s,O)}if(s&&O?.Role){const M=O.Role;if(!(y.custom?.serverAccess?.delete!==void 0?await oe(y.custom.serverAccess.delete,[M,C]):!0))throw new Error("PERMISSION_DENIED")}};m.auth&&(await R.runTransaction(async h=>{await p(h)},{maxAttempts:10}),C.User_ID&&await qt(C));try{await R.runTransaction(async h=>{await p(h);const S=Ws(a);Ks("delete",h,t,e,C,a,m,n,S,ie.FieldValue.arrayUnion,ie.FieldValue.arrayRemove,ie.FieldValue.delete,b=>R.collection("tenants").doc(i).collection("system_fields").doc(u.collection).collection(`${u.collection}-${b.name}`).doc(e),(b,T)=>R.collection("tenants").doc(i).collection("system_unique").doc(u.collection).collection(`Unique-${u.collection}-${b.name}`).doc(T),b=>R.collection("tenants").doc(i).collection("system_fields").doc(u.collection).collection(`${u.collection}-${b}`).doc(e),(b,T)=>xe(R,b,i).doc(T),(b,T,N)=>R.collection("tenants").doc(i).collection("system_fields").doc(b.collection).collection(`${b.collection}-${T}`).doc(N),(b,T,N)=>R.collection("tenants").doc(i).collection("system_fields").doc(b.collection).collection(`${b.collection}-${T.replaceAll(" ","-")}`).doc(N)),h.delete(I.doc(e))},{maxAttempts:10})}catch(h){const b=await ae("postWriteError",o,y,{operation:"delete",data:C,recordId:e,context:r,error:h});if(g&&(await new Promise(T=>{setTimeout(T,250)}),await vt("delete",b?.resolved?"success":"failed",C,i,t,e,m,b?.resolved?void 0:h)),!b?.resolved)throw m.auth&&C.User_ID&&await R.collection("tenants").doc(i).collection(u.collection).doc(e).update({User_ID:ie.FieldValue.delete()}),h}const F={operation:"delete",data:C,recordId:e,context:r},A={...F};return await ae("postWrite",o,y,F),await ae("postOperation",o,y,A),{id:e,...C}},lu=async(t,e)=>{const n=le(),s=er(),r=process.env.ADMIN_SMS;if(s==="development")if(r)t=r;else throw new Error("Admin phone not set");const i={to:t,body:e};await n.collection("system_messages").add(i)},cu=t=>t!==null&&typeof t=="object",uu=t=>{const e=n=>{Object.entries(n).forEach(([s,r])=>{r instanceof ie.Timestamp?n[s]={_seconds:r.seconds,_nanoseconds:r.nanoseconds}:cu(r)&&e(r)})};e(t)},du=async t=>await Ot.getAuth().getUser(t),fu=t=>{const e=tr();return L.fromJSDate(t).setZone(e)},Zo=t=>{const e=tr();return L.fromJSDate(t.toDate()).setZone(e)},hu=(t,e)=>{const s=L.fromJSDate(t,{zone:e}).offset,i=L.fromJSDate(t).offset,o=s-i;return L.fromJSDate(t).plus({minutes:o}).toJSDate()},mu=(t,e)=>{const s=L.fromJSDate(t,{zone:e}).offset,i=L.fromJSDate(t).offset,o=s-i;return L.fromJSDate(t).minus({minutes:o}).toJSDate()},pu=t=>"seconds"in t&&"nanoseconds"in t?Zo(new ie.Timestamp(t.seconds,t.nanoseconds)).toFormat("MMMM d, yyyy '@' h:mm a"):"";exports.addRecord=iu;exports.addUser=ar;exports.convertDateToTimezone=fu;exports.convertTimestampToTimezone=Zo;exports.deleteRecord=au;exports.deleteUser=qt;exports.deserializeDeleteSentinels=Kn;exports.deserializeTimestamps=cr;exports.deserializeTimestampsWithoutUnderscores=eu;exports.displayDate=pu;exports.fetchCurrentSchema=qe;exports.fetchLastSchema=Qc;exports.getCachedConfigValue=aa;exports.getCollectionRefs=Fo;exports.getCustomizationFile=He;exports.getCustomizationFiles=No;exports.getDocumentRefs=nr;exports.getFirestorePathRef=xe;exports.getGlobalConfigModule=St;exports.getInputSchema=jc;exports.getMaintenanceInfo=Gc;exports.getMode=er;exports.getOne=ze;exports.getSome=rr;exports.getStokerFirestore=le;exports.getTenant=Be;exports.getTimezone=tr;exports.getUser=du;exports.getVersionInfo=Hc;exports.getZodSchema=Si;exports.initializeFirebase=Jc;exports.initializeStoker=qc;exports.isDeleteSentinel=Te;exports.keepTimezone=hu;exports.removeTimezone=mu;exports.runChildProcess=Yc;exports.sendMail=ir;exports.sendMessage=lu;exports.serializeTimestamps=uu;exports.setTenant=Bc;exports.tryPromise=oe;exports.updateRecord=zo;exports.updateUser=jo;exports.validateRelations=ur;exports.validateSoftDelete=dr;exports.writeLog=vt;
|
|
14
|
+
<a href="${O}">${O}</a>`)}catch{throw await en(o.User_ID,a,c,g),new Error(g)}}}}}else if(t==="delete")try{await qt(o)}catch(y){throw new Error(y.message)}if(v)return v},ou=async(t,e,n)=>{const s=Be(),r=le(),i=[e];n&&n.trim()!==""&&i.push(n),i.sort();const o=[];for(const a of i){const l=r.collection("tenants").doc(s).collection("system_locks").doc(a);if((await t.get(l)).exists)throw new Error("DOCUMENT_LOCKED");o.push(l)}for(const a of o)t.set(a,{locked:!0})},ui=async(t,e)=>{const n=Be(),s=le(),r=[t];e&&e.trim()!==""&&r.push(e),r.sort();const i=s.batch();for(const o of r){const a=s.collection("tenants").doc(n).collection("system_locks").doc(o);i.delete(a)}await i.commit()},zo=async(t,e,n,s)=>{const r=s?.userId;let i=s?.user,o=s?.context;const a=Be(),l=St();if(s?.providedTransaction&&r)throw new Error("PERMISSION_DENIED");if(s?.providedSchema&&r)throw new Error("PERMISSION_DENIED");if(s?.providedTransaction&&!s?.originalRecord)throw new Error("PERMISSION_DENIED");let c=s?.providedSchema||await qe(!0);if(t.length===0)throw new Error("EMPTY_PATH");const d=t.at(-1),m=Object.keys(c.collections).includes(d),u=l.disabledCollections?.includes(d);if(!m||u)throw new Error("COLLECTION_NOT_FOUND");const f=c.collections[d],{labels:g,fields:v,enableWriteLog:y}=f,w=He(g.collection,c),R=await oe(l.appName),O=Ot.getAuth(),I=le();let E;r&&await lr(t,f);const C=xe(I,t,a);o=o||{},o.collection=g.collection;let _=s?.originalRecord||await ze(t,e,{userId:r,noComputedFields:!0,noEmbeddingFields:!0}),Z=_;r&&(Z=await ze(t,e,{noComputedFields:!0,noEmbeddingFields:!0}));for(const x of v)x.type==="Computed"&&(delete n[x.name],delete _[x.name]);if(c={...c,collections:Object.fromEntries(Object.entries(c.collections).map(([x,Q])=>[x,{...Q,fields:Q.fields.filter(B=>B.type!=="Computed")}]))},i&&!i.operation)throw new Error("VALIDATION_ERROR: User operation is required");if(i&&!f.auth)throw new Error("VALIDATION_ERROR: User operations are only permitted for auth collections");if(i?.operation==="delete"&&i.permissions)throw new Error("VALIDATION_ERROR: Permissions are not allowed for delete operations");const k=f.fields.filter(x=>x.saveToAuthToken&&!or(x.name)),p=f.auth&&i?.operation==="create",F=f.auth&&i?.operation==="delete",A=f.auth&&!p&&!F&&(i?.operation==="update"||n.Role||n.Enabled!==void 0||n.Name||n.Email||n.Photo_URL||k.some(x=>n[x.name]!==void 0)),j=x=>x.User_ID&&(f.auth&&!p&&!F&&i?.operation==="update"||n.Role&&n.Role!==x.Role||n.Enabled!==void 0&&n.Enabled!==x.Enabled||n.Name&&n.Name!==x.Name||n.Email&&n.Email!==x.Email||n.Photo_URL&&n.Photo_URL!==x.Photo_URL||k.some(Q=>n[Q.name]!==void 0&&n[Q.name]!==x[Q.name]));delete n.id,cr(n),Kn(n);const h=js("update",t,n,c,R,"Online",r||"System",ie.Timestamp.now(),ie.FieldValue.serverTimestamp()),S=he(h);for(const x of v)!X(x)&&Te(h[x.name])&&x.nullable&&(h[x.name]=null);Ue(h),Ue(_),pt(f,h,c),pt(f,_,c),y&&!s?.providedTransaction&&await vt("update","started",h,a,t,e,f,void 0,_);const b={operation:"update",data:h,recordId:e,context:o,originalRecord:he(_)};await ae("preOperation",l,w,b);const T={operation:"update",data:h,recordId:e,context:o,originalRecord:he(_)};await ae("preWrite",l,w,T),pt(f,h,c),xn(f,h),Ue(h);try{if(p&&!i?.password)throw new Error("Password is required");if(!s?.providedTransaction){const x={...Z,...h};await Lo("update",a,e,x,f,c),it(x),await an("update",x,f,w,{operation:"update",data:h,context:o,originalRecord:he(_)},c)}}catch(x){throw new Error(`VALIDATION_ERROR: ${x.message}`)}Ue(h),delete n.id,pt(f,h,c),xn(f,h);for(const x of v)!X(x)&&Te(h[x.name])&&x.nullable&&(h[x.name]=null);try{const x={...Z,...h};it(x),dr("update",f,h,_),Uo("update",h,S),await an("update",x,f,w,{operation:"update",data:h,context:o,originalRecord:he(_)},c)}catch(x){throw new Error(`VALIDATION_ERROR: ${x.message}`)}let N;i&&(i.permissions||={});const q=async(x,Q,B)=>{const[ve,Ve,Ge,me,Ye]=await Promise.all([s?.providedTransaction?Promise.resolve({}):x.get(I.collection("system_deployment").doc("latest_deploy")),s?.providedTransaction?Promise.resolve({}):x.get(I.collection("system_deployment").doc("maintenance_mode")),s?.providedTransaction?Promise.resolve(_):ze(t,e,{userId:r,providedTransaction:x,noComputedFields:!0}),r?x.get(I.collection("tenants").doc(a).collection("system_user_permissions").doc(r)):Promise.resolve({}),s?.providedSchema?Promise.resolve(s.providedSchema):qe()]);if(B&&(B.size+=3),!s?.providedTransaction){if(!ve.exists)throw new Error("VERSION_ERROR");const Oe=ve.data();if(Oe.force&&h.Last_Write_At.valueOf()<Oe.time.valueOf())throw new Error("VERSION_ERROR");if(!Ve.exists)throw new Error("MAINTENANCE_MODE");if(Ve.data().active)throw new Error("MAINTENANCE_MODE")}if(!Ge)throw new Error("NOT_FOUND");if(_=Ge,c=Ye,r){if(!me?.exists)throw new Error("PERMISSION_DENIED");if(E=me.data(),!E.Role)throw new Error("USER_ERROR");if(!E.Enabled)throw new Error("PERMISSION_DENIED")}if(B&&(B.size+=nr(a,t,e,c,E).length),p&&_.User_ID||F&&!_.User_ID)throw new Error("USER_ERROR");if(j(_)){const Oe=await x.get(I.collection("tenants").doc(a).collection("system_user_permissions").doc(_.User_ID));if(B&&B.size++,!Oe?.exists)throw new Error("PERMISSION_DENIED");N=Oe.data();const ut=i?.permissions&&Object.keys(i.permissions).length>0?i.permissions:void 0;if(h.Role&&h.Role!==_.Role&&!ut&&(i||={operation:"update",permissions:{}},i.permissions=he(N),i.permissions.Role=h.Role),i?.permissions){if(i.permissions.Role||=h.Role||_.Role,i.permissions.Enabled??=h.Enabled??_.Enabled,Te(i.permissions.Role))throw new Error("VALIDATION_ERROR: Role field is required");if(Te(i.permissions.Enabled))throw new Error("VALIDATION_ERROR: Enabled field is required")}}if(!s?.providedTransaction){const ut=v.filter(we=>"unique"in we&&we.unique).map(async we=>{if(!r||!we.access||we.access.includes(E.Role)){if(h[we.name]===void 0||Te(h[we.name]))return;const Bt=Ce(we,w),hn={..._,...h};if(!(r&&Bt?.custom?.serverAccess?.read!==void 0?await oe(Bt.custom.serverAccess.read,[E?.Role,hn]):!0))throw new Error("PERMISSION_DENIED");const Xn=h[we.name].toString().toLowerCase().replace(/\s/g,"---").replaceAll("/","|||");if(Xs(Xn)){if(B&&B.size++,B&&B.size>500)throw new Error("VALIDATION_ERROR: The number of operations in the Firestore transaction has exceeded the limit of 500. This is likely due to a large number of unique field checks.");const fr=await x.get(I.collection("tenants").doc(a).collection("system_unique").doc(g.collection).collection(`Unique-${g.collection}-${we.name}`).doc(Xn));if(fr.exists&&fr.data().id!==e)throw new Error(`VALIDATION_ERROR: ${we.name} "${h[we.name]}" already exists`)}else throw new Error(`VALIDATION_ERROR: ${we.name} "${h[we.name]}" is invalid`)}});await Promise.all(ut)}if(r&&E?.Role){const Oe=E.Role,ut={..._,...h};if(!(w.custom?.serverAccess?.update!==void 0?await oe(w.custom.serverAccess.update,[Oe,ut,_]):!0))throw new Error("PERMISSION_DENIED");for(const Bt of f.fields){const hn=Ce(Bt,w);if(Object.prototype.hasOwnProperty.call(h,Bt.name)&&hn?.custom?.serverAccess?.update!==void 0&&!await oe(hn.custom.serverAccess.update,[Oe,ut,_]))throw new Error("PERMISSION_DENIED")}}pr(h,_,e,f,c,r,E,i?.operation?i.operation:A?"update":void 0,i?.permissions,N),i?.permissions&&r&&E&&await Wo(x,i.permissions,r,E,c,N,B),pr(h,_,e,f,c,r,E,i?.operation?i.operation:A?"update":void 0,i?.permissions,N),Q&&(p||j(_)||F)&&await ou(x,e,_.User_ID)};(p||A||F)&&await I.runTransaction(async x=>{await q(x,!0)},{maxAttempts:10});const M=async(x,Q)=>{if(!_)throw new Error("NOT_FOUND");try{const B={size:1},ve={..._,...h};await q(x,!1,B);let Ve;!s?.noTwoWay&&!s?.providedTransaction&&(Ve=await ur("Update",a,e,ve,h,f,c,x,B,r,E,_));const Ge=Ws(c);Ks("update",x,t,e,h,c,f,s,Ge,ie.FieldValue.arrayUnion,ie.FieldValue.arrayRemove,ie.FieldValue.delete,me=>I.collection("tenants").doc(a).collection("system_fields").doc(g.collection).collection(`${g.collection}-${me.name}`).doc(e),(me,Ye)=>I.collection("tenants").doc(a).collection("system_unique").doc(g.collection).collection(`Unique-${g.collection}-${me.name}`).doc(Ye),me=>I.collection("tenants").doc(a).collection("system_fields").doc(g.collection).collection(`${g.collection}-${me}`).doc(e),(me,Ye)=>xe(I,me,a).doc(Ye),(me,Ye,Oe)=>I.collection("tenants").doc(a).collection("system_fields").doc(me.collection).collection(`${me.collection}-${Ye}`).doc(Oe),(me,Ye,Oe)=>I.collection("tenants").doc(a).collection("system_fields").doc(me.collection).collection(`${me.collection}-${Ye.replaceAll(" ","-")}`).doc(Oe),_,Ve,B),x.update(C.doc(e),h)}catch(B){if(s?.providedTransaction)throw B;{const ve={operation:"update",data:h,recordId:e,context:o,error:B,originalRecord:he(_)},Ve=await ae("postWriteError",l,w,ve);if(y&&(await new Promise(Ge=>{setTimeout(Ge,250)}),await vt("update",Ve?.resolved?"success":"failed",h,a,t,e,f,Ve?.resolved?void 0:B,_)),!Ve?.resolved)throw p&&await qt(_),j(_)&&await en(_.User_ID,Q,N,"USER_ERROR"),F&&await I.collection("tenants").doc(a).collection(g.collection).doc(e).update({User_ID:ie.FieldValue.delete()}),B}}(p||j(_)||F)&&await oi(ui,[e,_.User_ID]).catch(()=>{throw new Error("USER_ERROR")})};try{let x;if(p||j(_)||F){if(j(_)){x=await O.getUser(_.User_ID);const ve=x.customClaims;if(!(ve&&ve.role&&ve.collection&&ve.doc))throw new Error("USER_ERROR")}const Q={..._,...h};it(Q);const B=await jo(i?.operation||"update",a,e,l,f,Q,_,x,i?.permissions,N,i?.password);p&&(h.User_ID=B),F&&(h.User_ID=ie.FieldValue.delete())}s?.providedTransaction?await M(s.providedTransaction,x):await I.runTransaction(async Q=>{await M(Q,x)},{maxAttempts:10})}catch(x){throw(p||j(_)||F)&&await oi(ui,[e,_.User_ID]).catch(()=>{throw new Error("USER_ERROR")}),x}if(!s?.providedTransaction){const x={operation:"update",data:h,recordId:e,context:o,originalRecord:he(_)},Q={...x};await ae("postWrite",l,w,x),await ae("postOperation",l,w,Q)}const G={..._,...h};return it(G),{id:e,...G}},au=async(t,e,n)=>{const s=n?.userId;let r=n?.context;const i=Be(),o=St();let a=await qe();if(t.length===0)throw new Error("EMPTY_PATH");const l=t.at(-1),c=Object.keys(a.collections).includes(l),d=o.disabledCollections?.includes(l);if(!c||d)throw new Error("COLLECTION_NOT_FOUND");const m=a.collections[l],{labels:u,auth:f,enableWriteLog:g,softDelete:v}=m,y=He(u.collection,a),w=await oe(o.appName);if(v&&!f&&!n?.force)return await zo(t,e,{[v.archivedField]:!0,[v.timestampField]:ie.FieldValue.serverTimestamp()},{userId:s});const R=le();let O;s&&await lr(t,m);const I=xe(R,t,i);r=r||{},r.collection=u.collection;const E=await ze(t,e,{userId:s});let C=js("delete",t,E,a,w,"Online",s||"System",ie.Timestamp.now(),ie.FieldValue.serverTimestamp());const _=In(C);Ue(C),g&&await vt("delete","started",C,i,t,e,m),await ae("preOperation",o,y,{operation:"delete",data:C,recordId:e,context:r}),await ae("preWrite",o,y,{operation:"delete",data:C,recordId:e,context:r}),Ue(C);const p=async h=>{const[S,b,T,N]=await Promise.all([h.get(R.collection("system_deployment").doc("maintenance_mode")),ze(t,e,{userId:s,providedTransaction:h}),s?h.get(R.collection("tenants").doc(i).collection("system_user_permissions").doc(s)):Promise.resolve({}),qe()]);if(!S.exists)throw new Error("MAINTENANCE_MODE");if(S.data().active)throw new Error("MAINTENANCE_MODE");if(!b)throw new Error("NOT_FOUND");if(C={...b,..._},a=N,s){if(!T?.exists)throw new Error("PERMISSION_DENIED");if(O=T.data(),!O.Role)throw new Error("USER_ERROR");if(!O.Enabled)throw new Error("PERMISSION_DENIED");ta(C,e,m,a,s,O)}if(s&&O?.Role){const M=O.Role;if(!(y.custom?.serverAccess?.delete!==void 0?await oe(y.custom.serverAccess.delete,[M,C]):!0))throw new Error("PERMISSION_DENIED")}};m.auth&&(await R.runTransaction(async h=>{await p(h)},{maxAttempts:10}),C.User_ID&&await qt(C));try{await R.runTransaction(async h=>{await p(h);const S=Ws(a);Ks("delete",h,t,e,C,a,m,n,S,ie.FieldValue.arrayUnion,ie.FieldValue.arrayRemove,ie.FieldValue.delete,b=>R.collection("tenants").doc(i).collection("system_fields").doc(u.collection).collection(`${u.collection}-${b.name}`).doc(e),(b,T)=>R.collection("tenants").doc(i).collection("system_unique").doc(u.collection).collection(`Unique-${u.collection}-${b.name}`).doc(T),b=>R.collection("tenants").doc(i).collection("system_fields").doc(u.collection).collection(`${u.collection}-${b}`).doc(e),(b,T)=>xe(R,b,i).doc(T),(b,T,N)=>R.collection("tenants").doc(i).collection("system_fields").doc(b.collection).collection(`${b.collection}-${T}`).doc(N),(b,T,N)=>R.collection("tenants").doc(i).collection("system_fields").doc(b.collection).collection(`${b.collection}-${T.replaceAll(" ","-")}`).doc(N)),h.delete(I.doc(e))},{maxAttempts:10})}catch(h){const b=await ae("postWriteError",o,y,{operation:"delete",data:C,recordId:e,context:r,error:h});if(g&&(await new Promise(T=>{setTimeout(T,250)}),await vt("delete",b?.resolved?"success":"failed",C,i,t,e,m,b?.resolved?void 0:h)),!b?.resolved)throw m.auth&&C.User_ID&&await R.collection("tenants").doc(i).collection(u.collection).doc(e).update({User_ID:ie.FieldValue.delete()}),h}const F={operation:"delete",data:C,recordId:e,context:r},A={...F};return await ae("postWrite",o,y,F),await ae("postOperation",o,y,A),{id:e,...C}},lu=async(t,e)=>{const n=le(),s=er(),r=process.env.ADMIN_SMS;if(s==="development")if(r)t=r;else throw new Error("Admin phone not set");const i={to:t,body:e};await n.collection("system_messages").add(i)},cu=t=>t!==null&&typeof t=="object",uu=t=>{const e=n=>{Object.entries(n).forEach(([s,r])=>{r instanceof ie.Timestamp?n[s]={_seconds:r.seconds,_nanoseconds:r.nanoseconds}:cu(r)&&e(r)})};e(t)},du=async t=>await Ot.getAuth().getUser(t),fu=t=>{const e=tr();return L.fromJSDate(t).setZone(e)},Zo=t=>{const e=tr();return L.fromJSDate(t.toDate()).setZone(e)},hu=(t,e)=>{const s=L.fromJSDate(t,{zone:e}).offset,i=L.fromJSDate(t).offset,o=s-i;return L.fromJSDate(t).plus({minutes:o}).toJSDate()},mu=(t,e)=>{const s=L.fromJSDate(t,{zone:e}).offset,i=L.fromJSDate(t).offset,o=s-i;return L.fromJSDate(t).minus({minutes:o}).toJSDate()},pu=t=>"seconds"in t&&"nanoseconds"in t?Zo(new ie.Timestamp(t.seconds,t.nanoseconds)).toFormat("MMMM d, yyyy '@' h:mm a"):"";exports.addRecord=iu;exports.addUser=ar;exports.convertDateToTimezone=fu;exports.convertTimestampToTimezone=Zo;exports.deleteRecord=au;exports.deleteUser=qt;exports.deserializeDeleteSentinels=Kn;exports.deserializeTimestamps=cr;exports.deserializeTimestampsWithoutUnderscores=eu;exports.displayDate=pu;exports.fetchCurrentSchema=qe;exports.fetchLastSchema=Qc;exports.getCachedConfigValue=aa;exports.getCollectionRefs=Fo;exports.getCustomizationFile=He;exports.getCustomizationFiles=No;exports.getDocumentRefs=nr;exports.getFirestorePathRef=xe;exports.getGlobalConfigModule=St;exports.getInputSchema=jc;exports.getMaintenanceInfo=Gc;exports.getMode=er;exports.getOne=ze;exports.getSome=rr;exports.getStokerFirestore=le;exports.getTenant=Be;exports.getTimezone=tr;exports.getUser=du;exports.getVersionInfo=Hc;exports.getZodSchema=Si;exports.initializeFirebase=Jc;exports.initializeStoker=qc;exports.isDeleteSentinel=Te;exports.keepTimezone=hu;exports.removeTimezone=mu;exports.runChildProcess=Yc;exports.sendMail=ir;exports.sendMessage=lu;exports.serializeTimestamps=uu;exports.setTenant=Bc;exports.tryPromise=oe;exports.updateRecord=zo;exports.updateUser=jo;exports.validateRelations=ur;exports.validateSoftDelete=dr;exports.writeLog=vt;
|
package/dist/bundle.js
CHANGED
|
@@ -10569,7 +10569,7 @@ const Mo = async (t, e, n, s, r, i, o) => {
|
|
|
10569
10569
|
}
|
|
10570
10570
|
}
|
|
10571
10571
|
try {
|
|
10572
|
-
await on("update", C, O, I, { operation: "update",
|
|
10572
|
+
await on("update", C, O, I, { operation: "update", data: C, context: {}, originalRecord: C }, o);
|
|
10573
10573
|
} catch (k) {
|
|
10574
10574
|
throw new Error(`VALIDATION_ERROR: Two way relation update would invalidate record ${E} in ${w}: ${k.message}`);
|
|
10575
10575
|
}
|
|
@@ -10706,13 +10706,13 @@ const Mo = async (t, e, n, s, r, i, o) => {
|
|
|
10706
10706
|
try {
|
|
10707
10707
|
if (u.auth && r && !r.password)
|
|
10708
10708
|
throw new Error("Password is required");
|
|
10709
|
-
n?.providedTransaction || (await Co("create", o, k, p, u, l), await on("create", p, u, w, { operation: "create",
|
|
10709
|
+
n?.providedTransaction || (await Co("create", o, k, p, u, l), await on("create", p, u, w, { operation: "create", data: p, context: i }, l));
|
|
10710
10710
|
} catch (T) {
|
|
10711
10711
|
throw new Error(`VALIDATION_ERROR: ${T.message}`);
|
|
10712
10712
|
}
|
|
10713
10713
|
Ue(p), e.id = k, y && (p[y.archivedField] = !1), yt(u, p, l), xn(u, p);
|
|
10714
10714
|
try {
|
|
10715
|
-
Vo("create", p, F), Uo("create", u, p), await on("create", p, u, w, { operation: "create",
|
|
10715
|
+
Vo("create", p, F), Uo("create", u, p), await on("create", p, u, w, { operation: "create", data: p, context: i }, l);
|
|
10716
10716
|
} catch (T) {
|
|
10717
10717
|
throw new Error(`VALIDATION_ERROR: ${T.message}`);
|
|
10718
10718
|
}
|
|
@@ -11046,7 +11046,7 @@ const Mo = async (t, e, n, s, r, i, o) => {
|
|
|
11046
11046
|
throw new Error("Password is required");
|
|
11047
11047
|
if (!s?.providedTransaction) {
|
|
11048
11048
|
const x = { ...z, ...h };
|
|
11049
|
-
await Co("update", a, e, x, f, c), it(x), await on("update", x, f, w, { operation: "update",
|
|
11049
|
+
await Co("update", a, e, x, f, c), it(x), await on("update", x, f, w, { operation: "update", data: h, context: o, originalRecord: fe(_) }, c);
|
|
11050
11050
|
}
|
|
11051
11051
|
} catch (x) {
|
|
11052
11052
|
throw new Error(`VALIDATION_ERROR: ${x.message}`);
|
|
@@ -11056,7 +11056,7 @@ const Mo = async (t, e, n, s, r, i, o) => {
|
|
|
11056
11056
|
!X(x) && Re(h[x.name]) && x.nullable && (h[x.name] = null);
|
|
11057
11057
|
try {
|
|
11058
11058
|
const x = { ...z, ...h };
|
|
11059
|
-
it(x), Uo("update", f, h, _), Vo("update", h, S), await on("update", x, f, w, { operation: "update",
|
|
11059
|
+
it(x), Uo("update", f, h, _), Vo("update", h, S), await on("update", x, f, w, { operation: "update", data: h, context: o, originalRecord: fe(_) }, c);
|
|
11060
11060
|
} catch (x) {
|
|
11061
11061
|
throw new Error(`VALIDATION_ERROR: ${x.message}`);
|
|
11062
11062
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stoker-platform/node-client",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.86",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"files": [
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@google-cloud/storage": "^7.19.0",
|
|
27
|
-
"@stoker-platform/types": "0.5.
|
|
28
|
-
"@stoker-platform/utils": "0.5.
|
|
27
|
+
"@stoker-platform/types": "0.5.64",
|
|
28
|
+
"@stoker-platform/utils": "0.5.77",
|
|
29
29
|
"@types/luxon": "^3.7.1",
|
|
30
30
|
"cross-spawn": "^7.0.6",
|
|
31
31
|
"firebase-admin": "^13.10.0",
|