@lunora/bindings 1.0.0-alpha.34 → 1.0.0-alpha.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/images/index.mjs
CHANGED
|
@@ -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-
|
|
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};
|
|
@@ -0,0 +1,4 @@
|
|
|
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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/bindings",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.36",
|
|
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.22",
|
|
68
|
-
"@lunora/platform": "1.0.0-alpha.
|
|
68
|
+
"@lunora/platform": "1.0.0-alpha.17"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
const E=(e,t)=>{if(e.size<t)return;const n=e.keys().next().value;n!==void 0&&e.delete(n)},f=new TextEncoder,g=10080*60,x=/^[a-z][a-z0-9+\-.]*:\/\//i,C=Array.from({length:32},(e,t)=>t),R=new RegExp(`[${C.map(e=>String.fromCodePoint(e)).join("")}]`,"u"),U=e=>{const t=String.fromCodePoint(...e);return btoa(t).replaceAll("+","-").replaceAll("/","_").replaceAll("=","")},_=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},$=64,u=new Map,y=async e=>{const t=u.get(e);if(t)return t;E(u,$);const n=crypto.subtle.importKey("raw",f.encode(e),{hash:"SHA-256",name:"HMAC"},!1,["sign","verify"]);return u.set(e,n),n},p=e=>{try{return new URL(e).host}catch{return e.replace(x,"").split("/")[0]??""}},h=e=>{if(R.test(e))throw new TypeError("hmac-url: value must not contain control characters (including CR/LF)")},A=async(e,t)=>{const n=await y(e),r=await crypto.subtle.sign("HMAC",n,f.encode(t));return U(new Uint8Array(r))},N=async(e,t,n)=>{const r=await y(e);return crypto.subtle.verify("HMAC",r,n,f.encode(t))},b=/^\//,T=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("&"),O={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(O)),I=e=>{const t=[];for(const n of e.split("&")){const r=n.indexOf("="),s=r!==-1&&w.has(n.slice(0,r)),o=t.at(-1);s||o===void 0?t.push(n):t[t.length-1]=`${o}&${n}`}return t},K=(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}`,L=e=>e.split("/").map(t=>encodeURIComponent(t)).join("/"),H=e=>{if(e==="")return{};const t={};for(const n of I(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=w.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]=K(s,o,n.slice(r+1))}return t},M=async e=>{const t=e.expiresInSeconds??3600;if(!Number.isFinite(t)||t<=0)throw new TypeError("@lunora/bindings/images: expiresInSeconds must be a positive finite number");if(t>g)throw new TypeError(`@lunora/bindings/images: expiresInSeconds must not exceed ${String(g)} (7 days)`);let n="";try{n=new URL(e.baseUrl).pathname}catch{}if(n!==""&&n!=="/")throw new TypeError(`@lunora/bindings/images: baseUrl must not carry a path ("${n}") — the key is verified from the full URL pathname, so a subpath base would make every signed URL fail verification`);const r=Math.floor(Date.now()/1e3)+t,s=p(e.baseUrl),o=T(e.transform),i=e.key.replace(b,"");h(i);const c=await A(e.secret,S(s,i,r,o)),a=e.baseUrl.endsWith("/")?e.baseUrl.slice(0,-1):e.baseUrl,l=L(i),m=o===""?"":`&t=${encodeURIComponent(o)}`;return`${a}/${l}?exp=${String(r)}&sig=${c}${m}`},j=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),i=r.searchParams.get("sig"),c=r.searchParams.get("t")??"";if(!i||!Number.isInteger(o))return{reason:"malformed",valid:!1};if(o<Math.floor(Date.now()/1e3))return{reason:"expired",valid:!1};let a,l;try{a=r.pathname.replace(b,"").split("/").map(v=>decodeURIComponent(v)).join("/"),h(a),l=_(i)}catch{return{reason:"malformed",valid:!1}}const m=n?.expectedHost===void 0?r.host:p(n.expectedHost);if(!await N(t,S(m,a,o,c),l))return{reason:"bad_signature",valid:!1};if(c==="")return{key:a,valid:!0};let d;try{d=H(c)}catch{d=void 0}return{key:a,transform:c,transformOptions:d,valid:!0}};export{M as buildSignedImageUrl,H as parseSignedTransform,j as verifySignedImageUrl};
|