@lpdjs/firestore-repo-service 2.4.3 → 2.6.2-beta.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.
- package/README.md +92 -50
- package/dist/{create-servers-B9dTUhvR.d.cts → create-servers-B4GrBqdA.d.cts} +6 -6
- package/dist/{create-servers-BFhdPPeo.d.ts → create-servers-CVudVM8e.d.ts} +6 -6
- package/dist/{firebase-auth-D1APf9PA.d.cts → firebase-auth-Dpvrd8MP.d.cts} +13 -0
- package/dist/{firebase-auth-D1APf9PA.d.ts → firebase-auth-Dpvrd8MP.d.ts} +13 -0
- package/dist/history/index.cjs +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +10 -4
- package/dist/history/index.d.ts +10 -4
- package/dist/history/index.js +1 -1
- package/dist/history/index.js.map +1 -1
- package/dist/{index-BxurOEz1.d.ts → index-DzO9MfNI.d.cts} +9 -2
- package/dist/{index-BmagC7uw.d.cts → index-oFhGCBrY.d.ts} +9 -2
- package/dist/index.cjs +84 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +84 -84
- package/dist/index.js.map +1 -1
- package/dist/{openapi-ML_1hTx2.d.cts → openapi-B2w5tVRR.d.cts} +1 -1
- package/dist/{openapi-DIoQV_yQ.d.ts → openapi-DB8bXZB-.d.ts} +1 -1
- package/dist/{queue-xMOZxY0M.d.cts → queue-B8YUTnBT.d.cts} +20 -5
- package/dist/{queue-CVchaGAh.d.ts → queue-DYmbVDu5.d.ts} +20 -5
- package/dist/{read-BSyLao3I.d.cts → read-CTWZjxyh.d.cts} +1 -1
- package/dist/{read-BSyLao3I.d.ts → read-CTWZjxyh.d.ts} +1 -1
- package/dist/servers/admin/index.cjs +48 -48
- package/dist/servers/admin/index.cjs.map +1 -1
- package/dist/servers/admin/index.d.cts +4 -4
- package/dist/servers/admin/index.d.ts +4 -4
- package/dist/servers/admin/index.js +48 -48
- package/dist/servers/admin/index.js.map +1 -1
- package/dist/servers/auth/index.cjs +25 -12
- package/dist/servers/auth/index.cjs.map +1 -1
- package/dist/servers/auth/index.d.cts +1 -1
- package/dist/servers/auth/index.d.ts +1 -1
- package/dist/servers/auth/index.js +25 -12
- package/dist/servers/auth/index.js.map +1 -1
- package/dist/servers/crud/index.cjs +2 -2
- package/dist/servers/crud/index.cjs.map +1 -1
- package/dist/servers/crud/index.d.cts +6 -6
- package/dist/servers/crud/index.d.ts +6 -6
- package/dist/servers/crud/index.js +2 -2
- package/dist/servers/crud/index.js.map +1 -1
- package/dist/servers/hono/cli.cjs +142 -53
- package/dist/servers/hono/cli.cjs.map +1 -1
- package/dist/servers/hono/cli.js +142 -53
- package/dist/servers/hono/cli.js.map +1 -1
- package/dist/servers/hono/index.cjs +5 -5
- package/dist/servers/hono/index.cjs.map +1 -1
- package/dist/servers/hono/index.d.cts +241 -24
- package/dist/servers/hono/index.d.ts +241 -24
- package/dist/servers/hono/index.js +5 -5
- package/dist/servers/hono/index.js.map +1 -1
- package/dist/servers/index.cjs +98 -98
- package/dist/servers/index.cjs.map +1 -1
- package/dist/servers/index.d.cts +9 -9
- package/dist/servers/index.d.ts +9 -9
- package/dist/servers/index.js +98 -98
- package/dist/servers/index.js.map +1 -1
- package/dist/sync/bigquery.cjs +3 -3
- package/dist/sync/bigquery.cjs.map +1 -1
- package/dist/sync/bigquery.d.cts +18 -2
- package/dist/sync/bigquery.d.ts +18 -2
- package/dist/sync/bigquery.js +3 -3
- package/dist/sync/bigquery.js.map +1 -1
- package/dist/sync/index.cjs +37 -37
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +5 -5
- package/dist/sync/index.d.ts +5 -5
- package/dist/sync/index.js +37 -37
- package/dist/sync/index.js.map +1 -1
- package/dist/{types-5vgXdUM2.d.ts → types-BHZ-Gk-s.d.ts} +71 -4
- package/dist/{types-ChzVPw4k.d.ts → types-FLGn8CAI.d.ts} +15 -1
- package/dist/{types-BtdC0Qhu.d.cts → types-GvexCqrq.d.cts} +71 -4
- package/dist/{types-BgIGWlR1.d.cts → types-wcX7xfdo.d.cts} +15 -1
- package/package.json +2 -2
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { HttpsOptions } from 'firebase-functions/v2/https';
|
|
2
|
-
import { C as ConfiguredRepository, a as CrudServerOptions } from '../../types-
|
|
3
|
-
export { A as ApiResponse, B as BasicAuthConfig, b as CrudRepoConfig, c as CrudRepoEntry, d as CrudRepoRegistry, F as FieldRole, L as ListResponseData, M as Middleware, O as OpenAPISpecOptions, Q as QueryRequestBody, R as RepoFieldPath, e as RepoRelationKeys, U as UserFieldPath } from '../../types-
|
|
4
|
-
import { O as OpenAPIDocument } from '../../openapi-
|
|
5
|
-
export { g as generateOpenAPISpec } from '../../openapi-
|
|
6
|
-
import '../../firebase-auth-
|
|
2
|
+
import { C as ConfiguredRepository, a as CrudServerOptions } from '../../types-GvexCqrq.cjs';
|
|
3
|
+
export { A as ApiResponse, B as BasicAuthConfig, b as CrudRepoConfig, c as CrudRepoEntry, d as CrudRepoRegistry, F as FieldRole, L as ListResponseData, M as Middleware, O as OpenAPISpecOptions, Q as QueryRequestBody, R as RepoFieldPath, e as RepoRelationKeys, U as UserFieldPath } from '../../types-GvexCqrq.cjs';
|
|
4
|
+
import { O as OpenAPIDocument } from '../../openapi-B2w5tVRR.cjs';
|
|
5
|
+
export { g as generateOpenAPISpec } from '../../openapi-B2w5tVRR.cjs';
|
|
6
|
+
import '../../firebase-auth-Dpvrd8MP.cjs';
|
|
7
7
|
import 'zod';
|
|
8
|
-
import '../../read-
|
|
8
|
+
import '../../read-CTWZjxyh.cjs';
|
|
9
9
|
import 'firebase-admin/firestore';
|
|
10
10
|
import 'firebase-functions/v2/firestore';
|
|
11
11
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { HttpsOptions } from 'firebase-functions/v2/https';
|
|
2
|
-
import { C as ConfiguredRepository, a as CrudServerOptions } from '../../types-
|
|
3
|
-
export { A as ApiResponse, B as BasicAuthConfig, b as CrudRepoConfig, c as CrudRepoEntry, d as CrudRepoRegistry, F as FieldRole, L as ListResponseData, M as Middleware, O as OpenAPISpecOptions, Q as QueryRequestBody, R as RepoFieldPath, e as RepoRelationKeys, U as UserFieldPath } from '../../types-
|
|
4
|
-
import { O as OpenAPIDocument } from '../../openapi-
|
|
5
|
-
export { g as generateOpenAPISpec } from '../../openapi-
|
|
6
|
-
import '../../firebase-auth-
|
|
2
|
+
import { C as ConfiguredRepository, a as CrudServerOptions } from '../../types-BHZ-Gk-s.js';
|
|
3
|
+
export { A as ApiResponse, B as BasicAuthConfig, b as CrudRepoConfig, c as CrudRepoEntry, d as CrudRepoRegistry, F as FieldRole, L as ListResponseData, M as Middleware, O as OpenAPISpecOptions, Q as QueryRequestBody, R as RepoFieldPath, e as RepoRelationKeys, U as UserFieldPath } from '../../types-BHZ-Gk-s.js';
|
|
4
|
+
import { O as OpenAPIDocument } from '../../openapi-DB8bXZB-.js';
|
|
5
|
+
export { g as generateOpenAPISpec } from '../../openapi-DB8bXZB-.js';
|
|
6
|
+
import '../../firebase-auth-Dpvrd8MP.js';
|
|
7
7
|
import 'zod';
|
|
8
|
-
import '../../read-
|
|
8
|
+
import '../../read-CTWZjxyh.js';
|
|
9
9
|
import 'firebase-admin/firestore';
|
|
10
10
|
import 'firebase-functions/v2/firestore';
|
|
11
11
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {z as z$1}from'zod';import {Timestamp}from'firebase-admin/firestore';function Ce(e){let t=[],n=e.replace(/[.*+?^${}()|[\]\\]/g,i=>i===":"?i:`\\${i}`).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(i,s)=>(t.push(s),"([^/]+)"));return {pattern:new RegExp(`^${n}$`),paramNames:t}}function Se(e){let t=e.path??e.url??"/",n=t.indexOf("?");return n===-1?t:t.slice(0,n)}var W=class{constructor(){this.routes=[];this.middlewares=[];this.notFoundHandler=(t,n)=>{n.status(404).send("Not Found");};this.errorHandler=(t,n,i)=>{console.error("[MiniRouter]",t),i.status(500).send("Internal Server Error");};}use(t){return this.middlewares.push(t),this}get(t,n){return this.addRoute("GET",t,n)}post(t,n){return this.addRoute("POST",t,n)}put(t,n){return this.addRoute("PUT",t,n)}patch(t,n){return this.addRoute("PATCH",t,n)}delete(t,n){return this.addRoute("DELETE",t,n)}onNotFound(t){return this.notFoundHandler=t,this}onError(t){return this.errorHandler=t,this}addRoute(t,n,i){let{pattern:s,paramNames:d}=Ce(n);return this.routes.push({method:t.toUpperCase(),pattern:s,paramNames:d,handler:i}),this}async handle(t,n){let i=(t.method??"GET").toUpperCase(),s=Se(t),d=null,u={};for(let h of this.routes){if(h.method!==i)continue;let k=s.match(h.pattern);if(k){d=h,u={},h.paramNames.forEach((O,P)=>{u[O]=decodeURIComponent(k[P+1]??"");});break}}let y=Object.assign(t,{params:u}),g=d?d.handler:this.notFoundHandler;try{await this.runMiddlewareChain(y,n,g);}catch(h){this.errorHandler(h,t,n);}}async runMiddlewareChain(t,n,i){let s=0,d=async()=>{if(s<this.middlewares.length){let u=this.middlewares[s++];await u(t,n,d);}else await i(t,n);};await d();}};function V(e,t){let n=t==="/"?"":t.replace(/\/$/,"");if(process.env.FUNCTIONS_EMULATOR==="true"){let d=process.env.GCLOUD_PROJECT??process.env.GOOGLE_CLOUD_PROJECT??"demo-project",u=process.env.FUNCTION_REGION??"us-central1",y=(process.env.FUNCTION_TARGET??"").replace(/\./g,"-");return `/${d}/${u}/${y}${n}`}let i=process.env.K_SERVICE,s=e?.hostname??e?.headers?.host??"";return i&&typeof s=="string"&&s.includes("cloudfunctions.net")?`/${i.toLowerCase()}${n}`:n}function J(e){return !!e&&typeof e=="object"&&e.__authExtension===true}var de="preserve";function ue(){return de}function Ie(e){return typeof e=="object"&&e!==null&&typeof e._seconds=="number"&&typeof e._nanoseconds=="number"}function le(e){if(e==null)return null;if(e instanceof Date)return Number.isNaN(e.getTime())?null:e;if(e instanceof Timestamp)return e.toDate();if(Ie(e))return new Date(e._seconds*1e3+Math.floor(e._nanoseconds/1e6));if(typeof e=="string"){let t=new Date(e);return Number.isNaN(t.getTime())?null:t}if(typeof e=="number"){let t=new Date(e);return Number.isNaN(t.getTime())?null:t}return null}function pe(e){return e}var Ee=new Set(["<","<=",">",">=","!="]),Te=new Set(["array-contains","array-contains-any"]);function Y(e){return e==="desc"?"DESCENDING":"ASCENDING"}function De(e){let t=e.split("/").filter(Boolean);return t[t.length-1]??e}function Ne(e,t,n,i,s){let d=[],u=new Set;for(let g of i)if(g.op==="=="||g.op==="in"||g.op==="not-in"){if(u.has(g.field))continue;u.add(g.field),d.push({fieldPath:g.field,order:"ASCENDING"});}for(let g of i)if(Te.has(g.op)){if(u.has(g.field))continue;u.add(g.field),d.push({fieldPath:g.field,arrayConfig:"CONTAINS"});}for(let g of i)if(Ee.has(g.op)){if(u.has(g.field))continue;u.add(g.field);let h=s?.field===g.field?Y(s.dir):"ASCENDING";d.push({fieldPath:g.field,order:h});}if(s&&!u.has(s.field)&&d.push({fieldPath:s.field,order:Y(s.dir)}),d.length===1&&n)return Fe(e,t,d[0]);let y=s&&d.some(g=>g.fieldPath===s.field)?Y(s.dir):"ASCENDING";return d.push({fieldPath:"__name__",order:y}),je(e,t,n,d)}function je(e,t,n,i,s="(default)"){let d=`projects/${e}/databases/${s}/collectionGroups/${t}/indexes/_`,u=[...ne(1,d),...Z(2,n?2:1)];for(let h of i)u.push(...fe(3,ge(h)));let y=s==="(default)"?"-default-":s,g=encodeURIComponent(me(u));return `https://console.firebase.google.com/project/${e}/firestore/databases/${y}/indexes?create_composite=${g}`}function $e(e){return e.match(/https:\/\/console\.firebase\.google\.com[^\s)"]*/)?.[0]}function ee(e){let t=[],n=e>>>0;for(;n>=128;)t.push(n&127|128),n>>>=7;return t.push(n&127),t}function te(e,t){return e<<3|t}function ne(e,t){let n=Array.from(new TextEncoder().encode(t));return [te(e,2),...ee(n.length),...n]}function Z(e,t){return [te(e,0),...ee(t)]}function fe(e,t){return [te(e,2),...ee(t.length),...t]}function ge(e){let t=[...ne(1,e.fieldPath)];return e.arrayConfig==="CONTAINS"?t.push(...Z(3,1)):t.push(...Z(2,e.order==="DESCENDING"?2:1)),t}function me(e){let t=String.fromCharCode(...e),n;if(typeof Buffer<"u")n=Buffer.from(e).toString("base64");else if(typeof btoa<"u")n=btoa(t);else throw new Error("No base64 encoder available");return n.replace(/=+$/,"")}function Fe(e,t,n,i="(default)"){let s=`projects/${e}/databases/${i}/collectionGroups/${t}/fields/${n.fieldPath}`,d=[...ne(1,s),...Z(2,2),...fe(3,ge(n))],u=i==="(default)"?"-default-":i,y=encodeURIComponent(me(d));return `https://console.firebase.google.com/project/${e}/firestore/databases/${u}/indexes/automatic?create_exemption=${y}`}function _e(e){let t=e,n=[t?.firestore?.projectId,t?.firestore?.app?.options?.projectId,t?.firestore?._settings?.projectId,t?.firestore?.databaseId?.projectId,t?._firestore?.projectId];for(let s of n)if(typeof s=="string"&&s.length>0)return s;return process.env.GCLOUD_PROJECT||process.env.GOOGLE_CLOUD_PROJECT||process.env.FIREBASE_PROJECT_ID||void 0}function Be(e){let t=e;return t?t.code===9?true:typeof t.message=="string"?t.message.includes("requires an index"):false:false}function ye(e,t){let n=e??{},i=Be(e),s;if(i&&(s=n.message?$e(n.message):void 0,!s)){let d=_e(t.ref);if(d){let u=De(t.path);s=Ne(d,u,t.isGroup,t.filters,t.sort);}}return {type:i?"index":"error",message:i?"This query requires a composite index that does not exist yet.":n.message??"Query failed",indexUrl:s}}function oe(e,t,n=200){let i=pe(t);e.status(n).set("Content-Type","application/json; charset=utf-8").send(JSON.stringify(i));}function U(e,t,n,i=200){oe(e,{success:true,data:t,meta:n},i);}function x(e,t,n=400){oe(e,{success:false,error:t},n);}function re(e,t,n,i,s){let d=ye(t,n),u=d.type==="index",y=u?424:500,h={success:false,error:u?d.message:s&&t instanceof Error?t.message:i};u&&(h.errorType="index",d.indexUrl&&(h.indexUrl=d.indexUrl)),oe(e,h,y);}var he="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";function ze(){let e="";for(let t=0;t<20;t++)e+=he.charAt(Math.floor(Math.random()*he.length));return e}function q(e){let t=e._def??e.def;if(!t)return e;let n=t.typeName??t.type;if(n==="ZodDate"||n==="date")return z$1.preprocess(i=>le(i)??i,e);if(n==="ZodObject"||n==="object"){let i=e.shape,s={};for(let[d,u]of Object.entries(i))s[d]=q(u);return z$1.object(s)}if(n==="ZodArray"||n==="array"){let i=t.element??t.type;if(i)return z$1.array(q(i))}if(n==="ZodOptional"||n==="optional"){let i=t.innerType;if(i)return q(i).optional()}if(n==="ZodNullable"||n==="nullable"){let i=t.innerType;if(i)return q(i).nullable()}if(n==="ZodDefault"||n==="default"){let i=t.innerType,s=t.defaultValue;if(i){let d=q(i);return typeof s=="function"?d.default(s()):d.default(s)}}return e}function He(e,t,n=[]){let i=e.shape,s={},d=t&&t.length>0?t:Object.keys(i);for(let u of d){if(n.includes(u))continue;let y=u.split(".")[0];y&&i[y]&&(s[y]=i[y]);}return z$1.object(s)}function be(e,t,n,i=false,s=[]){try{let d=He(e,n,s),u=i?d.partial():d;return {success:!0,data:(ue()==="normalize"?q(u):u).parse(t)}}catch(d){return d instanceof z$1.ZodError?{success:false,error:`Validation failed: ${d.issues.map(y=>`${y.path.join(".")}: ${y.message}`).join(", ")}`}:{success:false,error:"Validation failed"}}}function Le(e,t){let n=[],i=t?new Set(t):null,s={eq:"==",ne:"!=",lt:"<",lte:"<=",gt:">",gte:">=",in:"in",nin:"not-in",contains:"array-contains",containsAny:"array-contains-any"};for(let[d,u]of Object.entries(e)){if(u===void 0||["cursor","limit","pageSize","orderBy","orderDir","select"].includes(d))continue;let y=Array.isArray(u)?u[0]:u;if(y===void 0||y==="")continue;let g=d.match(/^(\w+)__(\w+)$/),h,k="==";if(g&&g[1]&&g[2]){h=g[1];let P=g[2];if(s[P])k=s[P];else continue}else if(!g)h=d;else continue;if(i&&!i.has(h))continue;let O=y;k==="in"||k==="not-in"||k==="array-contains-any"?O=y.split(",").map(P=>we(P.trim())):O=we(y),n.push({field:h,op:k,value:O});}return n}function we(e){if(e==="true")return true;if(e==="false")return false;if(e==="null")return null;let t=Number(e);return !isNaN(t)&&e!==""?t:e}function Q(e){return e?{docId:e.id}:null}async function xe(e,t){if(!t||typeof t!="object")return;let n=t.docId;if(typeof n=="string")try{let i=e.repo.ref;if(typeof i.doc!="function")return;let s=await i.doc(n).get();return s.exists?s:void 0}catch{return}}function Re(e,t,n,i=false){async function s(f,o,a,r){if(!i)return true;let l=o.rules?.[a];if(!l)return x(f,`Operation "${a}" is not allowed for this repository`,403),false;try{return await l(r)?!0:(x(f,"Forbidden",403),!1)}catch(p){let m=n&&p instanceof Error?p.message:"Forbidden";return x(f,m,403),false}}async function d(f,o,a,r){if(!i)return r;let l=f.rules?.filter;if(!l)return r;let p=[];for(let m of r)try{await l({user:o,doc:m,params:a})&&p.push(m);}catch{}return p}function u(f){return f?.user??null}function y(f,o){return !f||!e[f]?(x(o,`Repository "${f}" not found`,404),null):e[f]}function g(f,o){if(!o)return;let a=f[o];if(typeof a!="string"||!a)return;let r=a.split("/").filter(Boolean),l=[];for(let p=1;p<r.length;p+=2)l.push(r[p]);return l.length>0?l:void 0}async function h(f,o){let a=`by${f.documentKey.charAt(0).toUpperCase()}${f.documentKey.slice(1)}`,r=f.repo.get[a];if(typeof r=="function")try{let p=await r(o);if(p)return p}catch{}return (await f.repo.query.by({where:[[f.documentKey,"==",o]],limit:1}))[0]??null}async function k(f,o){let a=f.params||{},r=y(a.repoName,o);if(!r)return;let l=u(f);if(!await s(o,r,"list",{user:l,query:f.query??{},params:a}))return;let p=[],m;try{let c=f.query??{},A=Math.min(Number(c.pageSize)||r.pageSize,100),w=c.cursor,C=c.direction?.toLowerCase()==="prev"?"prev":"next",S=c.orderBy,v=c.orderDir?.toLowerCase()==="desc"?"desc":"asc",$=c.select,I=$?$.split(",").map(D=>D.trim()):void 0,R;r.allowedIncludes&&c.includes&&(R=(typeof c.includes=="string"?c.includes.split(",").map(K=>K.trim()):Array.isArray(c.includes)?c.includes:[]).filter(K=>typeof K=="string"&&r.allowedIncludes.includes(K)),R?.length===0&&(R=void 0));let T=Le(c,r.filterableFields);p=T.map(D=>({field:D.field,op:D.op,value:String(D.value??"")})),S&&(m={field:S,dir:v});let F={pageSize:A,direction:C};if(w)try{let D=typeof w=="string"?JSON.parse(w):w;F.cursor=await xe(r,D);}catch{}S&&(F.orderBy=[{field:S,direction:v}]),T.length>0&&(F.where=T.map(D=>[D.field,D.op,D.value])),I&&(F.select=I),R&&(F.include=R);let L=await r.repo.query.paginate(F),Ae={items:await d(r,u(f),a,L.data),hasNextPage:L.hasNextPage,hasPrevPage:L.hasPrevPage,nextCursor:Q(L.nextCursor),prevCursor:Q(L.prevCursor)};U(o,Ae,{pageSize:A,hasMore:L.hasNextPage});}catch(c){re(o,c,{ref:r.repo.ref,path:r.path,isGroup:!!r.isGroup,filters:p,sort:m},"Failed to fetch documents",n);}}async function O(f,o){let a=f.params||{},r=y(a.repoName,o);if(!r)return;let l=u(f);if(!await s(o,r,"list",{user:l,query:f.body??{},params:a}))return;let p=[],m;try{let c=f.body??{},A=Math.min(c.pageSize||r.pageSize,100),w=c.direction==="prev"?"prev":"next";c.where&&(p=c.where.map(I=>({field:String(I[0]),op:I[1],value:String(I[2]??"")}))),c.orderBy&&c.orderBy[0]&&(m={field:c.orderBy[0].field,dir:c.orderBy[0].direction==="desc"?"desc":"asc"});let C={pageSize:A,direction:w};if(c.cursor)try{let I=typeof c.cursor=="string"?JSON.parse(c.cursor):c.cursor;C.cursor=await xe(r,I);}catch{}if(r.allowedIncludes&&c.includes&&c.includes.length>0){let I=c.includes.filter(R=>typeof R=="string"?r.allowedIncludes.includes(R):typeof R=="object"&&R!==null&&"relation"in R&&typeof R.relation=="string"?r.allowedIncludes.includes(R.relation):!1);I.length>0&&(C.include=I);}if(c.where&&c.where.length>0){if(r.filterableFields){let I=new Set(r.filterableFields),R=c.where.filter(T=>!I.has(T[0]));if(R.length>0){x(o,`Fields not filterable: ${R.map(T=>T[0]).join(", ")}`,400);return}}C.where=c.where;}if(c.orWhere&&c.orWhere.length>0){if(r.filterableFields){let I=new Set(r.filterableFields),R=c.orWhere.filter(T=>!I.has(T[0]));if(R.length>0){x(o,`Fields not filterable: ${R.map(T=>T[0]).join(", ")}`,400);return}}C.orWhere=c.orWhere;}if(c.orWhereGroups&&c.orWhereGroups.length>0){if(r.filterableFields){let I=new Set(r.filterableFields);for(let R of c.orWhereGroups){let T=R.filter(F=>!I.has(F[0]));if(T.length>0){x(o,`Fields not filterable: ${T.map(F=>F[0]).join(", ")}`,400);return}}}C.orWhereGroups=c.orWhereGroups;}c.orderBy&&c.orderBy.length>0&&(C.orderBy=c.orderBy),c.select&&c.select.length>0&&(C.select=c.select);let S=await r.repo.query.paginate(C),$={items:await d(r,u(f),a,S.data),hasNextPage:S.hasNextPage,hasPrevPage:S.hasPrevPage,nextCursor:Q(S.nextCursor),prevCursor:Q(S.prevCursor)};U(o,$,{pageSize:A,hasMore:S.hasNextPage});}catch(c){re(o,c,{ref:r.repo.ref,path:r.path,isGroup:!!r.isGroup,filters:p,sort:m},"Failed to query documents",n);}}async function P(f,o){let a=f.params||{},r=y(a.repoName,o);if(!r)return;let l=a.id;if(!l){x(o,"Document ID required",400);return}try{let p=await h(r,l);if(!p){x(o,"Document not found",404);return}let m=u(f);if(!await s(o,r,"get",{user:m,doc:p,params:a}))return;if(i&&r.rules?.filter)try{if(!await r.rules.filter({user:m,doc:p,params:a})){x(o,"Document not found",404);return}}catch{x(o,"Document not found",404);return}U(o,p);}catch(p){re(o,p,{ref:r.repo.ref,path:r.path,isGroup:!!r.isGroup,filters:[{field:r.documentKey,op:"==",value:l}]},"Failed to fetch document",n);}}async function j(f,o){let a=f.params||{},r=y(a.repoName,o);if(r)try{let l=f.body??{},p=u(f);if(!await s(o,r,"create",{user:p,body:l,params:a}))return;let m=be(r.schema,l,r.createFields,!1,r.systemKeys);if(!m.success){x(o,m.error,400);return}if(r.validate){let A=await r.validate(m.data,"create");if(A){x(o,A,400);return}}let c;if(r.isGroup&&r.parentKeys&&r.parentKeys.length>0){let A={...m.data};r.createdKey&&(A[r.createdKey]=new Date);let w=r.parentKeys.filter(v=>!A[v]);if(w.length>0){x(o,`Missing parent key(s) for subcollection create: ${w.join(", ")}`,400);return}let C=r.parentKeys.map(v=>A[v]),S=A[r.documentKey]||ze();c=await r.repo.set(...C,S,A);}else c=await r.repo.create(m.data);U(o,c,void 0,201);}catch(l){let p=n&&l instanceof Error?l.message:"Failed to create document";x(o,p,500);}}async function H(f,o,a){let r=f.params||{},l=y(r.repoName,o);if(!l)return;let p=r.id;if(!p){x(o,"Document ID required",400);return}try{let m=f.body??{},c=await h(l,p);if(!c){x(o,"Document not found",404);return}let A=u(f);if(!await s(o,l,"update",{user:A,doc:c,body:m,params:r}))return;let w=be(l.schema,m,l.mutableFields,a,l.systemKeys);if(!w.success){x(o,w.error,400);return}if(l.validate){let v=await l.validate(w.data,"update");if(v){x(o,v,400);return}}let C=g(c,l.pathKey)??[p],S=await l.repo.update(...C,w.data);U(o,S);}catch(m){let c=n&&m instanceof Error?m.message:"Failed to update document";x(o,c,500);}}async function B(f,o){let a=f.params||{},r=y(a.repoName,o);if(!r)return;if(!r.allowDelete){x(o,"Delete not allowed for this repository",403);return}let l=a.id;if(!l){x(o,"Document ID required",400);return}try{let p=await h(r,l);if(!p){x(o,"Document not found",404);return}let m=u(f);if(!await s(o,r,"delete",{user:m,doc:p,params:a}))return;let c=g(p,r.pathKey)??[l];await r.repo.delete(...c),U(o,{deleted:!0});}catch(p){let m=n&&p instanceof Error?p.message:"Failed to delete document";x(o,m,500);}}function b(f,o){o.status(204).set("Access-Control-Allow-Methods","GET, POST, PUT, PATCH, DELETE, OPTIONS").set("Access-Control-Allow-Headers","Content-Type, Authorization").set("Access-Control-Max-Age","86400").send("");}return {handleList:k,handleQuery:O,handleGet:P,handleCreate:j,handleUpdate:H,handleDelete:B,handleOptions:b}}function se(e){try{return z$1.toJSONSchema(e,{target:"openapi-3.1",unrepresentable:"any",override:t=>{let n=t.zodSchema?._zod?.def;n&&(n.type==="date"?(t.jsonSchema.type="string",t.jsonSchema.format="date-time"):n.type==="bigint"&&(t.jsonSchema.type="string",t.jsonSchema.format="int64"));}})}catch(t){return typeof console<"u"&&console.warn&&console.warn("[generateOpenAPISpec] Failed to convert Zod schema to JSON Schema; falling back to {type:object}.",t),{type:"object"}}}function N(e){return {$ref:`#/components/schemas/${e}`}}function E(e){return {description:e,content:{"application/json":{schema:N("ErrorResponse")}}}}function M(e,t){return {description:e,content:{"application/json":{schema:{type:"object",properties:{success:{type:"boolean",enum:[true]},data:t},required:["success","data"]}}}}}function ke(e){return {description:"Paginated list of documents",content:{"application/json":{schema:{type:"object",properties:{success:{type:"boolean",enum:[true]},data:{type:"object",properties:{items:{type:"array",items:e},nextCursor:{oneOf:[{type:"object"},{type:"null"}]},prevCursor:{oneOf:[{type:"object"},{type:"null"}]},hasNextPage:{type:"boolean"},hasPrevPage:{type:"boolean"}},required:["items","hasNextPage","hasPrevPage"]},meta:{type:"object",properties:{pageSize:{type:"integer"},hasMore:{type:"boolean"},cursor:{oneOf:[{type:"string"},{type:"null"}]}}}},required:["success","data"]}}}}}function Ue(e){return [{name:"pageSize",in:"query",schema:{type:"integer",default:e.pageSize,maximum:100},description:"Number of items per page"},{name:"cursor",in:"query",schema:{type:"string"},description:"Base64 pagination cursor"},{name:"orderBy",in:"query",schema:{type:"string"},description:"Field name to order by"},{name:"orderDir",in:"query",schema:{type:"string",enum:["asc","desc"]},description:"Order direction"},{name:"select",in:"query",schema:{type:"string"},description:"Comma-separated list of fields to return"}]}function qe(e){let t=e.filterableFields??Object.keys(e.schema.shape),n=["eq","ne","lt","lte","gt","gte","in","nin","contains"],i=[];for(let s of t){i.push({name:s,in:"query",schema:{type:"string"},description:`Filter by ${s} (equality)`});for(let d of n)i.push({name:`${s}__${d}`,in:"query",schema:{type:"string"},description:`Filter ${s} with operator ${d}`});}return i}function Ge(){return {type:"object",properties:{where:{type:"array",items:{type:"array",items:{},minItems:3,maxItems:3},description:"AND conditions: [field, operator, value][]"},orWhere:{type:"array",items:{type:"array",items:{},minItems:3,maxItems:3},description:"Simple OR conditions (each independently OR'd)"},orWhereGroups:{type:"array",items:{type:"array",items:{type:"array",items:{},minItems:3,maxItems:3}},description:"Advanced OR groups (AND within, OR across groups)"},orderBy:{type:"array",items:{type:"object",properties:{field:{type:"string"},direction:{type:"string",enum:["asc","desc"]}},required:["field"]}},select:{type:"array",items:{type:"string"},description:"Fields to select (projection)"},pageSize:{type:"integer",maximum:100,description:"Number of items per page"},cursor:{oneOf:[{type:"string"},{type:"object"}],description:"Pagination cursor"},direction:{type:"string",enum:["next","prev"],description:"Pagination direction"},includes:{type:"array",items:{oneOf:[{type:"string"},{type:"object",properties:{relation:{type:"string"},select:{type:"array",items:{type:"string"}}},required:["relation"]}]},description:"Relations to include (populate)"}}}}function Me(e,t,n,i,s){let d={},u=e.name,y=`${t}/${e.name}`,g=`${y}/{${e.documentKey}}`,h={name:e.documentKey,in:"path",required:true,schema:{type:"string"},description:"Unique document identifier"};d[y]={get:{operationId:`list${z(e.name)}`,summary:`List ${e.name} (paginated)`,tags:[u],parameters:[...Ue(e),...qe(e)],responses:{200:ke(N(n)),500:E("Internal server error")}},post:{operationId:`create${z(e.name)}`,summary:`Create a ${_(e.name)}`,tags:[u],requestBody:{required:true,content:{"application/json":{schema:N(i??n)}}},responses:{201:M("Document created",N(n)),400:E("Validation error"),500:E("Internal server error")}}},d[`${y}/query`]={post:{operationId:`query${z(e.name)}`,summary:`Query ${e.name} with advanced filters`,tags:[u],requestBody:{required:true,content:{"application/json":{schema:N("QueryRequestBody")}}},responses:{200:ke(N(n)),400:E("Invalid query"),500:E("Internal server error")}}};let k={};return k.get={operationId:`get${z(_(e.name))}`,summary:`Get a single ${_(e.name)}`,tags:[u],parameters:[h],responses:{200:M("Document found",N(n)),404:E("Document not found"),500:E("Internal server error")}},k.put={operationId:`update${z(_(e.name))}`,summary:`Update a ${_(e.name)} (full replace)`,tags:[u],parameters:[h],requestBody:{required:true,content:{"application/json":{schema:N(s??n)}}},responses:{200:M("Document updated",N(n)),400:E("Validation error"),404:E("Document not found"),500:E("Internal server error")}},k.patch={operationId:`patch${z(_(e.name))}`,summary:`Partially update a ${_(e.name)}`,tags:[u],parameters:[h],requestBody:{required:true,content:{"application/json":{schema:{allOf:[N(s??n)],description:"All fields are optional for partial updates"}}}},responses:{200:M("Document patched",N(n)),400:E("Validation error"),404:E("Document not found"),500:E("Internal server error")}},e.allowDelete&&(k.delete={operationId:`delete${z(_(e.name))}`,summary:`Delete a ${_(e.name)}`,tags:[u],parameters:[h],responses:{200:M("Document deleted",{type:"object",properties:{id:{type:"string"}}}),404:E("Document not found"),500:E("Internal server error")}}),d[g]=k,d}function ae(e,t,n={}){let{title:i="CRUD API",version:s="1.0.0",description:d,servers:u,auth:y=false}=n,g=t==="/"?"":t.replace(/\/$/,""),h={},k={},O=[];h.ErrorResponse={type:"object",properties:{success:{type:"boolean",enum:[false]},error:{type:"string"}},required:["success","error"]},h.QueryRequestBody=Ge();for(let[B,b]of Object.entries(e)){let f=z(_(B)),o=`${f}Create`,a=`${f}Update`;h[f]=se(b.schema);let r=w=>{let C=w&&w.length>0?w:Object.keys(b.schema.shape),S={};for(let v of C){let $=v.split(".")[0];$&&b.schema.shape[$]&&!b.systemKeys.includes($)&&(S[$]=b.schema.shape[$]);}return S},l=null,p=r(b.createFields);Object.keys(p).length>0&&(h[o]=se(z$1.object(p)),l=o);let m=null,c=r(b.mutableFields);Object.keys(c).length>0&&(h[a]=se(z$1.object(c)),m=a);let A=Me(b,g,f,l,m);Object.assign(k,A),O.push({name:B,description:`Operations on ${B} (collection: ${b.path})`});}let P={},j;return y==="basic"?(P.basicAuth={type:"http",scheme:"basic"},j=[{basicAuth:[]}]):y==="bearer"&&(P.bearerAuth={type:"http",scheme:"bearer",bearerFormat:"JWT"},j=[{bearerAuth:[]}]),{openapi:"3.1.0",info:{title:i,version:s,...d?{description:d}:{}},...u&&u.length>0?{servers:u}:{},paths:k,components:{schemas:h,...Object.keys(P).length>0?{securitySchemes:P}:{}},...j?{security:j}:{},tags:O}}function z(e){return e.charAt(0).toUpperCase()+e.slice(1)}function _(e){return e.endsWith("ies")?e.slice(0,-3)+"y":e.endsWith("ses")||e.endsWith("xes")||e.endsWith("zes")?e.slice(0,-2):e.endsWith("s")&&!e.endsWith("ss")?e.slice(0,-1):e}function Ke(e,t){return `<!DOCTYPE html>
|
|
1
|
+
import {randomBytes}from'crypto';import {z}from'zod';import {Timestamp}from'firebase-admin/firestore';function Pe(e){let t=[],r=e.replace(/[.*+?^${}()|[\]\\]/g,o=>o===":"?o:`\\${o}`).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(o,s)=>(t.push(s),"([^/]+)"));return {pattern:new RegExp(`^${r}$`),paramNames:t}}function ve(e){let t=e.path??e.url??"/",r=t.indexOf("?");return r===-1?t:t.slice(0,r)}var W=class{constructor(){this.routes=[];this.middlewares=[];this.notFoundHandler=(t,r)=>{r.status(404).send("Not Found");};this.errorHandler=(t,r,o)=>{console.error("[MiniRouter]",t),o.status(500).send("Internal Server Error");};}use(t){return this.middlewares.push(t),this}get(t,r){return this.addRoute("GET",t,r)}post(t,r){return this.addRoute("POST",t,r)}put(t,r){return this.addRoute("PUT",t,r)}patch(t,r){return this.addRoute("PATCH",t,r)}delete(t,r){return this.addRoute("DELETE",t,r)}onNotFound(t){return this.notFoundHandler=t,this}onError(t){return this.errorHandler=t,this}addRoute(t,r,o){let{pattern:s,paramNames:i}=Pe(r);return this.routes.push({method:t.toUpperCase(),pattern:s,paramNames:i,handler:o}),this}async handle(t,r){let o=(t.method??"GET").toUpperCase(),s=ve(t),i=null,d={};for(let g of this.routes){if(g.method!==o)continue;let A=s.match(g.pattern);if(A){i=g,d={},g.paramNames.forEach((N,C)=>{d[N]=decodeURIComponent(A[C+1]??"");});break}}let y=Object.assign(t,{params:d}),h=i?i.handler:this.notFoundHandler;try{await this.runMiddlewareChain(y,r,h);}catch(g){this.errorHandler(g,t,r);}}async runMiddlewareChain(t,r,o){let s=0,i=async()=>{if(s<this.middlewares.length){let d=this.middlewares[s++];await d(t,r,i);}else await o(t,r);};await i();}};function V(e,t){let r=t==="/"?"":t.replace(/\/$/,"");if(process.env.FUNCTIONS_EMULATOR==="true"){let i=process.env.GCLOUD_PROJECT??process.env.GOOGLE_CLOUD_PROJECT??"demo-project",d=process.env.FUNCTION_REGION??"us-central1",y=(process.env.FUNCTION_TARGET??"").replace(/\./g,"-");return `/${i}/${d}/${y}${r}`}let o=process.env.K_SERVICE,s=e?.hostname??e?.headers?.host??"";return o&&typeof s=="string"&&s.includes("cloudfunctions.net")?`/${o.toLowerCase()}${r}`:r}function J(e){return !!e&&typeof e=="object"&&e.__authExtension===true}var ue="preserve";function le(){return ue}function Te(e){return typeof e=="object"&&e!==null&&typeof e._seconds=="number"&&typeof e._nanoseconds=="number"}function pe(e){if(e==null)return null;if(e instanceof Date)return Number.isNaN(e.getTime())?null:e;if(e instanceof Timestamp)return e.toDate();if(Te(e))return new Date(e._seconds*1e3+Math.floor(e._nanoseconds/1e6));if(typeof e=="string"){let t=new Date(e);return Number.isNaN(t.getTime())?null:t}if(typeof e=="number"){let t=new Date(e);return Number.isNaN(t.getTime())?null:t}return null}function fe(e){return e}var Ne=new Set(["<","<=",">",">=","!="]),$e=new Set(["array-contains","array-contains-any"]);function Y(e){return e==="desc"?"DESCENDING":"ASCENDING"}function je(e){let t=e.split("/").filter(Boolean);return t[t.length-1]??e}function Fe(e,t,r,o,s){let i=[],d=new Set;for(let h of o)if(h.op==="=="||h.op==="in"||h.op==="not-in"){if(d.has(h.field))continue;d.add(h.field),i.push({fieldPath:h.field,order:"ASCENDING"});}for(let h of o)if($e.has(h.op)){if(d.has(h.field))continue;d.add(h.field),i.push({fieldPath:h.field,arrayConfig:"CONTAINS"});}for(let h of o)if(Ne.has(h.op)){if(d.has(h.field))continue;d.add(h.field);let g=s?.field===h.field?Y(s.dir):"ASCENDING";i.push({fieldPath:h.field,order:g});}if(s&&!d.has(s.field)&&i.push({fieldPath:s.field,order:Y(s.dir)}),i.length===1&&r)return Be(e,t,i[0]);let y=s&&i.some(h=>h.fieldPath===s.field)?Y(s.dir):"ASCENDING";return i.push({fieldPath:"__name__",order:y}),_e(e,t,r,i)}function _e(e,t,r,o,s="(default)"){let i=`projects/${e}/databases/${s}/collectionGroups/${t}/indexes/_`,d=[...ne(1,i),...Z(2,r?2:1)];for(let g of o)d.push(...ge(3,me(g)));let y=s==="(default)"?"-default-":s,h=encodeURIComponent(ye(d));return `https://console.firebase.google.com/project/${e}/firestore/databases/${y}/indexes?create_composite=${h}`}function He(e){return e.match(/https:\/\/console\.firebase\.google\.com[^\s)"]*/)?.[0]}function ee(e){let t=[],r=e>>>0;for(;r>=128;)t.push(r&127|128),r>>>=7;return t.push(r&127),t}function te(e,t){return e<<3|t}function ne(e,t){let r=Array.from(new TextEncoder().encode(t));return [te(e,2),...ee(r.length),...r]}function Z(e,t){return [te(e,0),...ee(t)]}function ge(e,t){return [te(e,2),...ee(t.length),...t]}function me(e){let t=[...ne(1,e.fieldPath)];return e.arrayConfig==="CONTAINS"?t.push(...Z(3,1)):t.push(...Z(2,e.order==="DESCENDING"?2:1)),t}function ye(e){let t=String.fromCharCode(...e),r;if(typeof Buffer<"u")r=Buffer.from(e).toString("base64");else if(typeof btoa<"u")r=btoa(t);else throw new Error("No base64 encoder available");return r.replace(/=+$/,"")}function Be(e,t,r,o="(default)"){let s=`projects/${e}/databases/${o}/collectionGroups/${t}/fields/${r.fieldPath}`,i=[...ne(1,s),...Z(2,2),...ge(3,me(r))],d=o==="(default)"?"-default-":o,y=encodeURIComponent(ye(i));return `https://console.firebase.google.com/project/${e}/firestore/databases/${d}/indexes/automatic?create_exemption=${y}`}function ze(e){let t=e,r=[t?.firestore?.projectId,t?.firestore?.app?.options?.projectId,t?.firestore?._settings?.projectId,t?.firestore?.databaseId?.projectId,t?._firestore?.projectId];for(let s of r)if(typeof s=="string"&&s.length>0)return s;return process.env.GCLOUD_PROJECT||process.env.GOOGLE_CLOUD_PROJECT||process.env.FIREBASE_PROJECT_ID||void 0}function Le(e){let t=e;return t?t.code===9?true:typeof t.message=="string"?t.message.includes("requires an index"):false:false}function he(e,t){let r=e??{},o=Le(e),s;if(o&&(s=r.message?He(r.message):void 0,!s)){let i=ze(t.ref);if(i){let d=je(t.path);s=Fe(i,d,t.isGroup,t.filters,t.sort);}}return {type:o?"index":"error",message:o?"This query requires a composite index that does not exist yet.":r.message??"Query failed",indexUrl:s}}function se(e,t,r=200){let o=fe(t);e.status(r).set("Content-Type","application/json; charset=utf-8").send(JSON.stringify(o));}function U(e,t,r,o=200){se(e,{success:true,data:t,meta:r},o);}function R(e,t,r=400){se(e,{success:false,error:t},r);}function re(e,t,r,o,s){let i=he(t,r),d=i.type==="index",y=d?424:500,g={success:false,error:d?i.message:s&&t instanceof Error?t.message:o};d&&(g.errorType="index",i.indexUrl&&(g.indexUrl=i.indexUrl)),se(e,g,y);}var oe="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";function qe(){let e="";for(;e.length<20;){let t=randomBytes(20);for(let r=0;r<t.length&&e.length<20;r++){let o=t[r];o>=oe.length*4||(e+=oe.charAt(o%oe.length));}}return e}function q(e){let t=e._def??e.def;if(!t)return e;let r=t.typeName??t.type;if(r==="ZodDate"||r==="date")return z.preprocess(o=>pe(o)??o,e);if(r==="ZodObject"||r==="object"){let o=e.shape,s={};for(let[i,d]of Object.entries(o))s[i]=q(d);return z.object(s)}if(r==="ZodArray"||r==="array"){let o=t.element??t.type;if(o)return z.array(q(o))}if(r==="ZodOptional"||r==="optional"){let o=t.innerType;if(o)return q(o).optional()}if(r==="ZodNullable"||r==="nullable"){let o=t.innerType;if(o)return q(o).nullable()}if(r==="ZodDefault"||r==="default"){let o=t.innerType,s=t.defaultValue;if(o){let i=q(o);return typeof s=="function"?i.default(s()):i.default(s)}}return e}function Ge(e,t,r=[]){let o=e.shape,s={},i=t&&t.length>0?t:Object.keys(o);for(let d of i){if(r.includes(d))continue;let y=d.split(".")[0];y&&o[y]&&(s[y]=o[y]);}return z.object(s)}function be(e,t,r,o=false,s=[]){try{let i=Ge(e,r,s),d=o?i.partial():i;return {success:!0,data:(le()==="normalize"?q(d):d).parse(t)}}catch(i){return i instanceof z.ZodError?{success:false,error:`Validation failed: ${i.issues.map(y=>`${y.path.join(".")}: ${y.message}`).join(", ")}`}:{success:false,error:"Validation failed"}}}function Me(e,t){let r=[],o=t?new Set(t):null,s={eq:"==",ne:"!=",lt:"<",lte:"<=",gt:">",gte:">=",in:"in",nin:"not-in",contains:"array-contains",containsAny:"array-contains-any"};for(let[i,d]of Object.entries(e)){if(d===void 0||["cursor","limit","pageSize","orderBy","orderDir","select"].includes(i))continue;let y=Array.isArray(d)?d[0]:d;if(y===void 0||y==="")continue;let h=i.match(/^(\w+)__(\w+)$/),g,A="==";if(h&&h[1]&&h[2]){g=h[1];let C=h[2];if(s[C])A=s[C];else continue}else if(!h)g=i;else continue;if(o&&!o.has(g))continue;let N=y;A==="in"||A==="not-in"||A==="array-contains-any"?N=y.split(",").map(C=>we(C.trim())):N=we(y),r.push({field:g,op:A,value:N});}return r}function we(e){if(e==="true")return true;if(e==="false")return false;if(e==="null")return null;let t=Number(e);return !isNaN(t)&&e!==""?t:e}function Q(e){return e?{docId:e.id}:null}async function Re(e,t){if(!t||typeof t!="object")return;let r=t.docId;if(typeof r=="string")try{let o=e.repo.ref;if(typeof o.doc!="function")return;let s=await o.doc(r).get();return s.exists?s:void 0}catch{return}}function xe(e,t,r,o=false){async function s(c,u,l,n){if(!o)return true;let f=u.rules?.[l];if(!f)return R(c,`Operation "${l}" is not allowed for this repository`,403),false;try{return await f(n)?!0:(R(c,"Forbidden",403),!1)}catch(p){let m=r&&p instanceof Error?p.message:"Forbidden";return R(c,m,403),false}}async function i(c,u,l,n){if(!o)return n;let f=c.rules?.filter;if(!f)return n;let p=[];for(let m of n)try{await f({user:u,doc:m,params:l})&&p.push(m);}catch{}return p}function d(c){return c?.user??null}function y(c,u){return !c||!e[c]?(R(u,`Repository "${c}" not found`,404),null):e[c]}function h(c,u){if(!u)return;let l=c[u];if(typeof l!="string"||!l)return;let n=l.split("/").filter(Boolean),f=[];for(let p=1;p<n.length;p+=2)f.push(n[p]);return f.length>0?f:void 0}async function g(c,u){let l=`by${c.documentKey.charAt(0).toUpperCase()}${c.documentKey.slice(1)}`,n=c.repo.get[l];if(typeof n=="function")try{let p=await n(u);if(p)return p}catch{}return (await c.repo.query.by({where:[[c.documentKey,"==",u]],limit:1}))[0]??null}async function A(c,u){let l=c.params||{},n=y(l.repoName,u);if(!n)return;let f=d(c);if(!await s(u,n,"list",{user:f,query:c.query??{},params:l}))return;let p=[],m;try{let a=c.query??{},w=Math.min(Number(a.pageSize)||n.pageSize,100),S=a.cursor,O=a.direction?.toLowerCase()==="prev"?"prev":"next",b=a.orderBy,v=a.orderDir?.toLowerCase()==="desc"?"desc":"asc",T=a.select,x=T?T.split(",").map(D=>D.trim()):void 0,k;n.allowedIncludes&&a.includes&&(k=(typeof a.includes=="string"?a.includes.split(",").map(K=>K.trim()):Array.isArray(a.includes)?a.includes:[]).filter(K=>typeof K=="string"&&n.allowedIncludes.includes(K)),k?.length===0&&(k=void 0));let P=Me(a,n.filterableFields);p=P.map(D=>({field:D.field,op:D.op,value:String(D.value??"")})),b&&(m={field:b,dir:v});let F={pageSize:w,direction:O};if(S)try{let D=typeof S=="string"?JSON.parse(S):S;F.cursor=await Re(n,D);}catch{}if(b){if(n.orderableFields&&!n.orderableFields.includes(b)){R(u,`Field not orderable: ${b}`,400);return}F.orderBy=[{field:b,direction:v}];}P.length>0&&(F.where=P.map(D=>[D.field,D.op,D.value])),x&&(F.select=x),k&&(F.include=k);let L=await n.repo.query.paginate(F),Oe={items:await i(n,d(c),l,L.data),hasNextPage:L.hasNextPage,hasPrevPage:L.hasPrevPage,nextCursor:Q(L.nextCursor),prevCursor:Q(L.prevCursor)};U(u,Oe,{pageSize:w,hasMore:L.hasNextPage});}catch(a){re(u,a,{ref:n.repo.ref,path:n.path,isGroup:!!n.isGroup,filters:p,sort:m},"Failed to fetch documents",r);}}async function N(c,u){let l=c.params||{},n=y(l.repoName,u);if(!n)return;let f=d(c);if(!await s(u,n,"list",{user:f,query:c.body??{},params:l}))return;let p=[],m;try{let a=c.body??{},w=Math.min(a.pageSize||n.pageSize,100),S=a.direction==="prev"?"prev":"next";a.where&&(p=a.where.map(x=>({field:String(x[0]),op:x[1],value:String(x[2]??"")}))),a.orderBy&&a.orderBy[0]&&(m={field:a.orderBy[0].field,dir:a.orderBy[0].direction==="desc"?"desc":"asc"});let O={pageSize:w,direction:S};if(a.cursor)try{let x=typeof a.cursor=="string"?JSON.parse(a.cursor):a.cursor;O.cursor=await Re(n,x);}catch{}if(n.allowedIncludes&&a.includes&&a.includes.length>0){let x=a.includes.filter(k=>typeof k=="string"?n.allowedIncludes.includes(k):typeof k=="object"&&k!==null&&"relation"in k&&typeof k.relation=="string"?n.allowedIncludes.includes(k.relation):!1);x.length>0&&(O.include=x);}if(a.where&&a.where.length>0){if(n.filterableFields){let x=new Set(n.filterableFields),k=a.where.filter(P=>!x.has(P[0]));if(k.length>0){R(u,`Fields not filterable: ${k.map(P=>P[0]).join(", ")}`,400);return}}O.where=a.where;}if(a.orWhere&&a.orWhere.length>0){if(n.filterableFields){let x=new Set(n.filterableFields),k=a.orWhere.filter(P=>!x.has(P[0]));if(k.length>0){R(u,`Fields not filterable: ${k.map(P=>P[0]).join(", ")}`,400);return}}O.orWhere=a.orWhere;}if(a.orWhereGroups&&a.orWhereGroups.length>0){if(n.filterableFields){let x=new Set(n.filterableFields);for(let k of a.orWhereGroups){let P=k.filter(F=>!x.has(F[0]));if(P.length>0){R(u,`Fields not filterable: ${P.map(F=>F[0]).join(", ")}`,400);return}}}O.orWhereGroups=a.orWhereGroups;}if(a.orderBy&&a.orderBy.length>0){if(n.orderableFields){let x=new Set(n.orderableFields),k=a.orderBy.filter(P=>!x.has(P.field));if(k.length>0){R(u,`Fields not orderable: ${k.map(P=>P.field).join(", ")}`,400);return}}O.orderBy=a.orderBy;}a.select&&a.select.length>0&&(O.select=a.select);let b=await n.repo.query.paginate(O),T={items:await i(n,d(c),l,b.data),hasNextPage:b.hasNextPage,hasPrevPage:b.hasPrevPage,nextCursor:Q(b.nextCursor),prevCursor:Q(b.prevCursor)};U(u,T,{pageSize:w,hasMore:b.hasNextPage});}catch(a){re(u,a,{ref:n.repo.ref,path:n.path,isGroup:!!n.isGroup,filters:p,sort:m},"Failed to query documents",r);}}async function C(c,u){let l=c.params||{},n=y(l.repoName,u);if(!n)return;let f=l.id;if(!f){R(u,"Document ID required",400);return}try{let p=await g(n,f);if(!p){R(u,"Document not found",404);return}let m=d(c);if(!await s(u,n,"get",{user:m,doc:p,params:l}))return;if(o&&n.rules?.filter)try{if(!await n.rules.filter({user:m,doc:p,params:l})){R(u,"Document not found",404);return}}catch{R(u,"Document not found",404);return}U(u,p);}catch(p){re(u,p,{ref:n.repo.ref,path:n.path,isGroup:!!n.isGroup,filters:[{field:n.documentKey,op:"==",value:f}]},"Failed to fetch document",r);}}async function $(c,u){let l=c.params||{},n=y(l.repoName,u);if(n)try{let f=c.body??{},p=d(c);if(!await s(u,n,"create",{user:p,body:f,params:l}))return;let m=be(n.schema,f,n.createFields,!1,n.systemKeys);if(!m.success){R(u,m.error,400);return}if(n.validate){let w=await n.validate(m.data,"create");if(w){R(u,w,400);return}}let a;if(n.isGroup&&n.parentKeys&&n.parentKeys.length>0){let w={...m.data};n.createdKey&&(w[n.createdKey]=new Date);let S=n.parentKeys.filter(v=>!w[v]);if(S.length>0){R(u,`Missing parent key(s) for subcollection create: ${S.join(", ")}`,400);return}let O=n.parentKeys.map(v=>w[v]),b=w[n.documentKey]||qe();a=await n.repo.set(...O,b,w);}else a=await n.repo.create(m.data);U(u,a,void 0,201);}catch(f){let p=r&&f instanceof Error?f.message:"Failed to create document";R(u,p,500);}}async function z(c,u,l){let n=c.params||{},f=y(n.repoName,u);if(!f)return;let p=n.id;if(!p){R(u,"Document ID required",400);return}try{let m=c.body??{},a=await g(f,p);if(!a){R(u,"Document not found",404);return}let w=d(c);if(!await s(u,f,"update",{user:w,doc:a,body:m,params:n}))return;let S=be(f.schema,m,f.mutableFields,l,f.systemKeys);if(!S.success){R(u,S.error,400);return}if(f.validate){let v=await f.validate(S.data,"update");if(v){R(u,v,400);return}}let O=h(a,f.pathKey)??[p],b=await f.repo.update(...O,S.data);U(u,b);}catch(m){let a=r&&m instanceof Error?m.message:"Failed to update document";R(u,a,500);}}async function H(c,u){let l=c.params||{},n=y(l.repoName,u);if(!n)return;if(!n.allowDelete){R(u,"Delete not allowed for this repository",403);return}let f=l.id;if(!f){R(u,"Document ID required",400);return}try{let p=await g(n,f);if(!p){R(u,"Document not found",404);return}let m=d(c);if(!await s(u,n,"delete",{user:m,doc:p,params:l}))return;let a=h(p,n.pathKey)??[f];await n.repo.delete(...a),U(u,{deleted:!0});}catch(p){let m=r&&p instanceof Error?p.message:"Failed to delete document";R(u,m,500);}}function E(c,u){u.status(204).set("Access-Control-Allow-Methods","GET, POST, PUT, PATCH, DELETE, OPTIONS").set("Access-Control-Allow-Headers","Content-Type, Authorization").set("Access-Control-Max-Age","86400").send("");}return {handleList:A,handleQuery:N,handleGet:C,handleCreate:$,handleUpdate:z,handleDelete:H,handleOptions:E}}function ie(e){try{return z.toJSONSchema(e,{target:"openapi-3.1",unrepresentable:"any",override:t=>{let r=t.zodSchema?._zod?.def;r&&(r.type==="date"?(t.jsonSchema.type="string",t.jsonSchema.format="date-time"):r.type==="bigint"&&(t.jsonSchema.type="string",t.jsonSchema.format="int64"));}})}catch(t){return typeof console<"u"&&console.warn&&console.warn("[generateOpenAPISpec] Failed to convert Zod schema to JSON Schema; falling back to {type:object}.",t),{type:"object"}}}function j(e){return {$ref:`#/components/schemas/${e}`}}function I(e){return {description:e,content:{"application/json":{schema:j("ErrorResponse")}}}}function M(e,t){return {description:e,content:{"application/json":{schema:{type:"object",properties:{success:{type:"boolean",enum:[true]},data:t},required:["success","data"]}}}}}function ke(e){return {description:"Paginated list of documents",content:{"application/json":{schema:{type:"object",properties:{success:{type:"boolean",enum:[true]},data:{type:"object",properties:{items:{type:"array",items:e},nextCursor:{oneOf:[{type:"object"},{type:"null"}]},prevCursor:{oneOf:[{type:"object"},{type:"null"}]},hasNextPage:{type:"boolean"},hasPrevPage:{type:"boolean"}},required:["items","hasNextPage","hasPrevPage"]},meta:{type:"object",properties:{pageSize:{type:"integer"},hasMore:{type:"boolean"},cursor:{oneOf:[{type:"string"},{type:"null"}]}}}},required:["success","data"]}}}}}function Ke(e){return [{name:"pageSize",in:"query",schema:{type:"integer",default:e.pageSize,maximum:100},description:"Number of items per page"},{name:"cursor",in:"query",schema:{type:"string"},description:"Base64 pagination cursor"},{name:"orderBy",in:"query",schema:{type:"string"},description:"Field name to order by"},{name:"orderDir",in:"query",schema:{type:"string",enum:["asc","desc"]},description:"Order direction"},{name:"select",in:"query",schema:{type:"string"},description:"Comma-separated list of fields to return"}]}function We(e){let t=e.filterableFields??Object.keys(e.schema.shape),r=["eq","ne","lt","lte","gt","gte","in","nin","contains"],o=[];for(let s of t){o.push({name:s,in:"query",schema:{type:"string"},description:`Filter by ${s} (equality)`});for(let i of r)o.push({name:`${s}__${i}`,in:"query",schema:{type:"string"},description:`Filter ${s} with operator ${i}`});}return o}function Je(){return {type:"object",properties:{where:{type:"array",items:{type:"array",items:{},minItems:3,maxItems:3},description:"AND conditions: [field, operator, value][]"},orWhere:{type:"array",items:{type:"array",items:{},minItems:3,maxItems:3},description:"Simple OR conditions (each independently OR'd)"},orWhereGroups:{type:"array",items:{type:"array",items:{type:"array",items:{},minItems:3,maxItems:3}},description:"Advanced OR groups (AND within, OR across groups)"},orderBy:{type:"array",items:{type:"object",properties:{field:{type:"string"},direction:{type:"string",enum:["asc","desc"]}},required:["field"]}},select:{type:"array",items:{type:"string"},description:"Fields to select (projection)"},pageSize:{type:"integer",maximum:100,description:"Number of items per page"},cursor:{oneOf:[{type:"string"},{type:"object"}],description:"Pagination cursor"},direction:{type:"string",enum:["next","prev"],description:"Pagination direction"},includes:{type:"array",items:{oneOf:[{type:"string"},{type:"object",properties:{relation:{type:"string"},select:{type:"array",items:{type:"string"}}},required:["relation"]}]},description:"Relations to include (populate)"}}}}function Ze(e,t,r,o,s){let i={},d=e.name,y=`${t}/${e.name}`,h=`${y}/{${e.documentKey}}`,g={name:e.documentKey,in:"path",required:true,schema:{type:"string"},description:"Unique document identifier"};i[y]={get:{operationId:`list${B(e.name)}`,summary:`List ${e.name} (paginated)`,tags:[d],parameters:[...Ke(e),...We(e)],responses:{200:ke(j(r)),500:I("Internal server error")}},post:{operationId:`create${B(e.name)}`,summary:`Create a ${_(e.name)}`,tags:[d],requestBody:{required:true,content:{"application/json":{schema:j(o??r)}}},responses:{201:M("Document created",j(r)),400:I("Validation error"),500:I("Internal server error")}}},i[`${y}/query`]={post:{operationId:`query${B(e.name)}`,summary:`Query ${e.name} with advanced filters`,tags:[d],requestBody:{required:true,content:{"application/json":{schema:j("QueryRequestBody")}}},responses:{200:ke(j(r)),400:I("Invalid query"),500:I("Internal server error")}}};let A={};return A.get={operationId:`get${B(_(e.name))}`,summary:`Get a single ${_(e.name)}`,tags:[d],parameters:[g],responses:{200:M("Document found",j(r)),404:I("Document not found"),500:I("Internal server error")}},A.put={operationId:`update${B(_(e.name))}`,summary:`Update a ${_(e.name)} (full replace)`,tags:[d],parameters:[g],requestBody:{required:true,content:{"application/json":{schema:j(s??r)}}},responses:{200:M("Document updated",j(r)),400:I("Validation error"),404:I("Document not found"),500:I("Internal server error")}},A.patch={operationId:`patch${B(_(e.name))}`,summary:`Partially update a ${_(e.name)}`,tags:[d],parameters:[g],requestBody:{required:true,content:{"application/json":{schema:{allOf:[j(s??r)],description:"All fields are optional for partial updates"}}}},responses:{200:M("Document patched",j(r)),400:I("Validation error"),404:I("Document not found"),500:I("Internal server error")}},e.allowDelete&&(A.delete={operationId:`delete${B(_(e.name))}`,summary:`Delete a ${_(e.name)}`,tags:[d],parameters:[g],responses:{200:M("Document deleted",{type:"object",properties:{id:{type:"string"}}}),404:I("Document not found"),500:I("Internal server error")}}),i[h]=A,i}function ce(e,t,r={}){let{title:o="CRUD API",version:s="1.0.0",description:i,servers:d,auth:y=false}=r,h=t==="/"?"":t.replace(/\/$/,""),g={},A={},N=[];g.ErrorResponse={type:"object",properties:{success:{type:"boolean",enum:[false]},error:{type:"string"}},required:["success","error"]},g.QueryRequestBody=Je();for(let[H,E]of Object.entries(e)){let c=B(_(H)),u=`${c}Create`,l=`${c}Update`;g[c]=ie(E.schema);let n=S=>{let O=S&&S.length>0?S:Object.keys(E.schema.shape),b={};for(let v of O){let T=v.split(".")[0];T&&E.schema.shape[T]&&!E.systemKeys.includes(T)&&(b[T]=E.schema.shape[T]);}return b},f=null,p=n(E.createFields);Object.keys(p).length>0&&(g[u]=ie(z.object(p)),f=u);let m=null,a=n(E.mutableFields);Object.keys(a).length>0&&(g[l]=ie(z.object(a)),m=l);let w=Ze(E,h,c,f,m);Object.assign(A,w),N.push({name:H,description:`Operations on ${H} (collection: ${E.path})`});}let C={},$;return y==="basic"?(C.basicAuth={type:"http",scheme:"basic"},$=[{basicAuth:[]}]):y==="bearer"&&(C.bearerAuth={type:"http",scheme:"bearer",bearerFormat:"JWT"},$=[{bearerAuth:[]}]),{openapi:"3.1.0",info:{title:o,version:s,...i?{description:i}:{}},...d&&d.length>0?{servers:d}:{},paths:A,components:{schemas:g,...Object.keys(C).length>0?{securitySchemes:C}:{}},...$?{security:$}:{},tags:N}}function B(e){return e.charAt(0).toUpperCase()+e.slice(1)}function _(e){return e.endsWith("ies")?e.slice(0,-3)+"y":e.endsWith("ses")||e.endsWith("xes")||e.endsWith("zes")?e.slice(0,-2):e.endsWith("s")&&!e.endsWith("ss")?e.slice(0,-1):e}var Ae="https://cdn.jsdelivr.net",Ce="https://www.gstatic.com",Qe=["default-src 'self'",`script-src 'self' 'unsafe-inline' 'unsafe-eval' ${Ae} ${Ce}`,`style-src 'self' 'unsafe-inline' ${Ae}`,`connect-src 'self' ${Ce} https://*.googleapis.com https://*.firebaseio.com https://identitytoolkit.googleapis.com`,"img-src 'self' data:","font-src 'self' data:","frame-ancestors 'none'","base-uri 'self'","form-action 'self'"].join("; ");function Se(e={}){let t=e.csp===void 0?Qe:e.csp,r=e.frameOptions===void 0?"DENY":e.frameOptions,o=e.referrerPolicy===void 0?"no-referrer":e.referrerPolicy,s=e.cacheControl===void 0?"private, no-store":e.cacheControl;return async(i,d,y)=>{typeof d?.setHeader=="function"&&(d.setHeader("X-Content-Type-Options","nosniff"),r&&d.setHeader("X-Frame-Options",r),o&&d.setHeader("Referrer-Policy",o),s&&d.setHeader("Cache-Control",s),t&&d.setHeader("Content-Security-Policy",t)),await y();}}function Ve(e,t){return `<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
@@ -9,5 +9,5 @@ import {z as z$1}from'zod';import {Timestamp}from'firebase-admin/firestore';func
|
|
|
9
9
|
<script id="api-reference" data-url="${t}"></script>
|
|
10
10
|
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
11
11
|
</body>
|
|
12
|
-
</html>`}async function
|
|
12
|
+
</html>`}async function Xe(e){return typeof e.rawBody=="string"?e.rawBody:Buffer.isBuffer(e.rawBody)?e.rawBody.toString("utf8"):""}function $t(e){let{basePath:t="/",repos:r,parseBody:o=true,securityHeaders:s,auth:i,middleware:d=[],verbose:y=false,httpsOptions:h}=e,g=t==="/"?"":t.replace(/\/$/,""),A={};for(let[l,n]of Object.entries(r)){let f=n.schema??n.repo.schema??null;if(!f)throw new Error(`[createCrudServer] Repository "${l}" has no Zod schema. Either use createRepositoryConfig(schema)(config) or pass schema: explicitly.`);let p,m,a,w;if(n.fieldsConfig){let b=n.fieldsConfig;p=[],m=[],a=[],w=[];for(let[v,T]of Object.entries(b))for(let x of T)x==="filterable"?p.push(v):x==="orderable"?m.push(v):x==="mutable"?a.push(v):x==="create"&&w.push(v);m.length===0&&(m=p),p.length===0&&(p=void 0),m&&m.length===0&&(m=void 0),a.length===0&&(a=void 0),w.length===0&&(w=void 0);}let S=(()=>{let b=n.repo._parentKeys;return b&&b.length>0?b:void 0})();if(S&&w)for(let b of S)w.includes(b)||w.push(b);let O={name:l,path:n.path,repo:n.repo,schema:f,systemKeys:n.repo._systemKeys??[n.documentKey??"docId"],documentKey:n.documentKey??"docId",pathKey:n.repo._pathKey??void 0,isGroup:!!n.repo._isGroup,parentKeys:S,createdKey:n.repo._createdKey??void 0,pageSize:n.pageSize??25,filterableFields:p,orderableFields:m,mutableFields:a,createFields:w,allowDelete:n.allowDelete??false,allowedIncludes:n.allowedIncludes,validate:n.validate,rules:n.rules};A[l]=O;}let C=xe(A,g,y,!!i),$=e.openapi,z=$&&typeof $=="object"?$:{},H=null;function E(){if(!H){let l=i?J(i)||typeof i=="function"?"bearer":"basic":false;H=ce(A,g,{...z,auth:z.auth??l});}return H}let c=new W;if(s!==false){let l=s===void 0?{csp:false}:s;c.use(Se(l));}if(c.use((l,n,f)=>{n.set("Access-Control-Allow-Origin","*"),n.set("Access-Control-Allow-Credentials","true"),f();}),o&&c.use(async(l,n,f)=>{let p=l;if(String(p.headers?.["content-type"]??"").includes("application/json")){if(typeof p.body=="string")try{l.body=JSON.parse(p.body);}catch{}else if(Buffer.isBuffer(l.rawBody))try{let a=await Xe(p);l.body=JSON.parse(a);}catch{}}await f();}),i)if(J(i)){for(let l of i.routes){let n=`${g}${l.path}`;l.method==="GET"?c.get(n,l.handler):c.post(n,l.handler);}c.use(i.middleware);}else if(typeof i=="function")c.use(i);else {let l=i.realm??"API",n="Basic "+Buffer.from(`${i.username}:${i.password}`).toString("base64");c.use((f,p,m)=>{if((f.headers?.authorization??"")!==n){p.status(401).set("WWW-Authenticate",`Basic realm="${l}"`).set("Content-Type","application/json").send(JSON.stringify({success:false,error:"Unauthorized"}));return}m();});}for(let l of d)c.use(l);if($!==false){let l=`${g}/__spec.json`,n=`${g}/__docs`;c.get(l,(f,p)=>{let m=E();p.status(200).set("Content-Type","application/json; charset=utf-8").send(JSON.stringify(m,null,2));}),c.get(n,(f,p)=>{let m=V(f,g)+"/__spec.json",a=Ve(z.title??"CRUD API",m);p.status(200).set("Content-Type","text/html; charset=utf-8").send(a);});}c.use((l,n,f)=>{if(l.method==="OPTIONS"){C.handleOptions(l,n);return}f();}),c.get(`${g}/:repoName`,C.handleList),c.post(`${g}/:repoName/query`,C.handleQuery),c.get(`${g}/:repoName/:id`,C.handleGet),c.post(`${g}/:repoName`,C.handleCreate),c.put(`${g}/:repoName/:id`,(l,n)=>C.handleUpdate(l,n,false)),c.patch(`${g}/:repoName/:id`,(l,n)=>C.handleUpdate(l,n,true)),c.delete(`${g}/:repoName/:id`,C.handleDelete);let u=async(l,n)=>{await c.handle(l,n);};return u.spec=E,h&&(u.httpsOptions=h),u}export{$t as createCrudServer,ce as generateOpenAPISpec};//# sourceMappingURL=index.js.map
|
|
13
13
|
//# sourceMappingURL=index.js.map
|