@lunora/bindings 1.0.0-alpha.39 → 1.0.0-alpha.40

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 +1 @@
1
- import{createImages as m}from"../packem_shared/createImages-CHgMZP4g.mjs";import{buildImageDeliveryUrl as g}from"../packem_shared/buildImageDeliveryUrl-Brqs-dcZ.mjs";import{buildSignedImageUrl as o,parseSignedTransform as l,verifySignedImageUrl as d}from"../packem_shared/buildSignedImageUrl-C-LlaNdt.mjs";export{g as buildImageDeliveryUrl,o as buildSignedImageUrl,m as createImages,l as parseSignedTransform,d as verifySignedImageUrl};
1
+ import{createImages as m}from"../packem_shared/createImages-CHgMZP4g.mjs";import{buildImageDeliveryUrl as g}from"../packem_shared/buildImageDeliveryUrl-Brqs-dcZ.mjs";import{buildSignedImageUrl as o,parseSignedTransform as l,verifySignedImageUrl as d}from"../packem_shared/buildSignedImageUrl-DbSDmjWK.mjs";export{g as buildImageDeliveryUrl,o as buildSignedImageUrl,m as createImages,l as parseSignedTransform,d as verifySignedImageUrl};
@@ -0,0 +1,4 @@
1
+ const v=e=>{const t=atob(e),n=new Uint8Array(t.length);for(let r=0;r<t.length;r+=1)n[r]=t.codePointAt(r)??0;return n},a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",E=e=>{let t="",n=0;const r=e.length-2;for(;n<r;n+=3){const s=e[n]<<16|e[n+1]<<8|e[n+2];t+=a.charAt(s>>18&63)+a.charAt(s>>12&63)+a.charAt(s>>6&63)+a.charAt(s&63)}const o=e.length-n;if(o===1){const s=e[n]<<16;t+=a.charAt(s>>18&63)+a.charAt(s>>12&63)}else if(o===2){const s=e[n]<<16|e[n+1]<<8;t+=a.charAt(s>>18&63)+a.charAt(s>>12&63)+a.charAt(s>>6&63)}return t},R=e=>{const t=e.replaceAll("-","+").replaceAll("_","/"),n=t+"=".repeat((4-t.length%4)%4);return v(n)},$=(e,t)=>{if(e.size<t)return;const n=e.keys().next().value;n!==void 0&&e.delete(n)},_=(e,t,n,r)=>{const o=e.get(t);if(o!==void 0)return o;$(e,r);const s=n().catch(i=>{throw e.get(t)===s&&e.delete(t),i});return e.set(t,s),s},u=new TextEncoder,x=10080*60,C=/^[a-z][a-z0-9+\-.]*:\/\//i,T=Array.from({length:32},(e,t)=>t),U=new RegExp(`[${T.map(e=>String.fromCodePoint(e)).join("")}]`,"u"),N=64,O=new Map,h=async e=>_(O,e,async()=>crypto.subtle.importKey("raw",u.encode(e),{hash:"SHA-256",name:"HMAC"},!1,["sign","verify"]),N),p=e=>{try{return new URL(e).host}catch{return e.replace(C,"").split("/")[0]??""}},y=e=>{if(U.test(e))throw new TypeError("hmac-url: value must not contain control characters (including CR/LF)")},L=/^\/+$/u,H=e=>L.test(e),I=(e,t)=>{if(!Number.isFinite(e)||e<=0)return"expiresInSeconds must be a positive finite number";if(e>t){const n=` (${String(t/86400)} days)`;return`expiresInSeconds must not exceed ${String(t)} seconds${n}`}},K=async(e,t)=>{const n=await h(e),r=await crypto.subtle.sign("HMAC",n,u.encode(t));return E(new Uint8Array(r))},P=async(e,t,n)=>{const r=await h(e);return crypto.subtle.verify("HMAC",r,n,u.encode(t))},b=/^\//,M=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},j=e=>e===void 0?"":Object.entries(e).filter(([,t])=>t!==void 0).toSorted(([t],[n])=>(t>n?1:0)-(t<n?1:0)).map(([t,n])=>`${t}=${typeof n=="object"?JSON.stringify(n):String(n)}`).join("&"),D={background:"string",blur:"number",brightness:"number",contrast:"number",draw:"json",fit:"string",flip:"string",gamma:"number",gravity:"string-or-json",height:"number",rotate:"number",saturation:"number",segment:"string",sharpen:"number",upscale:"string",width:"number"},w=new Map(Object.entries(D)),k=e=>{const t=[];for(const n of e.split("&")){const r=n.indexOf("="),o=r!==-1&&w.has(n.slice(0,r)),s=t.at(-1);o||s===void 0?t.push(n):t[t.length-1]=`${s}&${n}`}return t},z=(e,t,n)=>{if(t==="string"||t==="string-or-json"&&!n.startsWith("{"))return n;if(t==="number"){const r=Number(n);if(n===""||!Number.isFinite(r))throw new TypeError(`@lunora/bindings/images: transform key "${e}" expects a number, got "${n}"`);return r}try{return JSON.parse(n)}catch{throw new TypeError(`@lunora/bindings/images: transform key "${e}" carries malformed JSON`)}},S=(e,t,n,r)=>`${e.toLowerCase()}
2
+ ${t}
3
+ ${String(n)}
4
+ ${r}`,B=e=>e.split("/").map(t=>encodeURIComponent(t)).join("/"),F=e=>{if(e==="")return{};const t={};for(const n of k(e)){const r=n.indexOf("=");if(r===-1)throw new TypeError(`@lunora/bindings/images: malformed transform segment "${n}" — expected key=value`);const o=n.slice(0,r),s=w.get(o);if(s===void 0)throw new TypeError(`@lunora/bindings/images: unknown transform key "${o}" — the serialized transform does not match this version's TransformOptions`);t[o]=z(o,s,n.slice(r+1))}return t},Y=async e=>{const t=e.expiresInSeconds??3600,n=I(t,x);if(n!==void 0)throw new TypeError(`@lunora/bindings/images: ${n}`);let r="";try{r=new URL(e.baseUrl).pathname}catch{}if(r!==""&&!H(r))throw new TypeError(`@lunora/bindings/images: baseUrl must not carry a path ("${r}") — the key is verified from the full URL pathname, so a subpath base would make every signed URL fail verification`);const o=Math.floor(Date.now()/1e3)+t,s=p(e.baseUrl),i=j(e.transform),c=e.key.replace(b,"");y(c);const l=await K(e.secret,S(s,c,o,i)),m=M(e.baseUrl),d=B(c),g=i===""?"":`&t=${encodeURIComponent(i)}`;return`${m}/${d}?exp=${String(o)}&sig=${l}${g}`},J=async(e,t,n)=>{let r;try{r=e instanceof URL?e:new URL(e)}catch{return{reason:"malformed",valid:!1}}const o=r.searchParams.get("exp"),s=o===null?Number.NaN:Number(o),i=r.searchParams.get("sig"),c=r.searchParams.get("t")??"";if(!i||!Number.isInteger(s))return{reason:"malformed",valid:!1};if(s<Math.floor(Date.now()/1e3))return{reason:"expired",valid:!1};let l,m;try{l=r.pathname.replace(b,"").split("/").map(A=>decodeURIComponent(A)).join("/"),y(l),m=R(i)}catch{return{reason:"malformed",valid:!1}}const d=n?.expectedHost===void 0?r.host:p(n.expectedHost);if(!await P(t,S(d,l,s,c),m))return{reason:"bad_signature",valid:!1};if(c==="")return{key:l,valid:!0};let f;try{f=F(c)}catch{f=void 0}return{key:l,transform:c,transformOptions:f,valid:!0}};export{Y as buildSignedImageUrl,F as parseSignedTransform,J as verifySignedImageUrl};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/bindings",
3
- "version": "1.0.0-alpha.39",
3
+ "version": "1.0.0-alpha.40",
4
4
  "description": "Lightweight Cloudflare binding helpers for Lunora — ctx.kv, ctx.images, ctx.analytics, ctx.pipelines, ctx.vectors, ctx.r2sql — one install, per-binding subpaths",
5
5
  "keywords": [
6
6
  "analytics",
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@lunora/errors": "1.0.0-alpha.24",
68
- "@lunora/platform": "1.0.0-alpha.18"
68
+ "@lunora/platform": "1.0.0-alpha.19"
69
69
  },
70
70
  "engines": {
71
71
  "node": "^22.15.0 || >=24.11.0"
@@ -1,4 +0,0 @@
1
- const v=(e,t)=>{if(e.size<t)return;const n=e.keys().next().value;n!==void 0&&e.delete(n)},E=(e,t,n,r)=>{const s=e.get(t);if(s!==void 0)return s;v(e,r);const o=n().catch(a=>{throw e.get(t)===o&&e.delete(t),a});return e.set(t,o),o},f=new TextEncoder,$=10080*60,R=/^[a-z][a-z0-9+\-.]*:\/\//i,_=Array.from({length:32},(e,t)=>t),C=new RegExp(`[${_.map(e=>String.fromCodePoint(e)).join("")}]`,"u"),x=e=>{const t=String.fromCodePoint(...e);return btoa(t).replaceAll("+","-").replaceAll("/","_").replaceAll("=","")},A=e=>{const t=e.replaceAll("-","+").replaceAll("_","/")+"===".slice((e.length+3)%4),n=atob(t),r=new Uint8Array(n.length);for(let s=0;s<n.length;s+=1)r[s]=n.codePointAt(s)??0;return r},N=64,T=new Map,g=async e=>E(T,e,async()=>crypto.subtle.importKey("raw",f.encode(e),{hash:"SHA-256",name:"HMAC"},!1,["sign","verify"]),N),h=e=>{try{return new URL(e).host}catch{return e.replace(R,"").split("/")[0]??""}},y=e=>{if(C.test(e))throw new TypeError("hmac-url: value must not contain control characters (including CR/LF)")},U=/^\/+$/u,O=e=>U.test(e),L=(e,t)=>{if(!Number.isFinite(e)||e<=0)return"expiresInSeconds must be a positive finite number";if(e>t){const n=` (${String(t/86400)} days)`;return`expiresInSeconds must not exceed ${String(t)} seconds${n}`}},I=async(e,t)=>{const n=await g(e),r=await crypto.subtle.sign("HMAC",n,f.encode(t));return x(new Uint8Array(r))},H=async(e,t,n)=>{const r=await g(e);return crypto.subtle.verify("HMAC",r,n,f.encode(t))},p=/^\//,K=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},P=e=>e===void 0?"":Object.entries(e).filter(([,t])=>t!==void 0).toSorted(([t],[n])=>(t>n?1:0)-(t<n?1:0)).map(([t,n])=>`${t}=${typeof n=="object"?JSON.stringify(n):String(n)}`).join("&"),M={background:"string",blur:"number",brightness:"number",contrast:"number",draw:"json",fit:"string",flip:"string",gamma:"number",gravity:"string-or-json",height:"number",rotate:"number",saturation:"number",segment:"string",sharpen:"number",upscale:"string",width:"number"},b=new Map(Object.entries(M)),j=e=>{const t=[];for(const n of e.split("&")){const r=n.indexOf("="),s=r!==-1&&b.has(n.slice(0,r)),o=t.at(-1);s||o===void 0?t.push(n):t[t.length-1]=`${o}&${n}`}return t},D=(e,t,n)=>{if(t==="string"||t==="string-or-json"&&!n.startsWith("{"))return n;if(t==="number"){const r=Number(n);if(n===""||!Number.isFinite(r))throw new TypeError(`@lunora/bindings/images: transform key "${e}" expects a number, got "${n}"`);return r}try{return JSON.parse(n)}catch{throw new TypeError(`@lunora/bindings/images: transform key "${e}" carries malformed JSON`)}},w=(e,t,n,r)=>`${e.toLowerCase()}
2
- ${t}
3
- ${String(n)}
4
- ${r}`,k=e=>e.split("/").map(t=>encodeURIComponent(t)).join("/"),z=e=>{if(e==="")return{};const t={};for(const n of j(e)){const r=n.indexOf("=");if(r===-1)throw new TypeError(`@lunora/bindings/images: malformed transform segment "${n}" — expected key=value`);const s=n.slice(0,r),o=b.get(s);if(o===void 0)throw new TypeError(`@lunora/bindings/images: unknown transform key "${s}" — the serialized transform does not match this version's TransformOptions`);t[s]=D(s,o,n.slice(r+1))}return t},F=async e=>{const t=e.expiresInSeconds??3600,n=L(t,$);if(n!==void 0)throw new TypeError(`@lunora/bindings/images: ${n}`);let r="";try{r=new URL(e.baseUrl).pathname}catch{}if(r!==""&&!O(r))throw new TypeError(`@lunora/bindings/images: baseUrl must not carry a path ("${r}") — the key is verified from the full URL pathname, so a subpath base would make every signed URL fail verification`);const s=Math.floor(Date.now()/1e3)+t,o=h(e.baseUrl),a=P(e.transform),i=e.key.replace(p,"");y(i);const c=await I(e.secret,w(o,i,s,a)),l=K(e.baseUrl),m=k(i),u=a===""?"":`&t=${encodeURIComponent(a)}`;return`${l}/${m}?exp=${String(s)}&sig=${c}${u}`},Y=async(e,t,n)=>{let r;try{r=e instanceof URL?e:new URL(e)}catch{return{reason:"malformed",valid:!1}}const s=r.searchParams.get("exp"),o=s===null?Number.NaN:Number(s),a=r.searchParams.get("sig"),i=r.searchParams.get("t")??"";if(!a||!Number.isInteger(o))return{reason:"malformed",valid:!1};if(o<Math.floor(Date.now()/1e3))return{reason:"expired",valid:!1};let c,l;try{c=r.pathname.replace(p,"").split("/").map(S=>decodeURIComponent(S)).join("/"),y(c),l=A(a)}catch{return{reason:"malformed",valid:!1}}const m=n?.expectedHost===void 0?r.host:h(n.expectedHost);if(!await H(t,w(m,c,o,i),l))return{reason:"bad_signature",valid:!1};if(i==="")return{key:c,valid:!0};let d;try{d=z(i)}catch{d=void 0}return{key:c,transform:i,transformOptions:d,valid:!0}};export{F as buildSignedImageUrl,z as parseSignedTransform,Y as verifySignedImageUrl};