@ienlab/cloud-functions-library 1.0.0-dev.10 → 1.0.0-dev.12

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.
@@ -1,12 +1,18 @@
1
- import { HttpsFunction } from 'firebase-functions/v2/https';
1
+ import { HttpsFunction, Request } from 'firebase-functions/v2/https';
2
2
  export interface OGData {
3
3
  title: string;
4
4
  description: string;
5
5
  image: string;
6
6
  }
7
+ export interface SSRResolveContext {
8
+ req: Request;
9
+ pathname: string;
10
+ searchParams: URLSearchParams;
11
+ match: RegExpMatchArray;
12
+ }
7
13
  export interface SSRResolver {
8
14
  pattern: RegExp;
9
- resolve: (id: string) => Promise<OGData | null>;
15
+ resolve: (id: string, context: SSRResolveContext) => Promise<OGData | null>;
10
16
  }
11
17
  export interface SSRDynamicOGConfig {
12
18
  spaUrl: string;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("firebase-admin/firestore"),t=require("firebase-functions/v2/https");function n(e){return{...e.data(),id:e.id}}function r(e,t,n){let r=e.headers.origin;return r&&n.includes(r)&&(t.set(`Access-Control-Allow-Origin`,r),t.set(`Vary`,`Origin`)),t.set(`Access-Control-Allow-Methods`,`POST, OPTIONS`),t.set(`Access-Control-Allow-Headers`,`Content-Type, Authorization`),e.method===`OPTIONS`?(t.status(204).send(),!1):e.method===`POST`?!0:(t.status(405).send({error:`Method Not Allowed`}),!1)}var i=()=>({updateAt:e.FieldValue.serverTimestamp(),deletedAt:e.FieldValue.serverTimestamp()}),a=()=>({updateAt:e.FieldValue.serverTimestamp(),deletedAt:null});function o(e){let{spaUrl:n,defaultOG:r,resolvers:i,region:a=`asia-northeast3`}=e,o=null;async function s(){if(o)return o;let e=await fetch(n);if(!e.ok)throw Error(`Failed to fetch template: ${e.status}`);return o=await e.text(),o}function c(e,t,n,r){return e.replace(/(<title>)[^<]*(<\/title>)/,`$1${t}$2`).replace(/(<meta\s+name="description"\s+content=")[^"]*(")/,`$1${n}$2`).replace(/(<meta\s+property="og:title"\s+content=")[^"]*(")/,`$1${t}$2`).replace(/(<meta\s+property="og:description"\s+content=")[^"]*(")/,`$1${n}$2`).replace(/(<meta\s+property="og:image"\s+content=")[^"]*(")/,`$1${r}$2`).replace(/(<meta\s+name="twitter:title"\s+content=")[^"]*(")/,`$1${t}$2`).replace(/(<meta\s+name="twitter:description"\s+content=")[^"]*(")/,`$1${n}$2`).replace(/(<meta\s+name="twitter:image"\s+content=")[^"]*(")/,`$1${r}$2`)}return(0,t.onRequest)({region:a},async(e,t)=>{let a=Date.now();try{let n=new URL(e.url,`https://${e.headers.host??`localhost`}`).pathname,o=r;for(let e of i){let t=n.match(e.pattern);if(t){let n=await e.resolve(t[1]);if(n){o=n;break}}}let l=c(await s(),o.title,o.description,o.image);console.log(`[SSRDynamicOG] ${n} → ${o.title} (${Date.now()-a}ms)`),t.setHeader(`Content-Type`,`text/html; charset=utf-8`),t.status(200).send(l)}catch(e){console.error(`[SSRDynamicOG] error:`,e),t.redirect(302,n)}})}exports.applyCors=r,exports.createSSRDynamicOG=o,exports.del=i,exports.snapshotToData=n,exports.undel=a;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("firebase-admin/firestore"),t=require("firebase-functions/v2/https");function n(e){return{...e.data(),id:e.id}}function r(e,t,n){let r=e.headers.origin;return r&&n.includes(r)&&(t.set(`Access-Control-Allow-Origin`,r),t.set(`Vary`,`Origin`)),t.set(`Access-Control-Allow-Methods`,`POST, OPTIONS`),t.set(`Access-Control-Allow-Headers`,`Content-Type, Authorization`),e.method===`OPTIONS`?(t.status(204).send(),!1):e.method===`POST`?!0:(t.status(405).send({error:`Method Not Allowed`}),!1)}var i=()=>({updateAt:e.FieldValue.serverTimestamp(),deletedAt:e.FieldValue.serverTimestamp()}),a=()=>({updateAt:e.FieldValue.serverTimestamp(),deletedAt:null});function o(e){let{spaUrl:n,defaultOG:r,resolvers:i,region:a=`asia-northeast3`}=e,o=null,s=0;function c(){let e=Date.now();return(!o||e-s>18e5)&&(s=e,o=fetch(n,{signal:AbortSignal.timeout(5e3)}).then(async e=>{if(!e.ok)throw o=null,s=0,Error(`Failed to fetch template: ${e.status}`);return e.text()}).catch(e=>{throw o=null,s=0,e})),o}function l(e){return e?String(e).replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#039;`):``}function u(e,t,n,r){let i=l(r),a=RegExp(`(<meta\\s+[^>]*${t}=["']${n}["'][^>]*>)`,`gi`),o=!1,s=e.replace(a,e=>(o=!0,/\bcontent=/i.test(e)?e.replace(/\bcontent=(["'])(.*?)\1/i,`content=$1${i}$1`):e.replace(/(\/?>)$/,` content="${i}"$1`)));if(!o){let e=`<meta ${t}="${n}" content="${i}">`;return s.replace(/(<\/head>)/i,`${e}\n$1`)}return s}function d(e,t,n,r){let i=l(t),a=e;return a=/<title>.*<\/title>/i.test(a)?a.replace(/(<title>)[^<]*(<\/title>)/i,`$1${i}$2`):a.replace(/(<\/head>)/i,`<title>${i}</title>\n$1`),a=u(a,`name`,`description`,n),a=u(a,`property`,`og:title`,t),a=u(a,`property`,`og:description`,n),a=u(a,`property`,`og:image`,r),a=u(a,`property`,`og:type`,`website`),a=u(a,`name`,`twitter:card`,`summary_large_image`),a=u(a,`name`,`twitter:title`,t),a=u(a,`name`,`twitter:description`,n),a=u(a,`name`,`twitter:image`,r),a}return(0,t.onRequest)({region:a},async(e,t)=>{let a=Date.now(),o=e.path,s=new URL(e.url,n),l=s.searchParams;try{let n=r;for(let t of i){let r=o.match(t.pattern);if(!r)continue;let i=r[1]??r[0];try{let a=await t.resolve(i,{req:e,pathname:o,searchParams:l,match:r});if(a){n=a;break}}catch(e){console.error(`[SSRDynamicOG] Failed to resolve "${o}${s.search}" with pattern ${t.pattern}:`,e)}}let u=d(await c(),n.title,n.description,n.image);console.log(`[SSRDynamicOG] ${o}${s.search} → ${n.title} (${Date.now()-a}ms)`),t.setHeader(`Content-Type`,`text/html; charset=utf-8`),t.status(200).send(u)}catch(e){console.error(`[SSRDynamicOG] error:`,e),t.redirect(302,n)}})}exports.applyCors=r,exports.createSSRDynamicOG=o,exports.del=i,exports.snapshotToData=n,exports.undel=a;
package/dist/index.mjs CHANGED
@@ -25,32 +25,56 @@ var i = () => ({
25
25
  //#endregion
26
26
  //#region src/handlers/ssr-dynamic-og.ts
27
27
  function o(e) {
28
- let { spaUrl: n, defaultOG: r, resolvers: i, region: a = "asia-northeast3" } = e, o = null;
29
- async function s() {
30
- if (o) return o;
31
- let e = await fetch(n);
32
- if (!e.ok) throw Error(`Failed to fetch template: ${e.status}`);
33
- return o = await e.text(), o;
28
+ let { spaUrl: n, defaultOG: r, resolvers: i, region: a = "asia-northeast3" } = e, o = null, s = 0;
29
+ function c() {
30
+ let e = Date.now();
31
+ return (!o || e - s > 18e5) && (s = e, o = fetch(n, { signal: AbortSignal.timeout(5e3) }).then(async (e) => {
32
+ if (!e.ok) throw o = null, s = 0, Error(`Failed to fetch template: ${e.status}`);
33
+ return e.text();
34
+ }).catch((e) => {
35
+ throw o = null, s = 0, e;
36
+ })), o;
34
37
  }
35
- function c(e, t, n, r) {
36
- return e.replace(/(<title>)[^<]*(<\/title>)/, `$1${t}$2`).replace(/(<meta\s+name="description"\s+content=")[^"]*(")/, `$1${n}$2`).replace(/(<meta\s+property="og:title"\s+content=")[^"]*(")/, `$1${t}$2`).replace(/(<meta\s+property="og:description"\s+content=")[^"]*(")/, `$1${n}$2`).replace(/(<meta\s+property="og:image"\s+content=")[^"]*(")/, `$1${r}$2`).replace(/(<meta\s+name="twitter:title"\s+content=")[^"]*(")/, `$1${t}$2`).replace(/(<meta\s+name="twitter:description"\s+content=")[^"]*(")/, `$1${n}$2`).replace(/(<meta\s+name="twitter:image"\s+content=")[^"]*(")/, `$1${r}$2`);
38
+ function l(e) {
39
+ return e ? String(e).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;") : "";
40
+ }
41
+ function u(e, t, n, r) {
42
+ let i = l(r), a = RegExp(`(<meta\\s+[^>]*${t}=["']${n}["'][^>]*>)`, "gi"), o = !1, s = e.replace(a, (e) => (o = !0, /\bcontent=/i.test(e) ? e.replace(/\bcontent=(["'])(.*?)\1/i, `content=$1${i}$1`) : e.replace(/(\/?>)$/, ` content="${i}"$1`)));
43
+ if (!o) {
44
+ let e = `<meta ${t}="${n}" content="${i}">`;
45
+ return s.replace(/(<\/head>)/i, `${e}\n$1`);
46
+ }
47
+ return s;
48
+ }
49
+ function d(e, t, n, r) {
50
+ let i = l(t), a = e;
51
+ return a = /<title>.*<\/title>/i.test(a) ? a.replace(/(<title>)[^<]*(<\/title>)/i, `$1${i}$2`) : a.replace(/(<\/head>)/i, `<title>${i}</title>\n$1`), a = u(a, "name", "description", n), a = u(a, "property", "og:title", t), a = u(a, "property", "og:description", n), a = u(a, "property", "og:image", r), a = u(a, "property", "og:type", "website"), a = u(a, "name", "twitter:card", "summary_large_image"), a = u(a, "name", "twitter:title", t), a = u(a, "name", "twitter:description", n), a = u(a, "name", "twitter:image", r), a;
37
52
  }
38
53
  return t({ region: a }, async (e, t) => {
39
- let a = Date.now();
54
+ let a = Date.now(), o = e.path, s = new URL(e.url, n), l = s.searchParams;
40
55
  try {
41
- let n = new URL(e.url, `https://${e.headers.host ?? "localhost"}`).pathname, o = r;
42
- for (let e of i) {
43
- let t = n.match(e.pattern);
44
- if (t) {
45
- let n = await e.resolve(t[1]);
46
- if (n) {
47
- o = n;
56
+ let n = r;
57
+ for (let t of i) {
58
+ let r = o.match(t.pattern);
59
+ if (!r) continue;
60
+ let i = r[1] ?? r[0];
61
+ try {
62
+ let a = await t.resolve(i, {
63
+ req: e,
64
+ pathname: o,
65
+ searchParams: l,
66
+ match: r
67
+ });
68
+ if (a) {
69
+ n = a;
48
70
  break;
49
71
  }
72
+ } catch (e) {
73
+ console.error(`[SSRDynamicOG] Failed to resolve "${o}${s.search}" with pattern ${t.pattern}:`, e);
50
74
  }
51
75
  }
52
- let l = c(await s(), o.title, o.description, o.image);
53
- console.log(`[SSRDynamicOG] ${n} → ${o.title} (${Date.now() - a}ms)`), t.setHeader("Content-Type", "text/html; charset=utf-8"), t.status(200).send(l);
76
+ let u = d(await c(), n.title, n.description, n.image);
77
+ console.log(`[SSRDynamicOG] ${o}${s.search} → ${n.title} (${Date.now() - a}ms)`), t.setHeader("Content-Type", "text/html; charset=utf-8"), t.status(200).send(u);
54
78
  } catch (e) {
55
79
  console.error("[SSRDynamicOG] error:", e), t.redirect(302, n);
56
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ienlab/cloud-functions-library",
3
- "version": "1.0.0-dev.10",
3
+ "version": "1.0.0-dev.12",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",