@lpdjs/firestore-repo-service 2.2.0 → 2.2.2
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 +111 -529
- package/dist/{index-BJQXYHTC.d.ts → index-BnXFmcVp.d.ts} +4 -3
- package/dist/{index-BjH87AI4.d.cts → index-KbSSRIWJ.d.cts} +4 -3
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/servers/admin/index.cjs +2 -2
- package/dist/servers/admin/index.cjs.map +1 -1
- package/dist/servers/admin/index.d.cts +3 -2
- package/dist/servers/admin/index.d.ts +3 -2
- package/dist/servers/admin/index.js +2 -2
- package/dist/servers/admin/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 +4 -3
- package/dist/servers/crud/index.d.ts +4 -3
- package/dist/servers/crud/index.js +2 -2
- package/dist/servers/crud/index.js.map +1 -1
- package/dist/servers/index.cjs +6 -6
- package/dist/servers/index.cjs.map +1 -1
- package/dist/servers/index.d.cts +4 -3
- package/dist/servers/index.d.ts +4 -3
- package/dist/servers/index.js +6 -6
- package/dist/servers/index.js.map +1 -1
- package/dist/sync/bigquery.d.cts +3 -1
- package/dist/sync/bigquery.d.ts +3 -1
- package/dist/sync/index.cjs +5 -5
- package/dist/sync/index.d.cts +7 -12
- package/dist/sync/index.d.ts +7 -12
- package/dist/sync/index.js +5 -5
- package/dist/{types-CYVwoOQx.d.cts → types-B5NdBY1Z.d.cts} +2 -1
- package/dist/{types-CYVwoOQx.d.ts → types-B5NdBY1Z.d.ts} +2 -1
- package/dist/{types-Vvdx263s.d.cts → types-BbCdscqh.d.cts} +17 -9
- package/dist/{types-Vvdx263s.d.ts → types-BbCdscqh.d.ts} +17 -9
- package/package.json +9 -9
package/dist/sync/bigquery.d.cts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { c as SqlDialect, S as SqlAdapter, e as SqlTableDef, d as SqlColumn } from '../types-
|
|
1
|
+
import { c as SqlDialect, S as SqlAdapter, e as SqlTableDef, d as SqlColumn } from '../types-BbCdscqh.cjs';
|
|
2
|
+
import 'firebase-functions/v2/firestore';
|
|
3
|
+
import 'firebase-functions/v2/https';
|
|
2
4
|
import 'firebase-functions/v2/pubsub';
|
|
3
5
|
|
|
4
6
|
/** Shared BigQuery dialect singleton. */
|
package/dist/sync/bigquery.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { c as SqlDialect, S as SqlAdapter, e as SqlTableDef, d as SqlColumn } from '../types-
|
|
1
|
+
import { c as SqlDialect, S as SqlAdapter, e as SqlTableDef, d as SqlColumn } from '../types-BbCdscqh.js';
|
|
2
|
+
import 'firebase-functions/v2/firestore';
|
|
3
|
+
import 'firebase-functions/v2/https';
|
|
2
4
|
import 'firebase-functions/v2/pubsub';
|
|
3
5
|
|
|
4
6
|
/** Shared BigQuery dialect singleton. */
|
package/dist/sync/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';function re(o){let e=[],t=o.replace(/[.*+?^${}()|[\]\\]/g,a=>a===":"?a:`\\${a}`).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(a,s)=>(e.push(s),"([^/]+)"));return {pattern:new RegExp(`^${t}$`),paramNames:e}}function se(o){let e=o.path??o.url??"/",t=e.indexOf("?");return t===-1?e:e.slice(0,t)}var L=class{constructor(){this.routes=[];this.middlewares=[];this.notFoundHandler=(e,t)=>{t.status(404).send("Not Found");};this.errorHandler=(e,t,a)=>{console.error("[MiniRouter]",e),a.status(500).send("Internal Server Error");};}use(e){return this.middlewares.push(e),this}get(e,t){return this.addRoute("GET",e,t)}post(e,t){return this.addRoute("POST",e,t)}put(e,t){return this.addRoute("PUT",e,t)}patch(e,t){return this.addRoute("PATCH",e,t)}delete(e,t){return this.addRoute("DELETE",e,t)}onNotFound(e){return this.notFoundHandler=e,this}onError(e){return this.errorHandler=e,this}addRoute(e,t,a){let{pattern:s,paramNames:u}=re(t);return this.routes.push({method:e.toUpperCase(),pattern:s,paramNames:u,handler:a}),this}async handle(e,t){let a=(e.method??"GET").toUpperCase(),s=se(e),u=null,g={};for(let R of this.routes){if(R.method!==a)continue;let b=s.match(R.pattern);if(b){u=R,g={},R.paramNames.forEach((f,d)=>{g[f]=decodeURIComponent(b[d+1]??"");});break}}let w=Object.assign(e,{params:g}),c=u?u.handler:this.notFoundHandler;try{await this.runMiddlewareChain(w,t,c);}catch(R){this.errorHandler(R,e,t);}}async runMiddlewareChain(e,t,a){let s=0,u=async()=>{if(s<this.middlewares.length){let g=this.middlewares[s++];await g(e,t,u);}else await a(e,t);};await u();}};var ae={string:"ZodString",number:"ZodNumber",bigint:"ZodBigInt",boolean:"ZodBoolean",date:"ZodDate",enum:"ZodEnum",nativeEnum:"ZodNativeEnum",literal:"ZodLiteral",object:"ZodObject",array:"ZodArray",optional:"ZodOptional",nullable:"ZodNullable",default:"ZodDefault",coerce:"ZodCoerce",union:"ZodUnion",undefined:"ZodUndefined",unknown:"ZodUnknown",any:"ZodAny",record:"ZodRecord"};function F(o){let e=o,t=e._zod?.def?.type;if(t)return ae[t]??`Zod${t.charAt(0).toUpperCase()}${t.slice(1)}`;let a=e._def?.typeName;return a||""}function W(o){let e=o;if(e._zod?.def?.innerType)return e._zod.def.innerType;if(e._def?.innerType)return e._def.innerType}function K(o){let e=o;return e.shape&&typeof e.shape=="object"?e.shape:e._zod?.def?.shape&&typeof e._zod.def.shape=="object"?e._zod.def.shape:e._def?.shape?typeof e._def.shape=="function"?e._def.shape():e._def.shape:{}}var
|
|
1
|
+
'use strict';function re(o){let e=[],t=o.replace(/[.*+?^${}()|[\]\\]/g,a=>a===":"?a:`\\${a}`).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(a,s)=>(e.push(s),"([^/]+)"));return {pattern:new RegExp(`^${t}$`),paramNames:e}}function se(o){let e=o.path??o.url??"/",t=e.indexOf("?");return t===-1?e:e.slice(0,t)}var L=class{constructor(){this.routes=[];this.middlewares=[];this.notFoundHandler=(e,t)=>{t.status(404).send("Not Found");};this.errorHandler=(e,t,a)=>{console.error("[MiniRouter]",e),a.status(500).send("Internal Server Error");};}use(e){return this.middlewares.push(e),this}get(e,t){return this.addRoute("GET",e,t)}post(e,t){return this.addRoute("POST",e,t)}put(e,t){return this.addRoute("PUT",e,t)}patch(e,t){return this.addRoute("PATCH",e,t)}delete(e,t){return this.addRoute("DELETE",e,t)}onNotFound(e){return this.notFoundHandler=e,this}onError(e){return this.errorHandler=e,this}addRoute(e,t,a){let{pattern:s,paramNames:u}=re(t);return this.routes.push({method:e.toUpperCase(),pattern:s,paramNames:u,handler:a}),this}async handle(e,t){let a=(e.method??"GET").toUpperCase(),s=se(e),u=null,g={};for(let R of this.routes){if(R.method!==a)continue;let b=s.match(R.pattern);if(b){u=R,g={},R.paramNames.forEach((f,d)=>{g[f]=decodeURIComponent(b[d+1]??"");});break}}let w=Object.assign(e,{params:g}),c=u?u.handler:this.notFoundHandler;try{await this.runMiddlewareChain(w,t,c);}catch(R){this.errorHandler(R,e,t);}}async runMiddlewareChain(e,t,a){let s=0,u=async()=>{if(s<this.middlewares.length){let g=this.middlewares[s++];await g(e,t,u);}else await a(e,t);};await u();}};var ae={string:"ZodString",number:"ZodNumber",bigint:"ZodBigInt",boolean:"ZodBoolean",date:"ZodDate",enum:"ZodEnum",nativeEnum:"ZodNativeEnum",literal:"ZodLiteral",object:"ZodObject",array:"ZodArray",optional:"ZodOptional",nullable:"ZodNullable",default:"ZodDefault",coerce:"ZodCoerce",union:"ZodUnion",undefined:"ZodUndefined",unknown:"ZodUnknown",any:"ZodAny",record:"ZodRecord"};function F(o){let e=o,t=e._zod?.def?.type;if(t)return ae[t]??`Zod${t.charAt(0).toUpperCase()}${t.slice(1)}`;let a=e._def?.typeName;return a||""}function W(o){let e=o;if(e._zod?.def?.innerType)return e._zod.def.innerType;if(e._def?.innerType)return e._def.innerType}function K(o){let e=o;return e.shape&&typeof e.shape=="object"?e.shape:e._zod?.def?.shape&&typeof e._zod.def.shape=="object"?e._zod.def.shape:e._def?.shape?typeof e._def.shape=="function"?e._def.shape():e._def.shape:{}}var _="__sync_version";var ie=new Set(["ZodOptional","ZodNullable","ZodDefault"]);function V(o){let e=o,t=false;for(;;){let a=F(e);if(!ie.has(a))break;(a==="ZodOptional"||a==="ZodNullable")&&(t=true);let s=W(e);if(!s)break;e=s;}return {inner:e,nullable:t}}var J={ZodString:"string",ZodNumber:"number",ZodBigInt:"bigint",ZodBoolean:"boolean",ZodDate:"timestamp",ZodEnum:"string",ZodNativeEnum:"string",ZodLiteral:"string"};function ce(o){let{inner:e}=V(o);return J[F(e)]??"json"}function Y(o,e,t,a,s,u,g,w){for(let[c,R]of Object.entries(o)){let b=t?`${t}__${c}`:c;if(s.has(c)||s.has(b))continue;let{inner:f,nullable:d}=V(R),l=F(f),i=a||d;if(l==="ZodObject"){let r=K(f);Y(r,e,b,i,s,u,g,w);continue}let n=J[l]??"json",p=b===g||c===g,S=u[b]??u[c]??b;w.push({name:S,sqlType:e.mapType(n),nullable:p?false:i,isPrimaryKey:p});}}function O(o,e,t={}){let{primaryKey:a,exclude:s=[],columnMap:u={}}=t,g=new Set(s),w=K(o),c=[];return Y(w,e,"",false,g,u,a,c),c.some(R=>R.name===_)||c.push({name:_,sqlType:e.mapType("bigint"),nullable:true,isPrimaryKey:false,description:"Monotonic publish version (Date.now() ms). Internal."}),c}function Q(o){if(o==null)return null;if(typeof o=="object"&&typeof o.toDate=="function")return o.toDate().toISOString();if(o instanceof Date)return o.toISOString();if(Buffer.isBuffer(o))return o.toString("base64");if(o instanceof Uint8Array)return Buffer.from(o).toString("base64");if(typeof o=="object"&&"latitude"in o&&"longitude"in o){let e=o;return JSON.stringify({lat:e.latitude,lng:e.longitude})}return Array.isArray(o)?JSON.stringify(o.map(Q)):o}function X(o,e,t){for(let[a,s]of Object.entries(o)){let u=e?`${e}__${a}`:a;s!=null&&typeof s=="object"&&!Array.isArray(s)&&!(s instanceof Date)&&!Buffer.isBuffer(s)&&!(s instanceof Uint8Array)&&typeof s.toDate!="function"&&!("latitude"in s&&"longitude"in s)?X(s,u,t):t[u]=Q(s);}}function Z(o,e){let t=new Set(e?.exclude),a=e?.columnMap??{},s={};X(o,"",s);let u={};for(let[g,w]of Object.entries(s)){if(t.has(g))continue;let c=g.split("__")[0];if(c!==g&&t.has(c))continue;let R=a[g]??(g.includes("__")?a[g.split("__").pop()]:void 0)??g;u[R]=w;}return u}function q(o,e){if(process.env.FUNCTIONS_EMULATOR==="true"){let s=process.env.GCLOUD_PROJECT??process.env.GOOGLE_CLOUD_PROJECT??"demo-project",u=process.env.FUNCTION_REGION??"us-central1",g=(process.env.FUNCTION_TARGET??"").replace(/\./g,"-");return `/${s}/${u}/${g}${e}`}let t=process.env.K_SERVICE,a=o.hostname??o.headers?.host??"";return t&&a.includes("cloudfunctions.net")?`/${t.toLowerCase()}${e}`:e}function D(o,e,t){return `<!DOCTYPE html>
|
|
2
2
|
<html lang="en"><head>
|
|
3
3
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
4
4
|
<title>${o} \u2014 Sync Admin</title>
|
|
@@ -42,7 +42,7 @@ ${t}
|
|
|
42
42
|
</table>
|
|
43
43
|
${p}
|
|
44
44
|
${S}
|
|
45
|
-
</div>`);E(l,r);}),f.get(`${c}`,(d,l)=>{let i=q(d,c);l.status(302).set("Location",`${i}/`).send("");}),R.healthCheck&&f.get(`${c}/:repoName/health`,async(d,l)=>{let i=q(d,c),n=b.find(y=>y.name===d.params.repoName);if(!n){E(l,D("Not Found",i,`<p>Unknown repo: ${d.params.repoName}</p>`),404);return}if(!n.schema){E(l,D("Health Check",i,`<p class="badge badge-warn">No Zod schema attached to "${n.name}"</p>`));return}let p=
|
|
45
|
+
</div>`);E(l,r);}),f.get(`${c}`,(d,l)=>{let i=q(d,c);l.status(302).set("Location",`${i}/`).send("");}),R.healthCheck&&f.get(`${c}/:repoName/health`,async(d,l)=>{let i=q(d,c),n=b.find(y=>y.name===d.params.repoName);if(!n){E(l,D("Not Found",i,`<p>Unknown repo: ${d.params.repoName}</p>`),404);return}if(!n.schema){E(l,D("Health Check",i,`<p class="badge badge-warn">No Zod schema attached to "${n.name}"</p>`));return}let p=O(n.schema,e.dialect,{primaryKey:n.documentKey,exclude:n.repoCfg?.exclude,columnMap:n.repoCfg?.columnMap}),S=[],r=false,h=null;try{r=await e.tableExists(n.tableName),r&&(S=await e.getTableColumns(n.tableName));}catch(y){h=y?.message??String(y);}let C=new Set(S),k=new Set(p.map(y=>y.name)),x=p.filter(y=>!C.has(y.name)),P=S.filter(y=>!k.has(y)),A=p.filter(y=>C.has(y.name)),m=r&&x.length===0&&!h;if(I(d)){z(l,{repo:n.name,table:n.tableName,tableExists:r,healthy:m,error:h,columns:{expected:p.map(y=>({name:y.name,type:y.sqlType,nullable:y.nullable,isPrimaryKey:y.isPrimaryKey})),actual:S,matched:A.map(y=>y.name),missing:x.map(y=>({name:y.name,type:y.sqlType})),extra:P}});return}let $=m?'<span class="badge badge-ok">Healthy</span>':'<span class="badge badge-err">Unhealthy</span>',T=p.map(y=>{let j=C.has(y.name)?'<span class="badge badge-ok">OK</span>':'<span class="badge badge-err">MISSING</span>';return `<tr><td>${y.name}</td><td>${y.sqlType}</td><td>${y.nullable?"Yes":"No"}</td><td>${y.isPrimaryKey?"\u2713":""}</td><td>${j}</td></tr>`}).join(`
|
|
46
46
|
`),v=P.map(y=>`<tr><td>${y}</td><td colspan="3" class="muted">not in schema</td><td><span class="badge badge-warn">EXTRA</span></td></tr>`).join(`
|
|
47
47
|
`),N=D(`Health: ${n.name}`,i,`<div class="card">
|
|
48
48
|
<p>Table: <code>${n.tableName}</code> ${r?$:'<span class="badge badge-err">NOT FOUND</span>'}</p>
|
|
@@ -88,11 +88,11 @@ ${t}
|
|
|
88
88
|
${k("BigQuery",C.bigquery)}
|
|
89
89
|
${k("Pub/Sub",C.pubsub)}
|
|
90
90
|
${k("Firestore",C.firestore)}
|
|
91
|
-
</div>`);E(l,A);}),async(d,l)=>{await f.handle(d,l);}}var de="firestore-sync";function ue(o,e){let t=e.ref?.path??void 0;return t?`${t}/{docId}`:(console.warn(`[SyncTriggers] Cannot determine collection path for "${o}". Skipping.`),null)}function H(o,e){let{onDocumentCreated:t,onDocumentUpdated:a,onDocumentDeleted:s}=e.deps.firestoreTriggers,u=e.deps.pubsub,g=e?.topicPrefix??de,w={},c=new Map;function R(f){let d=c.get(f);return d||(d=u.topic(f),c.set(f,d),d)}async function b(f,d){await R(f).publishMessage({json:d});}for(let[f,d]of Object.entries(o)){let l=e?.repos?.[f],i;if(d._isGroup){if(!l?.triggerPath){console.warn(`[SyncTriggers] Skipping collection-group repo "${f}". Provide a triggerPath in the sync repos config for group collections.`);continue}i=l.triggerPath;}else i=l?.triggerPath??ue(f,d);if(!i)continue;let n=d._systemKeys?.[0]??"docId",p=`${g}-${f}`;w[`${f}_onCreate`]=t(i,async S=>{let r=S.data;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"INSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onUpdate`]=a(i,async S=>{let r=S.data?.after;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"UPSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onDelete`]=s(i,async S=>{let r=S.data;if(!r)return;let h=r.data(),C=String(h?.[n]??r.id),k={operation:"DELETE",repoName:f,docId:C,data:null,timestamp:new Date().toISOString(),version:Date.now()};await b(p,k);});}return w}var M=class{constructor(e){this.buffer=[];this.flushing=false;this.timer=null;this.adapter=e.adapter,this.tableName=e.tableName,this.primaryKey=e.primaryKey,this.batchSize=e.batchSize??100,this.onFlushError=e.onFlushError;let t=e.flushIntervalMs??5e3;t>0&&(this.timer=setInterval(()=>{this.flush();},t),typeof this.timer=="object"&&"unref"in this.timer&&this.timer.unref());}get size(){return this.buffer.length}enqueue(...e){this.buffer.push(...e),this.buffer.length>=this.batchSize&&this.flush();}async flush(){if(this.flushing||this.buffer.length===0)return;this.flushing=true;let e=this.buffer.splice(0,this.batchSize);try{let t=new Map,a=[];for(let u of e)if(u.operation==="DELETE")a.push(u.docId),t.delete(u.docId);else if(u.data){let g=t.get(u.docId);if(!g)t.set(u.docId,u.data);else {let w=Number(g[
|
|
91
|
+
</div>`);E(l,A);}),async(d,l)=>{await f.handle(d,l);}}var de="firestore-sync";function ue(o,e){let t=e.ref?.path??void 0;return t?`${t}/{docId}`:(console.warn(`[SyncTriggers] Cannot determine collection path for "${o}". Skipping.`),null)}function H(o,e){let{onDocumentCreated:t,onDocumentUpdated:a,onDocumentDeleted:s}=e.deps.firestoreTriggers,u=e.deps.pubsub,g=e?.topicPrefix??de,w={},c=new Map;function R(f){let d=c.get(f);return d||(d=u.topic(f),c.set(f,d),d)}async function b(f,d){await R(f).publishMessage({json:d});}for(let[f,d]of Object.entries(o)){let l=e?.repos?.[f],i;if(d._isGroup){if(!l?.triggerPath){console.warn(`[SyncTriggers] Skipping collection-group repo "${f}". Provide a triggerPath in the sync repos config for group collections.`);continue}i=l.triggerPath;}else i=l?.triggerPath??ue(f,d);if(!i)continue;let n=d._systemKeys?.[0]??"docId",p=`${g}-${f}`;w[`${f}_onCreate`]=t(i,async S=>{let r=S.data;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"INSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onUpdate`]=a(i,async S=>{let r=S.data?.after;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"UPSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onDelete`]=s(i,async S=>{let r=S.data;if(!r)return;let h=r.data(),C=String(h?.[n]??r.id),k={operation:"DELETE",repoName:f,docId:C,data:null,timestamp:new Date().toISOString(),version:Date.now()};await b(p,k);});}return w}var M=class{constructor(e){this.buffer=[];this.flushing=false;this.timer=null;this.adapter=e.adapter,this.tableName=e.tableName,this.primaryKey=e.primaryKey,this.batchSize=e.batchSize??100,this.onFlushError=e.onFlushError;let t=e.flushIntervalMs??5e3;t>0&&(this.timer=setInterval(()=>{this.flush();},t),typeof this.timer=="object"&&"unref"in this.timer&&this.timer.unref());}get size(){return this.buffer.length}enqueue(...e){this.buffer.push(...e),this.buffer.length>=this.batchSize&&this.flush();}async flush(){if(this.flushing||this.buffer.length===0)return;this.flushing=true;let e=this.buffer.splice(0,this.batchSize);try{let t=new Map,a=[];for(let u of e)if(u.operation==="DELETE")a.push(u.docId),t.delete(u.docId);else if(u.data){let g=t.get(u.docId);if(!g)t.set(u.docId,u.data);else {let w=Number(g[_]??0);Number(u.data[_]??0)>=w&&t.set(u.docId,u.data);}}let s=Array.from(t.values());s.length>0&&await this.adapter.upsertRows(this.tableName,s,this.primaryKey),a.length>0&&await this.adapter.deleteRows(this.tableName,this.primaryKey,a);}catch(t){this.onFlushError?await this.onFlushError(e,t).catch(a=>{console.error(`[SyncQueue] Flush error for ${this.tableName}:`,t),console.error("[SyncQueue] Error handler also failed:",a);}):(this.buffer.unshift(...e),console.error(`[SyncQueue] Flush failed for ${this.tableName}:`,t));}finally{this.flushing=false;}}async shutdown(){this.timer&&(clearInterval(this.timer),this.timer=null),await this.flush();}};var ee=new Set;async function le(o,e,t,a,s,u,g){if(ee.has(o))return;let w=O(t,e.dialect,{primaryKey:s,exclude:u,columnMap:g});if(!await e.tableExists(a))await e.createTable({tableName:a,columns:w});else {let R=new Set(await e.getTableColumns(a)),b=w.filter(f=>!R.has(f.name));b.length>0&&await e.addColumns(a,b);}ee.add(o);}function G(o,e){let{deps:t,adapter:a,batchSize:s=100,flushIntervalMs:u=5e3,autoMigrate:g=false,topicPrefix:w="firestore-sync",workerOptions:c,repos:R={}}=e,b=new Map;function f(i,n){let p=b.get(i);if(p)return p;let r=R[i]?.tableName??i,h=async(C,k)=>{console.error(`[SyncWorker] Flush failed for "${i}" (${C.length} events):`,k);try{let x=`${w}-${i}-dlq`,P=t.pubsub.topic(x),[A]=await P.exists();A||(await P.create(),console.info(`[SyncWorker] Created DLQ topic "${x}"`));for(let m of C)await P.publishMessage({json:m});}catch(x){console.error(`[SyncWorker] Dead-letter publish also failed for ${i}:`,x);}};return p=new M({adapter:a,tableName:r,primaryKey:n,batchSize:s,flushIntervalMs:u,onFlushError:h}),b.set(i,p),p}async function d(i){let{repoName:n}=i,p=o[n];if(!p){console.warn(`[SyncWorker] Unknown repo "${n}", skipping event`);return}let S=p._systemKeys?.[0]??p.documentKey??"docId",r=R[n],h=r?.columnMap,C=h?.[S]??S;if(g){let x=p.schema??void 0;if(x){let P=r?.tableName??n;await le(n,a,x,P,S,r?.exclude,h);}}let k=f(n,C);i.data&&(i.data[_]=i.version??Date.now()),k.enqueue(i);}function l(i){let n=async p=>{let S=p.data?.message?.json??p.data?.json;if(!S){console.warn("[SyncWorker] Received empty PubSub message");return}await d(S);let r=b.get(S.repoName);r&&await r.flush();};return c?t.pubsubHandler.onMessagePublished({topic:i,...c},n):t.pubsubHandler.onMessagePublished(i,n)}return {handleMessage:d,createHandler:l,queues:b,async shutdown(){let i=[];for(let n of b.values())i.push(n.shutdown());await Promise.all(i);}}}var pe="firestore-sync";function te(o){if(typeof o!="function")return o;let e=o,t;return new Proxy({},{get(a,s){return t||(t=e()),t[s]},has(a,s){return t||(t=e()),s in t}})}function fe(o,e){let{deps:t,adapter:a,topicPrefix:s=pe,batchSize:u,flushIntervalMs:g,autoMigrate:w,admin:c,workerOptions:R,repos:b}=e,f=te(t.pubsub),d=te(a),l=H(o,{deps:{firestoreTriggers:t.firestoreTriggers,pubsub:f},topicPrefix:s,repos:b}),i=G(o,{deps:{pubsubHandler:t.pubsubHandler,pubsub:f},adapter:d,batchSize:u,flushIntervalMs:g,autoMigrate:w,topicPrefix:s,workerOptions:R,repos:b}),n={};for(let r of Object.keys(o))n[`sync_${r}`]=i.createHandler(`${s}-${r}`);let p=null;c&&(p=B(o,d,i.queues,i.handleMessage,c,b??{},f,s),n.adminsync=c.onRequest?c.httpsOptions?c.onRequest(c.httpsOptions,p):c.onRequest(p):p);let S={functions:{...l,...n},adminHandler:p,handleMessage:i.handleMessage,queues:i.queues,shutdown:i.shutdown};for(let r of ["adminHandler","handleMessage","queues","shutdown"])Object.defineProperty(S,r,{enumerable:false});return S}function ne(o,e){let t=e.columns.map(a=>{let s=a.isPrimaryKey?" NOT NULL":"";return ` ${o.quoteIdentifier(a.name)} ${a.sqlType}${s}`}).join(`,
|
|
92
92
|
`);return `CREATE TABLE IF NOT EXISTS ${o.quoteIdentifier(e.tableName)} (
|
|
93
93
|
${t}
|
|
94
94
|
);`}function me(o,e,t){return t.map(a=>`ALTER TABLE ${o.quoteIdentifier(e)} ADD COLUMN ${o.quoteIdentifier(a.name)} ${a.sqlType};`).join(`
|
|
95
|
-
`)}function ge(o,e,t){let a=[];for(let[s,u]of Object.entries(o)){let g=u.schema??u._schema??void 0;if(!g)continue;let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=
|
|
95
|
+
`)}function ge(o,e,t){let a=[];for(let[s,u]of Object.entries(o)){let g=u.schema??u._schema??void 0;if(!g)continue;let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=O(g,e,{primaryKey:R,exclude:w?.exclude,columnMap:w?.columnMap}),f={tableName:c,columns:b};a.push(ne(e,f));}return a.join(`
|
|
96
96
|
|
|
97
|
-
`)}async function ye(o,e,t){let a={created:[],altered:[],upToDate:[],skipped:[]};for(let[s,u]of Object.entries(o)){let g=u.schema??void 0;if(!g){a.skipped.push(s);continue}let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=
|
|
97
|
+
`)}async function ye(o,e,t){let a={created:[],altered:[],upToDate:[],skipped:[]};for(let[s,u]of Object.entries(o)){let g=u.schema??void 0;if(!g){a.skipped.push(s);continue}let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=O(g,e.dialect,{primaryKey:R,exclude:w?.exclude,columnMap:w?.columnMap}),f={tableName:c,columns:b};if(!await e.tableExists(c))await e.createTable(f),a.created.push(c);else {let l=new Set(await e.getTableColumns(c)),i=b.filter(n=>!l.has(n.name));i.length>0?(await e.addColumns(c,i),a.altered.push(c)):a.upToDate.push(c);}}return a}exports.SyncQueue=M;exports.addColumnsDDL=me;exports.autoMigrate=ye;exports.createFirestoreSync=fe;exports.createSyncTriggers=H;exports.createSyncWorker=G;exports.createTableDDL=ne;exports.createadminsyncServer=B;exports.generateDDL=ge;exports.serializeDocument=Z;exports.serializeValue=Q;exports.zodSchemaToColumns=O;exports.zodTypeToLogical=ce;//# sourceMappingURL=index.cjs.map
|
|
98
98
|
//# sourceMappingURL=index.cjs.map
|
package/dist/sync/index.d.cts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { S as SqlAdapter, a as SyncEvent, b as adminsyncConfig, R as RepoSyncConfig, P as PubSubClientDep, F as FirestoreSyncConfig, c as SqlDialect, d as SqlColumn, e as SqlTableDef, G as GenerateDDLConfig, L as LogicalType, f as SyncTriggersConfig, g as SyncWorkerConfig } from '../types-
|
|
2
|
-
export { h as FirestoreTriggersDep, O as OrFactory, i as PubSubHandlerDep, j as SyncDeps, k as SyncOperation, l as SyncWorkerOptions, m as adminsyncBasicAuth, n as adminsyncFeaturesFlag } from '../types-
|
|
1
|
+
import { S as SqlAdapter, a as SyncEvent, b as adminsyncConfig, R as RepoSyncConfig, P as PubSubClientDep, F as FirestoreSyncConfig, c as SqlDialect, d as SqlColumn, e as SqlTableDef, G as GenerateDDLConfig, L as LogicalType, f as SyncTriggersConfig, g as SyncWorkerConfig } from '../types-BbCdscqh.cjs';
|
|
2
|
+
export { A as AdminHttpsOptions, h as FirestoreTriggersDep, O as OrFactory, i as PubSubHandlerDep, j as SyncDeps, k as SyncOperation, l as SyncWorkerOptions, m as adminsyncBasicAuth, n as adminsyncFeaturesFlag } from '../types-BbCdscqh.cjs';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
import * as firebase_functions_core from 'firebase-functions/core';
|
|
5
|
+
import * as firebase_functions_pubsub from 'firebase-functions/pubsub';
|
|
6
|
+
import 'firebase-functions/v2/firestore';
|
|
7
|
+
import 'firebase-functions/v2/https';
|
|
4
8
|
import 'firebase-functions/v2/pubsub';
|
|
5
9
|
|
|
6
10
|
/**
|
|
@@ -283,15 +287,6 @@ declare function serializeDocument(doc: Record<string, unknown>, options?: Pick<
|
|
|
283
287
|
*/
|
|
284
288
|
declare function createSyncTriggers<M extends Record<string, any>>(repoMapping: M, config: SyncTriggersConfig<NoInfer<M>>): Record<string, any>;
|
|
285
289
|
|
|
286
|
-
/**
|
|
287
|
-
* PubSub worker — creates a Cloud Function that receives {@link SyncEvent}
|
|
288
|
-
* messages from PubSub, routes them to per-repo {@link SyncQueue}s, and
|
|
289
|
-
* flushes batches to the configured {@link SqlAdapter}.
|
|
290
|
-
*
|
|
291
|
-
* Dependencies (`firebase-functions`, `@google-cloud/pubsub`) are injected
|
|
292
|
-
* via the `deps` config property.
|
|
293
|
-
*/
|
|
294
|
-
|
|
295
290
|
/**
|
|
296
291
|
* Create a PubSub-triggered Cloud Function that syncs Firestore changes
|
|
297
292
|
* to a SQL database.
|
|
@@ -306,7 +301,7 @@ declare function createSyncWorker<M extends Record<string, any>>(repoMapping: M,
|
|
|
306
301
|
/** Process a SyncEvent directly (for testing or custom PubSub integration). */
|
|
307
302
|
handleMessage: (syncEvent: SyncEvent) => Promise<void>;
|
|
308
303
|
/** Create a Cloud Function handler for a specific PubSub topic. */
|
|
309
|
-
createHandler: (topicName: string) => any
|
|
304
|
+
createHandler: (topicName: string) => firebase_functions_core.CloudFunction<firebase_functions_core.CloudEvent<firebase_functions_pubsub.MessagePublishedData<any>>>;
|
|
310
305
|
/** Internal queue map (for testing). */
|
|
311
306
|
queues: Map<string, SyncQueue>;
|
|
312
307
|
/** Flush all queues and stop timers. */
|
package/dist/sync/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { S as SqlAdapter, a as SyncEvent, b as adminsyncConfig, R as RepoSyncConfig, P as PubSubClientDep, F as FirestoreSyncConfig, c as SqlDialect, d as SqlColumn, e as SqlTableDef, G as GenerateDDLConfig, L as LogicalType, f as SyncTriggersConfig, g as SyncWorkerConfig } from '../types-
|
|
2
|
-
export { h as FirestoreTriggersDep, O as OrFactory, i as PubSubHandlerDep, j as SyncDeps, k as SyncOperation, l as SyncWorkerOptions, m as adminsyncBasicAuth, n as adminsyncFeaturesFlag } from '../types-
|
|
1
|
+
import { S as SqlAdapter, a as SyncEvent, b as adminsyncConfig, R as RepoSyncConfig, P as PubSubClientDep, F as FirestoreSyncConfig, c as SqlDialect, d as SqlColumn, e as SqlTableDef, G as GenerateDDLConfig, L as LogicalType, f as SyncTriggersConfig, g as SyncWorkerConfig } from '../types-BbCdscqh.js';
|
|
2
|
+
export { A as AdminHttpsOptions, h as FirestoreTriggersDep, O as OrFactory, i as PubSubHandlerDep, j as SyncDeps, k as SyncOperation, l as SyncWorkerOptions, m as adminsyncBasicAuth, n as adminsyncFeaturesFlag } from '../types-BbCdscqh.js';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
import * as firebase_functions_core from 'firebase-functions/core';
|
|
5
|
+
import * as firebase_functions_pubsub from 'firebase-functions/pubsub';
|
|
6
|
+
import 'firebase-functions/v2/firestore';
|
|
7
|
+
import 'firebase-functions/v2/https';
|
|
4
8
|
import 'firebase-functions/v2/pubsub';
|
|
5
9
|
|
|
6
10
|
/**
|
|
@@ -283,15 +287,6 @@ declare function serializeDocument(doc: Record<string, unknown>, options?: Pick<
|
|
|
283
287
|
*/
|
|
284
288
|
declare function createSyncTriggers<M extends Record<string, any>>(repoMapping: M, config: SyncTriggersConfig<NoInfer<M>>): Record<string, any>;
|
|
285
289
|
|
|
286
|
-
/**
|
|
287
|
-
* PubSub worker — creates a Cloud Function that receives {@link SyncEvent}
|
|
288
|
-
* messages from PubSub, routes them to per-repo {@link SyncQueue}s, and
|
|
289
|
-
* flushes batches to the configured {@link SqlAdapter}.
|
|
290
|
-
*
|
|
291
|
-
* Dependencies (`firebase-functions`, `@google-cloud/pubsub`) are injected
|
|
292
|
-
* via the `deps` config property.
|
|
293
|
-
*/
|
|
294
|
-
|
|
295
290
|
/**
|
|
296
291
|
* Create a PubSub-triggered Cloud Function that syncs Firestore changes
|
|
297
292
|
* to a SQL database.
|
|
@@ -306,7 +301,7 @@ declare function createSyncWorker<M extends Record<string, any>>(repoMapping: M,
|
|
|
306
301
|
/** Process a SyncEvent directly (for testing or custom PubSub integration). */
|
|
307
302
|
handleMessage: (syncEvent: SyncEvent) => Promise<void>;
|
|
308
303
|
/** Create a Cloud Function handler for a specific PubSub topic. */
|
|
309
|
-
createHandler: (topicName: string) => any
|
|
304
|
+
createHandler: (topicName: string) => firebase_functions_core.CloudFunction<firebase_functions_core.CloudEvent<firebase_functions_pubsub.MessagePublishedData<any>>>;
|
|
310
305
|
/** Internal queue map (for testing). */
|
|
311
306
|
queues: Map<string, SyncQueue>;
|
|
312
307
|
/** Flush all queues and stop timers. */
|
package/dist/sync/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function re(o){let e=[],t=o.replace(/[.*+?^${}()|[\]\\]/g,a=>a===":"?a:`\\${a}`).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(a,s)=>(e.push(s),"([^/]+)"));return {pattern:new RegExp(`^${t}$`),paramNames:e}}function se(o){let e=o.path??o.url??"/",t=e.indexOf("?");return t===-1?e:e.slice(0,t)}var L=class{constructor(){this.routes=[];this.middlewares=[];this.notFoundHandler=(e,t)=>{t.status(404).send("Not Found");};this.errorHandler=(e,t,a)=>{console.error("[MiniRouter]",e),a.status(500).send("Internal Server Error");};}use(e){return this.middlewares.push(e),this}get(e,t){return this.addRoute("GET",e,t)}post(e,t){return this.addRoute("POST",e,t)}put(e,t){return this.addRoute("PUT",e,t)}patch(e,t){return this.addRoute("PATCH",e,t)}delete(e,t){return this.addRoute("DELETE",e,t)}onNotFound(e){return this.notFoundHandler=e,this}onError(e){return this.errorHandler=e,this}addRoute(e,t,a){let{pattern:s,paramNames:u}=re(t);return this.routes.push({method:e.toUpperCase(),pattern:s,paramNames:u,handler:a}),this}async handle(e,t){let a=(e.method??"GET").toUpperCase(),s=se(e),u=null,g={};for(let R of this.routes){if(R.method!==a)continue;let b=s.match(R.pattern);if(b){u=R,g={},R.paramNames.forEach((f,d)=>{g[f]=decodeURIComponent(b[d+1]??"");});break}}let w=Object.assign(e,{params:g}),c=u?u.handler:this.notFoundHandler;try{await this.runMiddlewareChain(w,t,c);}catch(R){this.errorHandler(R,e,t);}}async runMiddlewareChain(e,t,a){let s=0,u=async()=>{if(s<this.middlewares.length){let g=this.middlewares[s++];await g(e,t,u);}else await a(e,t);};await u();}};var ae={string:"ZodString",number:"ZodNumber",bigint:"ZodBigInt",boolean:"ZodBoolean",date:"ZodDate",enum:"ZodEnum",nativeEnum:"ZodNativeEnum",literal:"ZodLiteral",object:"ZodObject",array:"ZodArray",optional:"ZodOptional",nullable:"ZodNullable",default:"ZodDefault",coerce:"ZodCoerce",union:"ZodUnion",undefined:"ZodUndefined",unknown:"ZodUnknown",any:"ZodAny",record:"ZodRecord"};function F(o){let e=o,t=e._zod?.def?.type;if(t)return ae[t]??`Zod${t.charAt(0).toUpperCase()}${t.slice(1)}`;let a=e._def?.typeName;return a||""}function W(o){let e=o;if(e._zod?.def?.innerType)return e._zod.def.innerType;if(e._def?.innerType)return e._def.innerType}function K(o){let e=o;return e.shape&&typeof e.shape=="object"?e.shape:e._zod?.def?.shape&&typeof e._zod.def.shape=="object"?e._zod.def.shape:e._def?.shape?typeof e._def.shape=="function"?e._def.shape():e._def.shape:{}}var
|
|
1
|
+
function re(o){let e=[],t=o.replace(/[.*+?^${}()|[\]\\]/g,a=>a===":"?a:`\\${a}`).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(a,s)=>(e.push(s),"([^/]+)"));return {pattern:new RegExp(`^${t}$`),paramNames:e}}function se(o){let e=o.path??o.url??"/",t=e.indexOf("?");return t===-1?e:e.slice(0,t)}var L=class{constructor(){this.routes=[];this.middlewares=[];this.notFoundHandler=(e,t)=>{t.status(404).send("Not Found");};this.errorHandler=(e,t,a)=>{console.error("[MiniRouter]",e),a.status(500).send("Internal Server Error");};}use(e){return this.middlewares.push(e),this}get(e,t){return this.addRoute("GET",e,t)}post(e,t){return this.addRoute("POST",e,t)}put(e,t){return this.addRoute("PUT",e,t)}patch(e,t){return this.addRoute("PATCH",e,t)}delete(e,t){return this.addRoute("DELETE",e,t)}onNotFound(e){return this.notFoundHandler=e,this}onError(e){return this.errorHandler=e,this}addRoute(e,t,a){let{pattern:s,paramNames:u}=re(t);return this.routes.push({method:e.toUpperCase(),pattern:s,paramNames:u,handler:a}),this}async handle(e,t){let a=(e.method??"GET").toUpperCase(),s=se(e),u=null,g={};for(let R of this.routes){if(R.method!==a)continue;let b=s.match(R.pattern);if(b){u=R,g={},R.paramNames.forEach((f,d)=>{g[f]=decodeURIComponent(b[d+1]??"");});break}}let w=Object.assign(e,{params:g}),c=u?u.handler:this.notFoundHandler;try{await this.runMiddlewareChain(w,t,c);}catch(R){this.errorHandler(R,e,t);}}async runMiddlewareChain(e,t,a){let s=0,u=async()=>{if(s<this.middlewares.length){let g=this.middlewares[s++];await g(e,t,u);}else await a(e,t);};await u();}};var ae={string:"ZodString",number:"ZodNumber",bigint:"ZodBigInt",boolean:"ZodBoolean",date:"ZodDate",enum:"ZodEnum",nativeEnum:"ZodNativeEnum",literal:"ZodLiteral",object:"ZodObject",array:"ZodArray",optional:"ZodOptional",nullable:"ZodNullable",default:"ZodDefault",coerce:"ZodCoerce",union:"ZodUnion",undefined:"ZodUndefined",unknown:"ZodUnknown",any:"ZodAny",record:"ZodRecord"};function F(o){let e=o,t=e._zod?.def?.type;if(t)return ae[t]??`Zod${t.charAt(0).toUpperCase()}${t.slice(1)}`;let a=e._def?.typeName;return a||""}function W(o){let e=o;if(e._zod?.def?.innerType)return e._zod.def.innerType;if(e._def?.innerType)return e._def.innerType}function K(o){let e=o;return e.shape&&typeof e.shape=="object"?e.shape:e._zod?.def?.shape&&typeof e._zod.def.shape=="object"?e._zod.def.shape:e._def?.shape?typeof e._def.shape=="function"?e._def.shape():e._def.shape:{}}var _="__sync_version";var ie=new Set(["ZodOptional","ZodNullable","ZodDefault"]);function V(o){let e=o,t=false;for(;;){let a=F(e);if(!ie.has(a))break;(a==="ZodOptional"||a==="ZodNullable")&&(t=true);let s=W(e);if(!s)break;e=s;}return {inner:e,nullable:t}}var J={ZodString:"string",ZodNumber:"number",ZodBigInt:"bigint",ZodBoolean:"boolean",ZodDate:"timestamp",ZodEnum:"string",ZodNativeEnum:"string",ZodLiteral:"string"};function ce(o){let{inner:e}=V(o);return J[F(e)]??"json"}function Y(o,e,t,a,s,u,g,w){for(let[c,R]of Object.entries(o)){let b=t?`${t}__${c}`:c;if(s.has(c)||s.has(b))continue;let{inner:f,nullable:d}=V(R),l=F(f),i=a||d;if(l==="ZodObject"){let r=K(f);Y(r,e,b,i,s,u,g,w);continue}let n=J[l]??"json",p=b===g||c===g,S=u[b]??u[c]??b;w.push({name:S,sqlType:e.mapType(n),nullable:p?false:i,isPrimaryKey:p});}}function O(o,e,t={}){let{primaryKey:a,exclude:s=[],columnMap:u={}}=t,g=new Set(s),w=K(o),c=[];return Y(w,e,"",false,g,u,a,c),c.some(R=>R.name===_)||c.push({name:_,sqlType:e.mapType("bigint"),nullable:true,isPrimaryKey:false,description:"Monotonic publish version (Date.now() ms). Internal."}),c}function Q(o){if(o==null)return null;if(typeof o=="object"&&typeof o.toDate=="function")return o.toDate().toISOString();if(o instanceof Date)return o.toISOString();if(Buffer.isBuffer(o))return o.toString("base64");if(o instanceof Uint8Array)return Buffer.from(o).toString("base64");if(typeof o=="object"&&"latitude"in o&&"longitude"in o){let e=o;return JSON.stringify({lat:e.latitude,lng:e.longitude})}return Array.isArray(o)?JSON.stringify(o.map(Q)):o}function X(o,e,t){for(let[a,s]of Object.entries(o)){let u=e?`${e}__${a}`:a;s!=null&&typeof s=="object"&&!Array.isArray(s)&&!(s instanceof Date)&&!Buffer.isBuffer(s)&&!(s instanceof Uint8Array)&&typeof s.toDate!="function"&&!("latitude"in s&&"longitude"in s)?X(s,u,t):t[u]=Q(s);}}function Z(o,e){let t=new Set(e?.exclude),a=e?.columnMap??{},s={};X(o,"",s);let u={};for(let[g,w]of Object.entries(s)){if(t.has(g))continue;let c=g.split("__")[0];if(c!==g&&t.has(c))continue;let R=a[g]??(g.includes("__")?a[g.split("__").pop()]:void 0)??g;u[R]=w;}return u}function q(o,e){if(process.env.FUNCTIONS_EMULATOR==="true"){let s=process.env.GCLOUD_PROJECT??process.env.GOOGLE_CLOUD_PROJECT??"demo-project",u=process.env.FUNCTION_REGION??"us-central1",g=(process.env.FUNCTION_TARGET??"").replace(/\./g,"-");return `/${s}/${u}/${g}${e}`}let t=process.env.K_SERVICE,a=o.hostname??o.headers?.host??"";return t&&a.includes("cloudfunctions.net")?`/${t.toLowerCase()}${e}`:e}function D(o,e,t){return `<!DOCTYPE html>
|
|
2
2
|
<html lang="en"><head>
|
|
3
3
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
4
4
|
<title>${o} \u2014 Sync Admin</title>
|
|
@@ -42,7 +42,7 @@ ${t}
|
|
|
42
42
|
</table>
|
|
43
43
|
${p}
|
|
44
44
|
${S}
|
|
45
|
-
</div>`);E(l,r);}),f.get(`${c}`,(d,l)=>{let i=q(d,c);l.status(302).set("Location",`${i}/`).send("");}),R.healthCheck&&f.get(`${c}/:repoName/health`,async(d,l)=>{let i=q(d,c),n=b.find(y=>y.name===d.params.repoName);if(!n){E(l,D("Not Found",i,`<p>Unknown repo: ${d.params.repoName}</p>`),404);return}if(!n.schema){E(l,D("Health Check",i,`<p class="badge badge-warn">No Zod schema attached to "${n.name}"</p>`));return}let p=
|
|
45
|
+
</div>`);E(l,r);}),f.get(`${c}`,(d,l)=>{let i=q(d,c);l.status(302).set("Location",`${i}/`).send("");}),R.healthCheck&&f.get(`${c}/:repoName/health`,async(d,l)=>{let i=q(d,c),n=b.find(y=>y.name===d.params.repoName);if(!n){E(l,D("Not Found",i,`<p>Unknown repo: ${d.params.repoName}</p>`),404);return}if(!n.schema){E(l,D("Health Check",i,`<p class="badge badge-warn">No Zod schema attached to "${n.name}"</p>`));return}let p=O(n.schema,e.dialect,{primaryKey:n.documentKey,exclude:n.repoCfg?.exclude,columnMap:n.repoCfg?.columnMap}),S=[],r=false,h=null;try{r=await e.tableExists(n.tableName),r&&(S=await e.getTableColumns(n.tableName));}catch(y){h=y?.message??String(y);}let C=new Set(S),k=new Set(p.map(y=>y.name)),x=p.filter(y=>!C.has(y.name)),P=S.filter(y=>!k.has(y)),A=p.filter(y=>C.has(y.name)),m=r&&x.length===0&&!h;if(I(d)){z(l,{repo:n.name,table:n.tableName,tableExists:r,healthy:m,error:h,columns:{expected:p.map(y=>({name:y.name,type:y.sqlType,nullable:y.nullable,isPrimaryKey:y.isPrimaryKey})),actual:S,matched:A.map(y=>y.name),missing:x.map(y=>({name:y.name,type:y.sqlType})),extra:P}});return}let $=m?'<span class="badge badge-ok">Healthy</span>':'<span class="badge badge-err">Unhealthy</span>',T=p.map(y=>{let j=C.has(y.name)?'<span class="badge badge-ok">OK</span>':'<span class="badge badge-err">MISSING</span>';return `<tr><td>${y.name}</td><td>${y.sqlType}</td><td>${y.nullable?"Yes":"No"}</td><td>${y.isPrimaryKey?"\u2713":""}</td><td>${j}</td></tr>`}).join(`
|
|
46
46
|
`),v=P.map(y=>`<tr><td>${y}</td><td colspan="3" class="muted">not in schema</td><td><span class="badge badge-warn">EXTRA</span></td></tr>`).join(`
|
|
47
47
|
`),N=D(`Health: ${n.name}`,i,`<div class="card">
|
|
48
48
|
<p>Table: <code>${n.tableName}</code> ${r?$:'<span class="badge badge-err">NOT FOUND</span>'}</p>
|
|
@@ -88,11 +88,11 @@ ${t}
|
|
|
88
88
|
${k("BigQuery",C.bigquery)}
|
|
89
89
|
${k("Pub/Sub",C.pubsub)}
|
|
90
90
|
${k("Firestore",C.firestore)}
|
|
91
|
-
</div>`);E(l,A);}),async(d,l)=>{await f.handle(d,l);}}var de="firestore-sync";function ue(o,e){let t=e.ref?.path??void 0;return t?`${t}/{docId}`:(console.warn(`[SyncTriggers] Cannot determine collection path for "${o}". Skipping.`),null)}function H(o,e){let{onDocumentCreated:t,onDocumentUpdated:a,onDocumentDeleted:s}=e.deps.firestoreTriggers,u=e.deps.pubsub,g=e?.topicPrefix??de,w={},c=new Map;function R(f){let d=c.get(f);return d||(d=u.topic(f),c.set(f,d),d)}async function b(f,d){await R(f).publishMessage({json:d});}for(let[f,d]of Object.entries(o)){let l=e?.repos?.[f],i;if(d._isGroup){if(!l?.triggerPath){console.warn(`[SyncTriggers] Skipping collection-group repo "${f}". Provide a triggerPath in the sync repos config for group collections.`);continue}i=l.triggerPath;}else i=l?.triggerPath??ue(f,d);if(!i)continue;let n=d._systemKeys?.[0]??"docId",p=`${g}-${f}`;w[`${f}_onCreate`]=t(i,async S=>{let r=S.data;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"INSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onUpdate`]=a(i,async S=>{let r=S.data?.after;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"UPSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onDelete`]=s(i,async S=>{let r=S.data;if(!r)return;let h=r.data(),C=String(h?.[n]??r.id),k={operation:"DELETE",repoName:f,docId:C,data:null,timestamp:new Date().toISOString(),version:Date.now()};await b(p,k);});}return w}var M=class{constructor(e){this.buffer=[];this.flushing=false;this.timer=null;this.adapter=e.adapter,this.tableName=e.tableName,this.primaryKey=e.primaryKey,this.batchSize=e.batchSize??100,this.onFlushError=e.onFlushError;let t=e.flushIntervalMs??5e3;t>0&&(this.timer=setInterval(()=>{this.flush();},t),typeof this.timer=="object"&&"unref"in this.timer&&this.timer.unref());}get size(){return this.buffer.length}enqueue(...e){this.buffer.push(...e),this.buffer.length>=this.batchSize&&this.flush();}async flush(){if(this.flushing||this.buffer.length===0)return;this.flushing=true;let e=this.buffer.splice(0,this.batchSize);try{let t=new Map,a=[];for(let u of e)if(u.operation==="DELETE")a.push(u.docId),t.delete(u.docId);else if(u.data){let g=t.get(u.docId);if(!g)t.set(u.docId,u.data);else {let w=Number(g[
|
|
91
|
+
</div>`);E(l,A);}),async(d,l)=>{await f.handle(d,l);}}var de="firestore-sync";function ue(o,e){let t=e.ref?.path??void 0;return t?`${t}/{docId}`:(console.warn(`[SyncTriggers] Cannot determine collection path for "${o}". Skipping.`),null)}function H(o,e){let{onDocumentCreated:t,onDocumentUpdated:a,onDocumentDeleted:s}=e.deps.firestoreTriggers,u=e.deps.pubsub,g=e?.topicPrefix??de,w={},c=new Map;function R(f){let d=c.get(f);return d||(d=u.topic(f),c.set(f,d),d)}async function b(f,d){await R(f).publishMessage({json:d});}for(let[f,d]of Object.entries(o)){let l=e?.repos?.[f],i;if(d._isGroup){if(!l?.triggerPath){console.warn(`[SyncTriggers] Skipping collection-group repo "${f}". Provide a triggerPath in the sync repos config for group collections.`);continue}i=l.triggerPath;}else i=l?.triggerPath??ue(f,d);if(!i)continue;let n=d._systemKeys?.[0]??"docId",p=`${g}-${f}`;w[`${f}_onCreate`]=t(i,async S=>{let r=S.data;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"INSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onUpdate`]=a(i,async S=>{let r=S.data?.after;if(!r)return;let h=r.data();if(!h)return;let C=String(h[n]??r.id),k=Z(h,{exclude:l?.exclude,columnMap:l?.columnMap}),x={operation:"UPSERT",repoName:f,docId:C,data:k,timestamp:new Date().toISOString(),version:Date.now()};await b(p,x);}),w[`${f}_onDelete`]=s(i,async S=>{let r=S.data;if(!r)return;let h=r.data(),C=String(h?.[n]??r.id),k={operation:"DELETE",repoName:f,docId:C,data:null,timestamp:new Date().toISOString(),version:Date.now()};await b(p,k);});}return w}var M=class{constructor(e){this.buffer=[];this.flushing=false;this.timer=null;this.adapter=e.adapter,this.tableName=e.tableName,this.primaryKey=e.primaryKey,this.batchSize=e.batchSize??100,this.onFlushError=e.onFlushError;let t=e.flushIntervalMs??5e3;t>0&&(this.timer=setInterval(()=>{this.flush();},t),typeof this.timer=="object"&&"unref"in this.timer&&this.timer.unref());}get size(){return this.buffer.length}enqueue(...e){this.buffer.push(...e),this.buffer.length>=this.batchSize&&this.flush();}async flush(){if(this.flushing||this.buffer.length===0)return;this.flushing=true;let e=this.buffer.splice(0,this.batchSize);try{let t=new Map,a=[];for(let u of e)if(u.operation==="DELETE")a.push(u.docId),t.delete(u.docId);else if(u.data){let g=t.get(u.docId);if(!g)t.set(u.docId,u.data);else {let w=Number(g[_]??0);Number(u.data[_]??0)>=w&&t.set(u.docId,u.data);}}let s=Array.from(t.values());s.length>0&&await this.adapter.upsertRows(this.tableName,s,this.primaryKey),a.length>0&&await this.adapter.deleteRows(this.tableName,this.primaryKey,a);}catch(t){this.onFlushError?await this.onFlushError(e,t).catch(a=>{console.error(`[SyncQueue] Flush error for ${this.tableName}:`,t),console.error("[SyncQueue] Error handler also failed:",a);}):(this.buffer.unshift(...e),console.error(`[SyncQueue] Flush failed for ${this.tableName}:`,t));}finally{this.flushing=false;}}async shutdown(){this.timer&&(clearInterval(this.timer),this.timer=null),await this.flush();}};var ee=new Set;async function le(o,e,t,a,s,u,g){if(ee.has(o))return;let w=O(t,e.dialect,{primaryKey:s,exclude:u,columnMap:g});if(!await e.tableExists(a))await e.createTable({tableName:a,columns:w});else {let R=new Set(await e.getTableColumns(a)),b=w.filter(f=>!R.has(f.name));b.length>0&&await e.addColumns(a,b);}ee.add(o);}function G(o,e){let{deps:t,adapter:a,batchSize:s=100,flushIntervalMs:u=5e3,autoMigrate:g=false,topicPrefix:w="firestore-sync",workerOptions:c,repos:R={}}=e,b=new Map;function f(i,n){let p=b.get(i);if(p)return p;let r=R[i]?.tableName??i,h=async(C,k)=>{console.error(`[SyncWorker] Flush failed for "${i}" (${C.length} events):`,k);try{let x=`${w}-${i}-dlq`,P=t.pubsub.topic(x),[A]=await P.exists();A||(await P.create(),console.info(`[SyncWorker] Created DLQ topic "${x}"`));for(let m of C)await P.publishMessage({json:m});}catch(x){console.error(`[SyncWorker] Dead-letter publish also failed for ${i}:`,x);}};return p=new M({adapter:a,tableName:r,primaryKey:n,batchSize:s,flushIntervalMs:u,onFlushError:h}),b.set(i,p),p}async function d(i){let{repoName:n}=i,p=o[n];if(!p){console.warn(`[SyncWorker] Unknown repo "${n}", skipping event`);return}let S=p._systemKeys?.[0]??p.documentKey??"docId",r=R[n],h=r?.columnMap,C=h?.[S]??S;if(g){let x=p.schema??void 0;if(x){let P=r?.tableName??n;await le(n,a,x,P,S,r?.exclude,h);}}let k=f(n,C);i.data&&(i.data[_]=i.version??Date.now()),k.enqueue(i);}function l(i){let n=async p=>{let S=p.data?.message?.json??p.data?.json;if(!S){console.warn("[SyncWorker] Received empty PubSub message");return}await d(S);let r=b.get(S.repoName);r&&await r.flush();};return c?t.pubsubHandler.onMessagePublished({topic:i,...c},n):t.pubsubHandler.onMessagePublished(i,n)}return {handleMessage:d,createHandler:l,queues:b,async shutdown(){let i=[];for(let n of b.values())i.push(n.shutdown());await Promise.all(i);}}}var pe="firestore-sync";function te(o){if(typeof o!="function")return o;let e=o,t;return new Proxy({},{get(a,s){return t||(t=e()),t[s]},has(a,s){return t||(t=e()),s in t}})}function fe(o,e){let{deps:t,adapter:a,topicPrefix:s=pe,batchSize:u,flushIntervalMs:g,autoMigrate:w,admin:c,workerOptions:R,repos:b}=e,f=te(t.pubsub),d=te(a),l=H(o,{deps:{firestoreTriggers:t.firestoreTriggers,pubsub:f},topicPrefix:s,repos:b}),i=G(o,{deps:{pubsubHandler:t.pubsubHandler,pubsub:f},adapter:d,batchSize:u,flushIntervalMs:g,autoMigrate:w,topicPrefix:s,workerOptions:R,repos:b}),n={};for(let r of Object.keys(o))n[`sync_${r}`]=i.createHandler(`${s}-${r}`);let p=null;c&&(p=B(o,d,i.queues,i.handleMessage,c,b??{},f,s),n.adminsync=c.onRequest?c.httpsOptions?c.onRequest(c.httpsOptions,p):c.onRequest(p):p);let S={functions:{...l,...n},adminHandler:p,handleMessage:i.handleMessage,queues:i.queues,shutdown:i.shutdown};for(let r of ["adminHandler","handleMessage","queues","shutdown"])Object.defineProperty(S,r,{enumerable:false});return S}function ne(o,e){let t=e.columns.map(a=>{let s=a.isPrimaryKey?" NOT NULL":"";return ` ${o.quoteIdentifier(a.name)} ${a.sqlType}${s}`}).join(`,
|
|
92
92
|
`);return `CREATE TABLE IF NOT EXISTS ${o.quoteIdentifier(e.tableName)} (
|
|
93
93
|
${t}
|
|
94
94
|
);`}function me(o,e,t){return t.map(a=>`ALTER TABLE ${o.quoteIdentifier(e)} ADD COLUMN ${o.quoteIdentifier(a.name)} ${a.sqlType};`).join(`
|
|
95
|
-
`)}function ge(o,e,t){let a=[];for(let[s,u]of Object.entries(o)){let g=u.schema??u._schema??void 0;if(!g)continue;let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=
|
|
95
|
+
`)}function ge(o,e,t){let a=[];for(let[s,u]of Object.entries(o)){let g=u.schema??u._schema??void 0;if(!g)continue;let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=O(g,e,{primaryKey:R,exclude:w?.exclude,columnMap:w?.columnMap}),f={tableName:c,columns:b};a.push(ne(e,f));}return a.join(`
|
|
96
96
|
|
|
97
|
-
`)}async function ye(o,e,t){let a={created:[],altered:[],upToDate:[],skipped:[]};for(let[s,u]of Object.entries(o)){let g=u.schema??void 0;if(!g){a.skipped.push(s);continue}let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=
|
|
97
|
+
`)}async function ye(o,e,t){let a={created:[],altered:[],upToDate:[],skipped:[]};for(let[s,u]of Object.entries(o)){let g=u.schema??void 0;if(!g){a.skipped.push(s);continue}let w=t?.repos?.[s],c=w?.tableName??s,R=u._systemKeys?.[0]??u.documentKey??"docId",b=O(g,e.dialect,{primaryKey:R,exclude:w?.exclude,columnMap:w?.columnMap}),f={tableName:c,columns:b};if(!await e.tableExists(c))await e.createTable(f),a.created.push(c);else {let l=new Set(await e.getTableColumns(c)),i=b.filter(n=>!l.has(n.name));i.length>0?(await e.addColumns(c,i),a.altered.push(c)):a.upToDate.push(c);}}return a}export{M as SyncQueue,me as addColumnsDDL,ye as autoMigrate,fe as createFirestoreSync,H as createSyncTriggers,G as createSyncWorker,ne as createTableDDL,B as createadminsyncServer,ge as generateDDL,Z as serializeDocument,Q as serializeValue,O as zodSchemaToColumns,ce as zodTypeToLogical};//# sourceMappingURL=index.js.map
|
|
98
98
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as zod from 'zod';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { HttpsOptions } from 'firebase-functions/v2/https';
|
|
3
4
|
import { DocumentReference, Firestore, DocumentSnapshot, WhereFilterOp, Query, CollectionReference, WriteBatch, Transaction } from 'firebase-admin/firestore';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -820,7 +821,7 @@ interface CrudServerOptions<TRepos extends Record<string, ConfiguredRepository<a
|
|
|
820
821
|
* export const crud = onRequest(handler.httpsOptions!, handler);
|
|
821
822
|
* ```
|
|
822
823
|
*/
|
|
823
|
-
httpsOptions?:
|
|
824
|
+
httpsOptions?: HttpsOptions;
|
|
824
825
|
}
|
|
825
826
|
/**
|
|
826
827
|
* Standard API response wrapper.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as zod from 'zod';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { HttpsOptions } from 'firebase-functions/v2/https';
|
|
3
4
|
import { DocumentReference, Firestore, DocumentSnapshot, WhereFilterOp, Query, CollectionReference, WriteBatch, Transaction } from 'firebase-admin/firestore';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -820,7 +821,7 @@ interface CrudServerOptions<TRepos extends Record<string, ConfiguredRepository<a
|
|
|
820
821
|
* export const crud = onRequest(handler.httpsOptions!, handler);
|
|
821
822
|
* ```
|
|
822
823
|
*/
|
|
823
|
-
httpsOptions?:
|
|
824
|
+
httpsOptions?: HttpsOptions;
|
|
824
825
|
}
|
|
825
826
|
/**
|
|
826
827
|
* Standard API response wrapper.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { onDocumentCreated, onDocumentUpdated, onDocumentDeleted } from 'firebase-functions/v2/firestore';
|
|
2
|
+
import { onRequest, HttpsOptions } from 'firebase-functions/v2/https';
|
|
3
|
+
import { onMessagePublished, PubSubOptions } from 'firebase-functions/v2/pubsub';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Types and interfaces for the Firestore → SQL sync module.
|
|
@@ -14,6 +16,11 @@ import { PubSubOptions } from 'firebase-functions/v2/pubsub';
|
|
|
14
16
|
* it itself from `topicPrefix` + repo name.
|
|
15
17
|
*/
|
|
16
18
|
type SyncWorkerOptions = Omit<PubSubOptions, "topic">;
|
|
19
|
+
/**
|
|
20
|
+
* Cloud Functions v2 options forwarded to `onRequest()` for the admin handler.
|
|
21
|
+
* Re-export of `HttpsOptions` from `firebase-functions/v2/https`.
|
|
22
|
+
*/
|
|
23
|
+
type AdminHttpsOptions = HttpsOptions;
|
|
17
24
|
/** A value that can be provided directly or as a lazy factory function. */
|
|
18
25
|
type OrFactory<T> = T | (() => T);
|
|
19
26
|
/** A single column in a SQL table. */
|
|
@@ -174,13 +181,13 @@ type TypedRepoSyncConfigs<M> = {
|
|
|
174
181
|
};
|
|
175
182
|
/** Firestore trigger constructors from `firebase-functions/v2/firestore`. */
|
|
176
183
|
interface FirestoreTriggersDep {
|
|
177
|
-
onDocumentCreated:
|
|
178
|
-
onDocumentUpdated:
|
|
179
|
-
onDocumentDeleted:
|
|
184
|
+
onDocumentCreated: typeof onDocumentCreated;
|
|
185
|
+
onDocumentUpdated: typeof onDocumentUpdated;
|
|
186
|
+
onDocumentDeleted: typeof onDocumentDeleted;
|
|
180
187
|
}
|
|
181
188
|
/** PubSub handler from `firebase-functions/v2/pubsub`. */
|
|
182
189
|
interface PubSubHandlerDep {
|
|
183
|
-
onMessagePublished:
|
|
190
|
+
onMessagePublished: typeof onMessagePublished;
|
|
184
191
|
}
|
|
185
192
|
/** PubSub client instance (e.g. `new PubSub()`). */
|
|
186
193
|
interface PubSubClientDep {
|
|
@@ -277,16 +284,17 @@ interface adminsyncConfig {
|
|
|
277
284
|
/** Feature flags controlling which endpoints are enabled */
|
|
278
285
|
featuresFlag?: adminsyncFeaturesFlag;
|
|
279
286
|
/**
|
|
280
|
-
* `onRequest` from `firebase-functions/https` (or `firebase-functions/
|
|
287
|
+
* `onRequest` from `firebase-functions/v2/https` (or `firebase-functions/https`,
|
|
288
|
+
* which re-exports the v2 version in `firebase-functions` ≥ 5).
|
|
281
289
|
* When provided, the admin handler is automatically wrapped as a Cloud Function.
|
|
282
290
|
* If omitted, the raw `(req, res) => void` handler is exposed instead.
|
|
283
291
|
*/
|
|
284
|
-
onRequest?:
|
|
292
|
+
onRequest?: typeof onRequest;
|
|
285
293
|
/**
|
|
286
294
|
* Options forwarded to `onRequest()` as the first argument (e.g. `{ invoker: "public" }`).
|
|
287
295
|
* Only used when `onRequest` is also provided.
|
|
288
296
|
*/
|
|
289
|
-
httpsOptions?:
|
|
297
|
+
httpsOptions?: AdminHttpsOptions;
|
|
290
298
|
}
|
|
291
299
|
/** Options for `createFirestoreSync()` — the unified wrapper. */
|
|
292
300
|
interface FirestoreSyncConfig<M = Record<string, any>> {
|
|
@@ -329,4 +337,4 @@ interface FirestoreSyncConfig<M = Record<string, any>> {
|
|
|
329
337
|
repos?: TypedRepoSyncConfigs<M>;
|
|
330
338
|
}
|
|
331
339
|
|
|
332
|
-
export type { FirestoreSyncConfig as F, GenerateDDLConfig as G, LogicalType as L, OrFactory as O, PubSubClientDep as P, RepoSyncConfig as R, SqlAdapter as S, SyncEvent as a, adminsyncConfig as b, SqlDialect as c, SqlColumn as d, SqlTableDef as e, SyncTriggersConfig as f, SyncWorkerConfig as g, FirestoreTriggersDep as h, PubSubHandlerDep as i, SyncDeps as j, SyncOperation as k, SyncWorkerOptions as l, adminsyncBasicAuth as m, adminsyncFeaturesFlag as n };
|
|
340
|
+
export type { AdminHttpsOptions as A, FirestoreSyncConfig as F, GenerateDDLConfig as G, LogicalType as L, OrFactory as O, PubSubClientDep as P, RepoSyncConfig as R, SqlAdapter as S, SyncEvent as a, adminsyncConfig as b, SqlDialect as c, SqlColumn as d, SqlTableDef as e, SyncTriggersConfig as f, SyncWorkerConfig as g, FirestoreTriggersDep as h, PubSubHandlerDep as i, SyncDeps as j, SyncOperation as k, SyncWorkerOptions as l, adminsyncBasicAuth as m, adminsyncFeaturesFlag as n };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { onDocumentCreated, onDocumentUpdated, onDocumentDeleted } from 'firebase-functions/v2/firestore';
|
|
2
|
+
import { onRequest, HttpsOptions } from 'firebase-functions/v2/https';
|
|
3
|
+
import { onMessagePublished, PubSubOptions } from 'firebase-functions/v2/pubsub';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Types and interfaces for the Firestore → SQL sync module.
|
|
@@ -14,6 +16,11 @@ import { PubSubOptions } from 'firebase-functions/v2/pubsub';
|
|
|
14
16
|
* it itself from `topicPrefix` + repo name.
|
|
15
17
|
*/
|
|
16
18
|
type SyncWorkerOptions = Omit<PubSubOptions, "topic">;
|
|
19
|
+
/**
|
|
20
|
+
* Cloud Functions v2 options forwarded to `onRequest()` for the admin handler.
|
|
21
|
+
* Re-export of `HttpsOptions` from `firebase-functions/v2/https`.
|
|
22
|
+
*/
|
|
23
|
+
type AdminHttpsOptions = HttpsOptions;
|
|
17
24
|
/** A value that can be provided directly or as a lazy factory function. */
|
|
18
25
|
type OrFactory<T> = T | (() => T);
|
|
19
26
|
/** A single column in a SQL table. */
|
|
@@ -174,13 +181,13 @@ type TypedRepoSyncConfigs<M> = {
|
|
|
174
181
|
};
|
|
175
182
|
/** Firestore trigger constructors from `firebase-functions/v2/firestore`. */
|
|
176
183
|
interface FirestoreTriggersDep {
|
|
177
|
-
onDocumentCreated:
|
|
178
|
-
onDocumentUpdated:
|
|
179
|
-
onDocumentDeleted:
|
|
184
|
+
onDocumentCreated: typeof onDocumentCreated;
|
|
185
|
+
onDocumentUpdated: typeof onDocumentUpdated;
|
|
186
|
+
onDocumentDeleted: typeof onDocumentDeleted;
|
|
180
187
|
}
|
|
181
188
|
/** PubSub handler from `firebase-functions/v2/pubsub`. */
|
|
182
189
|
interface PubSubHandlerDep {
|
|
183
|
-
onMessagePublished:
|
|
190
|
+
onMessagePublished: typeof onMessagePublished;
|
|
184
191
|
}
|
|
185
192
|
/** PubSub client instance (e.g. `new PubSub()`). */
|
|
186
193
|
interface PubSubClientDep {
|
|
@@ -277,16 +284,17 @@ interface adminsyncConfig {
|
|
|
277
284
|
/** Feature flags controlling which endpoints are enabled */
|
|
278
285
|
featuresFlag?: adminsyncFeaturesFlag;
|
|
279
286
|
/**
|
|
280
|
-
* `onRequest` from `firebase-functions/https` (or `firebase-functions/
|
|
287
|
+
* `onRequest` from `firebase-functions/v2/https` (or `firebase-functions/https`,
|
|
288
|
+
* which re-exports the v2 version in `firebase-functions` ≥ 5).
|
|
281
289
|
* When provided, the admin handler is automatically wrapped as a Cloud Function.
|
|
282
290
|
* If omitted, the raw `(req, res) => void` handler is exposed instead.
|
|
283
291
|
*/
|
|
284
|
-
onRequest?:
|
|
292
|
+
onRequest?: typeof onRequest;
|
|
285
293
|
/**
|
|
286
294
|
* Options forwarded to `onRequest()` as the first argument (e.g. `{ invoker: "public" }`).
|
|
287
295
|
* Only used when `onRequest` is also provided.
|
|
288
296
|
*/
|
|
289
|
-
httpsOptions?:
|
|
297
|
+
httpsOptions?: AdminHttpsOptions;
|
|
290
298
|
}
|
|
291
299
|
/** Options for `createFirestoreSync()` — the unified wrapper. */
|
|
292
300
|
interface FirestoreSyncConfig<M = Record<string, any>> {
|
|
@@ -329,4 +337,4 @@ interface FirestoreSyncConfig<M = Record<string, any>> {
|
|
|
329
337
|
repos?: TypedRepoSyncConfigs<M>;
|
|
330
338
|
}
|
|
331
339
|
|
|
332
|
-
export type { FirestoreSyncConfig as F, GenerateDDLConfig as G, LogicalType as L, OrFactory as O, PubSubClientDep as P, RepoSyncConfig as R, SqlAdapter as S, SyncEvent as a, adminsyncConfig as b, SqlDialect as c, SqlColumn as d, SqlTableDef as e, SyncTriggersConfig as f, SyncWorkerConfig as g, FirestoreTriggersDep as h, PubSubHandlerDep as i, SyncDeps as j, SyncOperation as k, SyncWorkerOptions as l, adminsyncBasicAuth as m, adminsyncFeaturesFlag as n };
|
|
340
|
+
export type { AdminHttpsOptions as A, FirestoreSyncConfig as F, GenerateDDLConfig as G, LogicalType as L, OrFactory as O, PubSubClientDep as P, RepoSyncConfig as R, SqlAdapter as S, SyncEvent as a, adminsyncConfig as b, SqlDialect as c, SqlColumn as d, SqlTableDef as e, SyncTriggersConfig as f, SyncWorkerConfig as g, FirestoreTriggersDep as h, PubSubHandlerDep as i, SyncDeps as j, SyncOperation as k, SyncWorkerOptions as l, adminsyncBasicAuth as m, adminsyncFeaturesFlag as n };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lpdjs/firestore-repo-service",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "⚡ Type-safe Firestore ORM with auto-generated repositories, advanced queries, relations with typed select, pagination with include, batch/bulk operations, aggregations and transactions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
"@google-cloud/bigquery": "^7.0.0 || ^8.0.0",
|
|
153
153
|
"@google-cloud/pubsub": "^4.0.0 || ^5.0.0",
|
|
154
154
|
"firebase-admin": "^11.0.0 || ^12.0.0 || ^13.0.0",
|
|
155
|
-
"firebase-functions": "^
|
|
155
|
+
"firebase-functions": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
156
156
|
"typescript": "^5.0.0",
|
|
157
157
|
"zod": "^4"
|
|
158
158
|
},
|
|
@@ -168,20 +168,20 @@
|
|
|
168
168
|
}
|
|
169
169
|
},
|
|
170
170
|
"devDependencies": {
|
|
171
|
-
"@google-cloud/bigquery": "^8.
|
|
171
|
+
"@google-cloud/bigquery": "^8.3.0",
|
|
172
172
|
"@google-cloud/pubsub": "^5.3.0",
|
|
173
173
|
"@types/bun": "latest",
|
|
174
174
|
"@types/express": "latest",
|
|
175
175
|
"concurrently": "^9.2.1",
|
|
176
|
-
"firebase-admin": "^13.
|
|
177
|
-
"firebase-functions": "^7.2.
|
|
176
|
+
"firebase-admin": "^13.8.0",
|
|
177
|
+
"firebase-functions": "^7.2.5",
|
|
178
178
|
"tsup": "^8.5.1",
|
|
179
179
|
"typescript": "^5.9.3",
|
|
180
|
-
"wait-on": "^9.0.
|
|
181
|
-
"zod": "^4.3
|
|
180
|
+
"wait-on": "^9.0.5",
|
|
181
|
+
"zod": "^4.4.3"
|
|
182
182
|
},
|
|
183
183
|
"dependencies": {
|
|
184
|
-
"@hono/node-server": "^1.19.
|
|
185
|
-
"hono": "^4.12.
|
|
184
|
+
"@hono/node-server": "^1.19.14",
|
|
185
|
+
"hono": "^4.12.17"
|
|
186
186
|
}
|
|
187
187
|
}
|