@stacksjs/socials 0.75.432 → 0.75.439

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.
Files changed (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // @bun
2
2
  var ve=import.meta.require;import{Buffer as x}from"buffer";class g{clientId;clientSecret;redirectUrl;parameters={};_scopes=[];scopeSeparator=",";_stateless=!1;_usesPKCE=!1;_codeVerifier=null;_state=null;user=null;constructor(e){this.clientId=e.clientId,this.clientSecret=e.clientSecret,this.redirectUrl=e.redirectUrl}async getCodeFields(e=null){let t={client_id:this.clientId,redirect_uri:this.redirectUrl,scope:this.formatScopes(this.getScopes(),this.scopeSeparator),response_type:"code"};if(this.usesState())t.state=e;return{...t,...await this.pkceFields(),...this.parameters}}formatScopes(e,t){return e.join(t)}async userFromToken(e){return{...await this.getUserByToken(e),token:e}}scopes(e){let t=Array.isArray(e)?e:[e];return this._scopes=[...new Set([...this._scopes,...t])],this}setScopes(e){let t=Array.isArray(e)?e:[e];return this._scopes=[...new Set(t)],this}getScopes(){return this._scopes}setRedirectUrl(e){if(typeof e!=="string"||e.length===0)throw Error("[socials] setRedirectUrl requires a non-empty string");let t;try{t=new URL(e)}catch{throw Error(`[socials] setRedirectUrl: invalid URL: ${e}`)}if(t.protocol!=="https:"&&t.protocol!=="http:")throw Error(`[socials] setRedirectUrl protocol must be http(s)://, got ${t.protocol}`);return this.redirectUrl=e,this}withState(e){if(typeof e!=="string"||e.length===0)throw Error("[socials] withState requires a non-empty string");return this._state=e,this}resolveState(){return this._state??this.getState()}usesState(){return!this._stateless}validateState(e,t){if(typeof e!=="string"||typeof t!=="string")return!1;if(e.length===0||t.length===0)return!1;if(e.length!==t.length)return!1;try{let{timingSafeEqual:r}=ve("crypto");return r(x.from(e,"utf8"),x.from(t,"utf8"))}catch{let r=0;for(let i=0;i<e.length;i++)r|=e.charCodeAt(i)^t.charCodeAt(i);return r===0}}isStateless(){return this._stateless}stateless(){return this._stateless=!0,this}getState(){let e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e).map((t)=>t.toString(16).padStart(2,"0")).join("")}usesPKCE(){return this._usesPKCE}enablePKCE(){return this._usesPKCE=!0,this}getCodeVerifier(){if(this._codeVerifier===null){let e=new Uint8Array(32);crypto.getRandomValues(e),this._codeVerifier=x.from(e).toString("base64url")}return this._codeVerifier}getPkceVerifier(){return this._codeVerifier}withCodeVerifier(e){if(!e)throw Error("A PKCE code verifier must be a non-empty string");return this._usesPKCE=!0,this._codeVerifier=e,this}async pkceFields(){if(!this.usesPKCE())return{};return{code_challenge:await this.getCodeChallenge(),code_challenge_method:this.getCodeChallengeMethod()}}pkceTokenFields(){if(!this.usesPKCE())return{};if(this._codeVerifier===null)throw Error("PKCE is enabled but no code verifier is known for this exchange. Pass the one from getPkceVerifier() back with withCodeVerifier().");return{code_verifier:this._codeVerifier}}async getCodeChallenge(){let t=new TextEncoder().encode(this.getCodeVerifier()),r=await crypto.subtle.digest("SHA-256",t);return x.from(new Uint8Array(r)).toString("base64url")}getCodeChallengeMethod(){return"S256"}with(e){return this.parameters=e,this}async buildAuthUrlFromBase(e,t){let r=new URLSearchParams(await this.getCodeFields(t));return`${e}?${r.toString()}`}}import{Buffer as ne}from"buffer";import{createPrivateKey as Ce,sign as Ae}from"crypto";import{config as y}from"@stacksjs/config";class Ie extends Error{constructor(e="Invalid state"){super(e);this.name="InvalidStateException"}}class l extends Error{constructor(e){super(e);this.name="ConfigException"}}import{Buffer as q}from"buffer";var se=new Map,Te=600000,xe=60000;async function re(e,t){let r=await fetch(e);if(!r.ok)throw Error(`id_token verification failed: could not fetch signing keys from ${e} (HTTP ${r.status})`);let i=await r.json(),s=Array.isArray(i.keys)?i.keys:[];return se.set(e,{keys:s,fetchedAt:t}),s}async function _e(e,t,r){let i=se.get(e),s=i!==void 0&&r-i.fetchedAt<Te,n=s?i.keys:await re(e,r),o=n.find((a)=>a.kid===t);if(!o&&s&&r-i.fetchedAt>=xe)n=await re(e,r),o=n.find((a)=>a.kid===t);if(!o)throw Error(`id_token verification failed: no signing key matches kid "${t}"`);if(o.kty!=="RSA")throw Error(`id_token verification failed: signing key "${t}" is ${o.kty}, expected RSA`);return o}function ie(e,t){try{return JSON.parse(q.from(e,"base64url").toString("utf8"))}catch{throw Error(`id_token verification failed: ${t} is not valid JSON`)}}async function _(e,t){let r=e.split(".");if(r.length!==3)throw Error("id_token verification failed: malformed token");let[i,s,n]=r,o=ie(i,"header");if(o.alg!=="RS256")throw Error(`id_token verification failed: unsupported algorithm ${JSON.stringify(o.alg)}, expected RS256`);if(typeof o.kid!=="string"||o.kid==="")throw Error("id_token verification failed: header has no kid");let a=t.now??Date.now(),c=await _e(t.jwksUrl,o.kid,a),u=await crypto.subtle.importKey("jwk",{kty:"RSA",n:c.n,e:c.e,alg:"RS256",ext:!0},{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},!1,["verify"]);if(!await crypto.subtle.verify("RSASSA-PKCS1-v1_5",u,q.from(n,"base64url"),q.from(`${i}.${s}`)))throw Error("id_token verification failed: signature does not match the issuer's key");let d=ie(s,"payload"),F=(t.clockSkewSeconds??60)*1000,te=Array.isArray(t.issuer)?t.issuer:[t.issuer];if(!te.includes(d.iss))throw Error(`id_token verification failed: issuer is ${JSON.stringify(d.iss)}, expected ${te.map((N)=>JSON.stringify(N)).join(" or ")}`);let Pe=Array.isArray(t.audience)?t.audience:[t.audience];if(!(Array.isArray(d.aud)?d.aud:[d.aud]).some((N)=>Pe.includes(N)))throw Error("id_token verification failed: token is addressed to a different client");if(typeof d.exp!=="number"||d.exp*1000+F<=a)throw Error("id_token verification failed: token has expired");if(typeof d.iat==="number"&&d.iat*1000-F>a)throw Error("id_token verification failed: token was issued in the future");if(typeof d.nbf==="number"&&d.nbf*1000-F>a)throw Error("id_token verification failed: token is not valid yet");if(t.nonce!==void 0&&d.nonce!==t.nonce)throw Error("id_token verification failed: nonce does not match");if(typeof d.sub!=="string"||d.sub==="")throw Error("id_token verification failed: token has no subject");return d}class H extends g{baseUrl="https://appleid.apple.com";teamId="";keyId="";privateKey="";native=!1;constructor(e){super(e);this.teamId=e.teamId??"",this.keyId=e.keyId??"",this.privateKey=e.privateKey??"",this.native=e.native===!0}getConfig(){let e={clientId:this.clientId||(y.services.apple?.clientId??""),teamId:this.teamId||(y.services.apple?.teamId??""),keyId:this.keyId||(y.services.apple?.keyId??""),privateKey:(this.privateKey||(y.services.apple?.privateKey??"")).replace(/\\n/g,`
3
- `),redirectUrl:this.native?"":this.redirectUrl||(y.services.apple?.redirectUrl??""),scopes:this._scopes.length>0?this._scopes:y.services.apple?.scopes??["name","email"]};return this.setScopes(e.scopes),e}async getAuthUrl(){if(this.native)throw new l("A native Apple provider redeems the app's codes; the web flow needs the Service ID");let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();this.validateConfig();let s={client_id:t,redirect_uri:r,scope:i.join(" "),state:e,response_type:"code",...this.parameters};if(i.length>0)s.response_mode="form_post";return`${this.baseUrl}/auth/authorize?${new URLSearchParams(s).toString()}`}async getAccessToken(e){let{clientId:t,redirectUrl:r}=this.getConfig();this.validateConfig();let i=await fetch(`${this.baseUrl}/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,...this.native?{}:{redirect_uri:r},client_id:t,client_secret:this.generateClientSecret()})}),s=await i.json();if(!i.ok||s.error)throw Error(`Apple OAuth error: ${s.error_description??s.error??`HTTP ${i.status}`}`);if(!s.id_token)throw Error("Apple OAuth error: token response contained no id_token");return s.id_token}async getUserByToken(e,t={}){let{clientId:r}=this.getConfig(),i;try{i=await _(e,{jwksUrl:`${this.baseUrl}/auth/keys`,issuer:this.baseUrl,audience:r,nonce:t.nonce})}catch(o){throw Error(`Apple OAuth error: ${o instanceof Error?o.message:String(o)}`)}let s=typeof i.email==="string"?i.email:null,n=null;if(i.email_verified===!0||i.email_verified==="true")n=!0;else if(i.email_verified===!1||i.email_verified==="false")n=!1;return{id:String(i.sub),nickname:null,name:"",email:s,emailVerified:n,avatar:null,token:e,raw:i}}generateClientSecret(){let{clientId:e,teamId:t,keyId:r,privateKey:i}=this.getConfig(),s=Math.floor(Date.now()/1000),n={alg:"ES256",kid:r,typ:"JWT"},o={iss:t,iat:s,exp:s+3600,aud:this.baseUrl,sub:e},a=`${this.base64urlJson(n)}.${this.base64urlJson(o)}`,c;try{c=Ce(i)}catch(p){throw new l(`Apple private key could not be parsed: ${p instanceof Error?p.message:String(p)}`)}let u=Ae("sha256",ne.from(a),{key:c,dsaEncoding:"ieee-p1363"});return`${a}.${u.toString("base64url")}`}base64urlJson(e){return ne.from(JSON.stringify(e)).toString("base64url")}validateConfig(){let{clientId:e,teamId:t,keyId:r,privateKey:i,redirectUrl:s}=this.getConfig();if(!e)throw new l(this.native?"Apple client ID (the app's bundle ID) not provided":"Apple client ID (Service ID) not provided");if(!t)throw new l("Apple team ID not provided");if(!r)throw new l("Apple key ID not provided");if(!i)throw new l("Apple private key not provided");if(!s&&!this.native)throw new l("Apple redirect URL not provided")}getTokenUrl(){return`${this.baseUrl}/auth/token`}}function Ue(e){let t=/^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(String(e||"").trim());if(!t)throw Error(`"${e}" is not a Bluesky post URI.`);return{repo:t[1],collection:t[2],rkey:t[3]}}class oe extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="BlueskyApiError"}get isAuthError(){return this.status===400||this.status===401||this.status===403}}var Re=new TextEncoder;function m(e){return Re.encode(e).length}function $e(e){let t=[],r=/https?:\/\/[^\s<>"']+/g;for(let o of e.matchAll(r)){let a=o[0].replace(/[),.;!?]+$/,"");t.push({byteStart:m(e.slice(0,o.index)),byteEnd:m(e.slice(0,o.index))+m(a),type:"link",value:a})}let i=(o,a)=>t.some((c)=>c.type==="link"&&o<c.byteEnd&&a>c.byteStart),s=/(^|\s)(#[A-Za-z0-9_]+)/g;for(let o of e.matchAll(s)){let a=o[1],c=o[2];if(a===void 0||c===void 0)continue;if(/^#\d+$/.test(c))continue;let u=(o.index??0)+a.length,p=m(e.slice(0,u)),d=p+m(c);if(i(p,d))continue;t.push({byteStart:p,byteEnd:d,type:"tag",value:c.slice(1)})}let n=/(^|\s)(@[a-z0-9][a-z0-9.-]*\.[a-z]{2,})/gi;for(let o of e.matchAll(n)){let a=o[1],c=o[2];if(a===void 0||c===void 0)continue;let u=(o.index??0)+a.length,p=m(e.slice(0,u)),d=p+m(c);if(i(p,d))continue;t.push({byteStart:p,byteEnd:d,type:"mention",value:c.slice(1).replace(/\.+$/,"")})}return t.sort((o,a)=>o.byteStart-a.byteStart)}class C{provider="bluesky";characterLimit=300;service;constructor(e={}){this.service=e.service||"https://bsky.social"}async createSession(e){let t=e.identifier.trim(),r=e.password.trim();if(!t)throw Error("Bluesky identifier is required.");if(!r)throw Error("Bluesky app password is required.");let i=await this.post("/xrpc/com.atproto.server.createSession",{identifier:t,password:r}),s=await this.getProfile({did:i.did,handle:i.handle,accessToken:i.accessJwt,refreshToken:i.refreshJwt}).catch(()=>{return});return{did:i.did,handle:i.handle,displayName:s?.displayName,accessJwt:i.accessJwt,refreshJwt:i.refreshJwt}}async refreshSession(e){if(!e)throw Error("Bluesky refresh token is required.");let t=await this.post("/xrpc/com.atproto.server.refreshSession",void 0,{authorization:`Bearer ${e}`});return{did:t.did,handle:t.handle,accessJwt:t.accessJwt,refreshJwt:t.refreshJwt}}async publish(e,t){let r=e.did||e.handle;if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(!r)throw Error("Bluesky identity DID or handle is required.");if(t.text.length>this.characterLimit)throw Error(`Bluesky posts must be ${this.characterLimit} characters or fewer.`);let i={$type:"app.bsky.feed.post",text:t.text,createdAt:t.scheduledAt||new Date().toISOString()};if(t.langs?.length)i.langs=t.langs;if(t.reply)i.reply=t.reply;let s=t.facets??await this.buildFacets(t.text);if(s.length)i.facets=s;if(t.external)i.embed={$type:"app.bsky.embed.external",external:{uri:t.external.uri,title:t.external.title,description:t.external.description||""}};let n=(t.media||[]).filter((a)=>a.bytes?.length).slice(0,4);if(n.length){let a=[];for(let c of n){let u=await this.uploadBlob(e,c.bytes,c.mimeType||"image/jpeg");a.push({image:u,alt:c.altText||""})}i.embed={$type:"app.bsky.embed.images",images:a}}let o=await this.post("/xrpc/com.atproto.repo.createRecord",{repo:r,collection:"app.bsky.feed.post",record:i},{authorization:`Bearer ${e.accessToken}`});return{provider:this.provider,uri:o.uri,cid:o.cid,url:this.toPostUrl(e.handle,o.uri)}}async postMetrics(e,t){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(t.length===0)return[];let r=new URL(`${this.service}/xrpc/app.bsky.feed.getPosts`);for(let s of t.slice(0,25))r.searchParams.append("uris",s);return((await this.request(r,{headers:{authorization:`Bearer ${e.accessToken}`}})).posts||[]).map((s)=>({uri:s.uri,likeCount:s.likeCount||0,repostCount:s.repostCount||0,replyCount:s.replyCount||0}))}async timeline(e,t={}){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");let r=new URL(`${this.service}/xrpc/app.bsky.feed.getTimeline`);if(r.searchParams.set("limit",String(Math.min(Math.max(t.limit||30,1),100))),t.cursor)r.searchParams.set("cursor",t.cursor);let i=await this.request(r,{headers:{authorization:`Bearer ${e.accessToken}`}});return{cursor:i.cursor,items:(i.feed||[]).flatMap((s)=>{let n=s.post;if(!n?.uri||!n.author?.handle)return[];return[{uri:n.uri,authorHandle:n.author.handle,authorName:n.author.displayName,authorAvatar:n.author.avatar,postUrl:this.toPostUrl(n.author.handle,n.uri),body:n.record?.text||"",postedAt:n.record?.createdAt||new Date().toISOString(),likeCount:n.likeCount||0,repostCount:n.repostCount||0,replyCount:n.replyCount||0}]})}}async getProfile(e){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");let t=e.did||e.handle;if(!t)throw Error("Bluesky identity DID or handle is required.");let r=new URL(`${this.service}/xrpc/app.bsky.actor.getProfile`);return r.searchParams.set("actor",t),await this.request(r,{headers:{authorization:`Bearer ${e.accessToken}`}})}async buildFacets(e){let t=[];for(let r of $e(e)){let i=null;if(r.type==="link")i={$type:"app.bsky.richtext.facet#link",uri:r.value};else if(r.type==="tag")i={$type:"app.bsky.richtext.facet#tag",tag:r.value};else if(r.type==="mention"){let s=await this.resolveHandle(r.value);if(s)i={$type:"app.bsky.richtext.facet#mention",did:s}}if(i)t.push({index:{byteStart:r.byteStart,byteEnd:r.byteEnd},features:[i]})}return t}async resolveHandle(e){try{let t=new URL(`${this.service}/xrpc/com.atproto.identity.resolveHandle`);return t.searchParams.set("handle",e),(await this.request(t,{})).did||null}catch{return null}}async uploadBlob(e,t,r){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(t.length>1e6)throw Error("Bluesky images must be 1MB or smaller.");return(await this.request(new URL(`${this.service}/xrpc/com.atproto.repo.uploadBlob`),{method:"POST",headers:{"content-type":r,authorization:`Bearer ${e.accessToken}`},body:new Uint8Array(t)})).blob}async listAuthoredPosts(e,t={}){let r=e.did||e.handle;if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(!r)throw Error("Bluesky identity DID or handle is required.");let i=new URL(`${this.service}/xrpc/com.atproto.repo.listRecords`);if(i.searchParams.set("repo",r),i.searchParams.set("collection","app.bsky.feed.post"),i.searchParams.set("limit",String(Math.min(Math.max(t.limit||100,1),100))),t.cursor)i.searchParams.set("cursor",t.cursor);let s=await this.request(i,{headers:{authorization:`Bearer ${e.accessToken}`}});return{cursor:s.cursor,posts:(s.records||[]).filter((n)=>n?.uri).map((n)=>({uri:n.uri,cid:n.cid,text:n.value?.text,postedAt:n.value?.createdAt,url:e.handle?this.toPostUrl(e.handle,n.uri):void 0}))}}async deletePost(e,t){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");let{repo:r,collection:i,rkey:s}=Ue(t.uri);await this.post("/xrpc/com.atproto.repo.deleteRecord",{repo:r,collection:i,rkey:s},{authorization:`Bearer ${e.accessToken}`})}async post(e,t,r={}){return await this.request(new URL(`${this.service}${e}`),{method:"POST",headers:{...t===void 0?{}:{"content-type":"application/json"},...r},...t===void 0?{}:{body:JSON.stringify(t)}})}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new oe(`Bluesky API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}toPostUrl(e,t){let r=t.split("/").pop();return`https://bsky.app/profile/${e}/post/${r}`}}import{fetcher as ae}from"@stacksjs/api";import{config as P}from"@stacksjs/config";var A="v24.0";class V extends g{baseUrl="https://www.facebook.com";apiUrl="https://graph.facebook.com";get graphVersion(){return P.services.facebook?.graphVersion??A}getConfig(){let e={clientId:this.clientId||(P.services.facebook?.clientId??""),clientSecret:this.clientSecret||(P.services.facebook?.clientSecret??""),redirectUrl:this.redirectUrl||(P.services.facebook?.redirectUrl??""),scopes:P.services.facebook?.scopes??["email","public_profile"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/${this.graphVersion}/dialog/oauth?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(","),response_type:"code",...await this.pkceFields(),...this.parameters,state:e}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=await ae.get(`${this.apiUrl}/${this.graphVersion}/oauth/access_token?${new URLSearchParams({client_id:t,client_secret:r,redirect_uri:i,code:e,...this.pkceTokenFields()}).toString()}`);if(s.data.error)throw Error(`Facebook OAuth error: ${s.data.error.message}`);return s.data.access_token}async getUserByToken(e){let t=await ae.withHeaders({Authorization:`Bearer ${e}`}).get(`${this.apiUrl}/${this.graphVersion}/me?${new URLSearchParams({fields:"id,name,email,picture"}).toString()}`);return{id:t.data.id,nickname:null,name:t.data.name,email:t.data.email??null,avatar:t.data.picture?.data.url??null,token:e,raw:t.data}}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("Facebook client ID not provided");if(!t)throw new l("Facebook client secret not provided");if(!r)throw new l("Facebook redirect URL not provided")}getTokenUrl(){return`${this.apiUrl}/${this.graphVersion}/oauth/access_token`}}import{fetcher as z}from"@stacksjs/api";import{config as U}from"@stacksjs/config";class M extends g{baseUrl="https://github.com";apiUrl="https://api.github.com";getConfig(){let e={clientId:this.clientId||(U.services.github?.clientId??""),clientSecret:this.clientSecret||(U.services.github?.clientSecret??""),redirectUrl:this.redirectUrl||(U.services.github?.redirectUrl??""),scopes:this._scopes.length>0?this._scopes:U.services.github?.scopes??["read:user","user:email"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/login/oauth/authorize?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(" "),state:e,response_type:"code",...await this.pkceFields(),...this.parameters}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=await z.post(`${this.baseUrl}/login/oauth/access_token`,{client_id:t,client_secret:r,code:e,redirect_uri:i,...this.pkceTokenFields()});if(s.data.error)throw Error(`GitHub OAuth error: ${s.data.error_description}`);return s.data.access_token}async getUserByToken(e){let[t,r]=await Promise.all([z.withHeaders({Accept:"application/vnd.github.v3+json",Authorization:`token ${e}`}).get(`${this.apiUrl}/user`),z.withHeaders({Accept:"application/vnd.github.v3+json",Authorization:`token ${e}`}).get(`${this.apiUrl}/user/emails`)]),i=this.pickEmail(r.data);return{id:t.data.id.toString(),nickname:t.data.login,name:t.data.name??t.data.login,email:i?.email??t.data.email??null,emailVerified:i?i.verified:null,avatar:t.data.avatar_url,token:e,raw:t.data}}pickEmail(e){if(!Array.isArray(e)||e.length===0)return null;let t=e.find((i)=>i.primary&&i.verified),r=e.find((i)=>i.verified);return t??r??e.find((i)=>i.primary)??e[0]??null}getEmail(e){return this.pickEmail(e)?.email??null}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("GitHub client ID not provided");if(!t)throw new l("GitHub client secret not provided");if(!r)throw new l("GitHub redirect URL not provided")}getTokenUrl(){return`${this.baseUrl}/login/oauth/access_token`}}import{fetcher as ce}from"@stacksjs/api";import{config as R}from"@stacksjs/config";class J extends g{baseUrl="https://accounts.google.com";apiUrl="https://www.googleapis.com";tokenUrl="https://oauth2.googleapis.com/token";tokenInfoUrl="https://oauth2.googleapis.com/tokeninfo";certsUrl="https://www.googleapis.com/oauth2/v3/certs";issuers=["https://accounts.google.com","accounts.google.com"];getConfig(){let e={clientId:this.clientId||(R.services.google?.clientId??""),clientSecret:this.clientSecret||(R.services.google?.clientSecret??""),redirectUrl:this.redirectUrl||(R.services.google?.redirectUrl??""),scopes:this._scopes.length>0?this._scopes:R.services.google?.scopes??["openid","email"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/o/oauth2/v2/auth?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(" "),state:e,response_type:"code",access_type:"offline",prompt:"consent",...await this.pkceFields(),...this.parameters}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=await ce.post(this.tokenUrl,{client_id:t,client_secret:r,code:e,redirect_uri:i,grant_type:"authorization_code",...this.pkceTokenFields()});if(s.data.error)throw Error(`Google OAuth error: ${s.data.error_description}`);return s.data.access_token}async getUserByToken(e){await this.assertTokenWasIssuedToUs(e);let t=await ce.withHeaders({Authorization:`Bearer ${e}`}).get(`${this.apiUrl}/oauth2/v2/userinfo`);return{id:t.data.id,nickname:t.data.given_name,name:t.data.name,email:t.data.email,emailVerified:typeof t.data.verified_email==="boolean"?t.data.verified_email:null,avatar:t.data.picture,token:e,raw:t.data}}async getUserByIdToken(e,t={}){let{clientId:r}=this.getConfig(),i;try{i=await _(e,{jwksUrl:this.certsUrl,issuer:this.issuers,audience:r,nonce:t.nonce})}catch(n){throw Error(`Google OAuth error: ${n instanceof Error?n.message:String(n)}`)}let s=typeof i.email==="string"?i.email:"";return{id:String(i.sub),nickname:typeof i.given_name==="string"?i.given_name:null,name:typeof i.name==="string"?i.name:"",email:s,emailVerified:typeof i.email_verified==="boolean"?i.email_verified:i.email_verified==="true"?!0:i.email_verified==="false"?!1:null,avatar:typeof i.picture==="string"?i.picture:null,token:e,raw:i}}async assertTokenWasIssuedToUs(e){let{clientId:t}=this.getConfig(),r=await fetch(`${this.tokenInfoUrl}?access_token=${encodeURIComponent(e)}`);if(!r.ok)throw Error(`Google OAuth error: access token was not accepted by tokeninfo (HTTP ${r.status})`);let i=await r.json();if(i.error)throw Error(`Google OAuth error: ${i.error_description??i.error}`);let s=i.aud??i.audience;if(!s)throw Error("Google OAuth error: tokeninfo did not report an audience for the access token");if(s!==t)throw Error("Google OAuth error: access token was issued to a different client")}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("Google client ID not provided");if(!t)throw new l("Google client secret not provided");if(!r)throw new l("Google redirect URL not provided")}getTokenUrl(){return this.tokenUrl}}class v extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="InstagramApiError"}get isAuthError(){return this.status===401||this.status===403||this.status===190}}class j{provider="instagram";characterLimit=2200;graphVersion;authBase;graphBase;constructor(e={}){this.graphVersion=e.graphVersion||A,this.authBase=e.authBase||"https://www.facebook.com",this.graphBase=e.graphBase||"https://graph.facebook.com"}getAuthUrl(e){let t=new URLSearchParams({client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(","),state:e.state,response_type:"code"});return`${this.authBase}/${this.graphVersion}/dialog/oauth?${t.toString()}`}async exchangeCode(e){let t=await this.graph("/oauth/access_token",{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:e.clientId,client_secret:e.clientSecret,redirect_uri:e.redirectUrl,code:e.code}).toString()});if(!t.access_token)throw new v("Facebook did not return an access token.",400,JSON.stringify(t));return{accessToken:t.access_token,expiresIn:t.expires_in}}async resolveAccount(e){let t=new URLSearchParams({fields:"name,access_token,instagram_business_account{id,username}"}),r=await this.graph(`/me/accounts?${t.toString()}`,{method:"GET"},e),i=(r.data||[]).find((n)=>n.instagram_business_account?.id),s=i?.instagram_business_account;if(!s?.id||!i?.access_token)throw new v("No Instagram Business account is linked to your Facebook Pages.",400,JSON.stringify(r));return{igUserId:s.id,username:s.username,pageAccessToken:i.access_token}}async publish(e,t){if(!e.accessToken)throw Error("Instagram access token is missing for this identity.");let r=e.did;if(!r)throw Error("Instagram account id is required to publish.");let i=t.media?.[0];if(!i?.url)throw Error("Instagram requires an image to post.");if(t.text.length>this.characterLimit)throw Error(`Instagram captions must be ${this.characterLimit} characters or fewer.`);let s=await this.graph(`/${r}/media`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({image_url:i.url,caption:t.text}).toString()},e.accessToken);if(!s.id)throw new v("Instagram did not return a media container id.",400,JSON.stringify(s));let n=await this.graph(`/${r}/media_publish`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({creation_id:s.id}).toString()},e.accessToken),o=await this.graph(`/${n.id}?fields=permalink`,{method:"GET"},e.accessToken).catch(()=>{return});return{provider:this.provider,uri:n.id,url:o?.permalink}}async timeline(e,t={}){return{items:[]}}async graph(e,t,r){let i=new Headers(t.headers);if(r)i.set("authorization",`Bearer ${r}`);let s=await fetch(`${this.graphBase}/${this.graphVersion}${e}`,{...t,headers:i}),n=await s.text(),o={};try{o=n?JSON.parse(n):{}}catch{o={}}if(!s.ok||o?.error){let a=o?.error?.message||n||s.statusText;throw new v(`Instagram API failed (${s.status}): ${a}`,s.status,n)}return o}}class f extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="LinkedInApiError"}get isAuthError(){return this.status===401||this.status===403}}class G{provider="linkedin";characterLimit=3000;apiVersion;authBase;apiBase;constructor(e={}){this.apiVersion=e.apiVersion||"202405",this.authBase=e.authBase||"https://www.linkedin.com",this.apiBase=e.apiBase||"https://api.linkedin.com"}getAuthUrl(e){let t=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(" "),state:e.state});return`${this.authBase}/oauth/v2/authorization?${t.toString()}`}async exchangeCode(e){let t=new URLSearchParams({grant_type:"authorization_code",code:e.code,redirect_uri:e.redirectUrl,client_id:e.clientId,client_secret:e.clientSecret}),r=await this.request(`${this.authBase}/oauth/v2/accessToken`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:t.toString()});if(!r.access_token)throw new f("LinkedIn did not return an access token.",400,JSON.stringify(r));return{accessToken:r.access_token,expiresIn:r.expires_in,scope:r.scope}}async getProfile(e){if(!e)throw Error("LinkedIn access token is required.");let t=await this.request(`${this.apiBase}/v2/userinfo`,{headers:{authorization:`Bearer ${e}`}});if(!t.sub)throw new f("LinkedIn profile is missing a subject id.",400,JSON.stringify(t));return{sub:t.sub,name:t.name,picture:t.picture}}async publish(e,t){if(!e.accessToken)throw Error("LinkedIn access token is missing for this identity.");let r=e.did;if(!r)throw Error("LinkedIn member URN is required to publish.");if(t.text.length>this.characterLimit)throw Error(`LinkedIn posts must be ${this.characterLimit} characters or fewer.`);let i={author:r,commentary:Oe(t.text),visibility:"PUBLIC",distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"PUBLISHED",isReshareDisabledByAuthor:!1};if(t.external)i.content={article:{source:t.external.uri,title:t.external.title,description:t.external.description||""}};let s=await fetch(`${this.apiBase}/rest/posts`,{method:"POST",headers:{authorization:`Bearer ${e.accessToken}`,"content-type":"application/json","linkedin-version":this.apiVersion,"x-restli-protocol-version":"2.0.0"},body:JSON.stringify(i)}),n=await s.text();if(!s.ok)throw new f(`LinkedIn API failed (${s.status}): ${n||s.statusText}`,s.status,n);let o=s.headers.get("x-restli-id")||s.headers.get("x-linkedin-id")||"";return{provider:this.provider,uri:o,url:o?`https://www.linkedin.com/feed/update/${o}`:void 0}}async timeline(e,t={}){return{items:[]}}async listAuthoredPosts(e,t={}){if(!e.accessToken)throw Error("LinkedIn access token is missing for this identity.");let r=e.did;if(!r)throw Error("LinkedIn member URN is required to list posts.");let i=Math.min(Math.max(t.limit||50,1),100),s=Number(t.cursor||0)||0,n=new URL(`${this.apiBase}/rest/posts`);n.searchParams.set("q","author"),n.searchParams.set("author",r),n.searchParams.set("count",String(i)),n.searchParams.set("start",String(s));let o;try{o=await this.request(n.toString(),{headers:{authorization:`Bearer ${e.accessToken}`,"linkedin-version":this.apiVersion,"x-restli-protocol-version":"2.0.0"}})}catch(c){if(c instanceof f&&(c.status===401||c.status===403))throw new f("LinkedIn will not list this account's posts - the Posts author finder needs the r_member_social permission, which this app does not hold.",c.status,c.body);throw c}let a=(o.elements||[]).filter((c)=>c?.id).map((c)=>({uri:String(c.id),text:c.commentary,postedAt:c.createdAt?new Date(c.createdAt).toISOString():void 0,url:`https://www.linkedin.com/feed/update/${c.id}`}));return{cursor:a.length===i?String(s+i):void 0,posts:a}}async deletePost(e,t){if(!e.accessToken)throw Error("LinkedIn access token is missing for this identity.");let r=String(t.uri||"").trim();if(!r)throw Error("A LinkedIn post URN is required to delete a post.");let i=await fetch(`${this.apiBase}/rest/posts/${encodeURIComponent(r)}`,{method:"DELETE",headers:{authorization:`Bearer ${e.accessToken}`,"linkedin-version":this.apiVersion,"x-restli-protocol-version":"2.0.0"}});if(!i.ok&&i.status!==404){let s=await i.text().catch(()=>"");throw new f(`LinkedIn API failed (${i.status}): ${s||i.statusText}`,i.status,s)}}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new f(`LinkedIn API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}}function Oe(e){return e.replace(/[\\|{}@[\]()<>#*_~]/g,"\\$&")}class le extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="MastodonApiError"}get isAuthError(){return this.status===401||this.status===403}}function Ee(e){let t=String(e||"").trim().replace(/\/+$/,"");if(!t)throw Error("Mastodon instance URL is required.");let r=/^https?:\/\//i.test(t)?t:`https://${t}`;try{let i=new URL(r);return`${i.protocol}//${i.host}`}catch{throw Error("Mastodon instance URL is invalid.")}}class K{provider="mastodon";characterLimit=500;instanceOf(e){return Ee(e.did||"")}tokenOf(e){if(!e.accessToken)throw Error("Mastodon access token is missing for this identity.");return e.accessToken}async verifyCredentials(e){let t=await this.request(`${this.instanceOf(e)}/api/v1/accounts/verify_credentials`,{headers:{authorization:`Bearer ${this.tokenOf(e)}`}});return{accountId:t.id,username:t.username,displayName:t.display_name||void 0,url:t.url}}async uploadMedia(e,t,r,i){let s=new FormData;if(s.set("file",new Blob([new Uint8Array(t)],{type:r||"image/jpeg"}),"upload"),i)s.set("description",i);return(await this.request(`${this.instanceOf(e)}/api/v2/media`,{method:"POST",headers:{authorization:`Bearer ${this.tokenOf(e)}`},body:s})).id}async publish(e,t){let r=this.instanceOf(e),i=this.tokenOf(e);if(t.text.length>this.characterLimit)throw Error(`Mastodon posts must be ${this.characterLimit} characters or fewer.`);let s=[];for(let a of(t.media||[]).slice(0,4)){let{bytes:c,mimeType:u}=a;if(!c?.length&&a.url){let p=await fetch(a.url);if(!p.ok)continue;c=new Uint8Array(await p.arrayBuffer()),u=u||p.headers.get("content-type")||"image/jpeg"}if(c?.length)s.push(await this.uploadMedia(e,c,u||"image/jpeg",a.altText))}let n={status:t.text,visibility:"public"};if(s.length)n.media_ids=s;if(t.reply?.parent?.uri)n.in_reply_to_id=t.reply.parent.uri;let o=await this.request(`${r}/api/v1/statuses`,{method:"POST",headers:{authorization:`Bearer ${i}`,"content-type":"application/json"},body:JSON.stringify(n)});return{provider:this.provider,uri:o.id,cid:o.id,url:o.url||o.uri}}async timeline(e,t={}){return{items:[]}}async listAuthoredPosts(e,t={}){let r=this.instanceOf(e),{accountId:i}=await this.verifyCredentials(e),s=new URL(`${r}/api/v1/accounts/${encodeURIComponent(i)}/statuses`);if(s.searchParams.set("limit",String(Math.min(Math.max(t.limit||40,1),40))),s.searchParams.set("exclude_reblogs","true"),t.cursor)s.searchParams.set("max_id",t.cursor);let o=(await this.request(s.toString(),{headers:{authorization:`Bearer ${this.tokenOf(e)}`}})||[]).filter((a)=>a?.id).map((a)=>({uri:a.id,cid:a.id,text:a.content,postedAt:a.created_at,url:a.url}));return{cursor:o.length?o[o.length-1]?.uri:void 0,posts:o}}async deletePost(e,t){let r=String(t.cid||Be(t.uri)||"").trim();if(!r)throw Error("A status id is required to delete a post.");await this.request(`${this.instanceOf(e)}/api/v1/statuses/${encodeURIComponent(r)}`,{method:"DELETE",headers:{authorization:`Bearer ${this.tokenOf(e)}`}})}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new le(`Mastodon API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}}function Be(e){return String(e||"").replace(/\/+$/,"").split("/").pop()||""}class k extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="ThreadsApiError"}get isAuthError(){return this.status===401||this.status===403||this.status===190}}class Q{provider="threads";characterLimit=500;graphVersion;authBase;graphBase;constructor(e={}){this.graphVersion=e.graphVersion||"v1.0",this.authBase=e.authBase||"https://threads.net",this.graphBase=e.graphBase||"https://graph.threads.net"}getAuthUrl(e){let t=new URLSearchParams({client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(","),response_type:"code",state:e.state});return`${this.authBase}/oauth/authorize?${t.toString()}`}async exchangeCode(e){let t=await fetch(`${this.graphBase}/oauth/access_token`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:e.clientId,client_secret:e.clientSecret,grant_type:"authorization_code",redirect_uri:e.redirectUrl,code:e.code}).toString()}),r=await t.text(),i={};try{i=r?JSON.parse(r):{}}catch{i={}}if(!t.ok||i?.error||!i?.access_token){let s=i?.error_message||i?.error?.message||r||t.statusText;throw new k(`Threads token exchange failed (${t.status}): ${s}`,t.status,r)}return{accessToken:i.access_token,userId:i.user_id!=null?String(i.user_id):void 0,expiresIn:i.expires_in}}async resolveAccount(e){let t=new URLSearchParams({fields:"id,username"}),r=await this.graph(`/me?${t.toString()}`,{method:"GET"},e);if(!r.id)throw new k("Could not resolve the Threads account for this token.",400,JSON.stringify(r));return{threadsUserId:r.id,username:r.username,accessToken:e}}async publish(e,t){if(!e.accessToken)throw Error("Threads access token is missing for this identity.");let r=e.did;if(!r)throw Error("Threads account id is required to publish.");if(t.text.length>this.characterLimit)throw Error(`Threads posts must be ${this.characterLimit} characters or fewer.`);let i=t.media?.[0],s=new URLSearchParams({text:t.text});if(i?.url)s.set("media_type","IMAGE"),s.set("image_url",i.url);else s.set("media_type","TEXT");let n=await this.graph(`/${r}/threads`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:s.toString()},e.accessToken);if(!n.id)throw new k("Threads did not return a media container id.",400,JSON.stringify(n));let o=await this.graph(`/${r}/threads_publish`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({creation_id:n.id}).toString()},e.accessToken);if(!o.id)throw new k("Threads did not return a published post id.",400,JSON.stringify(o));let a=await this.graph(`/${o.id}?fields=permalink`,{method:"GET"},e.accessToken).catch(()=>{return});return{provider:this.provider,uri:o.id,url:a?.permalink}}async timeline(e,t={}){return{items:[]}}async graph(e,t,r){let i=new Headers(t.headers);if(r)i.set("authorization",`Bearer ${r}`);let s=await fetch(`${this.graphBase}/${this.graphVersion}${e}`,{...t,headers:i}),n=await s.text(),o={};try{o=n?JSON.parse(n):{}}catch{o={}}if(!s.ok||o?.error){let a=o?.error?.message||n||s.statusText;throw new k(`Threads API failed (${s.status}): ${a}`,s.status,n)}return o}}import{Buffer as De}from"buffer";import{fetcher as de}from"@stacksjs/api";import{config as O}from"@stacksjs/config";class W extends g{baseUrl="https://twitter.com";apiUrl="https://api.twitter.com";_usesPKCE=!0;getConfig(){let e={clientId:this.clientId||(O.services.twitter?.clientId??""),clientSecret:this.clientSecret||(O.services.twitter?.clientSecret??""),redirectUrl:this.redirectUrl||(O.services.twitter?.redirectUrl??""),scopes:O.services.twitter?.scopes??["users.read","tweet.read"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/i/oauth2/authorize?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(" "),response_type:"code",...this.parameters,...await this.pkceFields(),state:e}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=this.pkceTokenFields(),n=De.from(`${t}:${r}`).toString("base64"),o=await de.withHeaders({Authorization:`Basic ${n}`,"Content-Type":"application/x-www-form-urlencoded"}).post(`${this.apiUrl}/2/oauth2/token`,{code:e,grant_type:"authorization_code",redirect_uri:i,...s});if(o.data.error)throw Error(`Twitter OAuth error: ${o.data.error_description}`);return o.data.access_token}async getUserByToken(e){let t=await de.withHeaders({Authorization:`Bearer ${e}`}).get(`${this.apiUrl}/2/users/me?user.fields=profile_image_url`);return{id:t.data.id,nickname:t.data.username,name:t.data.name,email:t.data.email??null,avatar:t.data.profile_image_url??null,token:e,raw:t.data}}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("Twitter client ID not provided");if(!t)throw new l("Twitter client secret not provided");if(!r)throw new l("Twitter redirect URL not provided")}getTokenUrl(){return`${this.apiUrl}/2/oauth2/token`}}class w extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="TwitterApiError"}get isAuthError(){return this.status===401||this.status===403}}function ue(e){let t="";for(let r of e)t+=String.fromCharCode(r);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}class b{provider="twitter";characterLimit=280;apiBase;authorizeBase;constructor(e={}){this.apiBase=e.apiBase||"https://api.twitter.com",this.authorizeBase=e.authorizeBase||"https://twitter.com"}async createAuthorization(e){let t=ue(crypto.getRandomValues(new Uint8Array(32))),r=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t)),i=ue(new Uint8Array(r)),s=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(" "),state:e.state,code_challenge:i,code_challenge_method:"S256"});return{url:`${this.authorizeBase}/i/oauth2/authorize?${s.toString()}`,codeVerifier:t}}async exchangeCode(e){return this.tokenRequest(new URLSearchParams({grant_type:"authorization_code",code:e.code,redirect_uri:e.redirectUrl,code_verifier:e.codeVerifier,client_id:e.clientId}),e.clientId,e.clientSecret)}async refreshAccessToken(e){return this.tokenRequest(new URLSearchParams({grant_type:"refresh_token",refresh_token:e.refreshToken,client_id:e.clientId}),e.clientId,e.clientSecret)}async getProfile(e){let t=await this.request(`${this.apiBase}/2/users/me?user.fields=username,name`,{headers:{authorization:`Bearer ${e}`}});if(!t.data?.id||!t.data.username)throw new w("Twitter did not return the authenticated user.",400,JSON.stringify(t));return{id:t.data.id,username:t.data.username,name:t.data.name}}async uploadMedia(e,t,r){let i=new FormData;i.set("media",new Blob([new Uint8Array(t)],{type:r||"image/jpeg"})),i.set("media_category","tweet_image");let s=await this.request(`${this.apiBase}/2/media/upload`,{method:"POST",headers:{authorization:`Bearer ${e}`},body:i}),n=s.data?.id||s.media_id_string||s.id;if(!n)throw new w("Twitter did not return a media id.",400,JSON.stringify(s));return n}async publish(e,t){if(!e.accessToken)throw Error("Twitter access token is missing for this identity.");if(t.text.length>this.characterLimit)throw Error(`Twitter posts must be ${this.characterLimit} characters or fewer.`);let r=[],i=t.media?.[0];if(i){let{bytes:a,mimeType:c}=i;if(!a?.length&&i.url){let u=await fetch(i.url);if(u.ok)a=new Uint8Array(await u.arrayBuffer()),c=c||u.headers.get("content-type")||"image/jpeg"}if(a?.length)r.push(await this.uploadMedia(e.accessToken,a,c||"image/jpeg"))}let s={text:t.text};if(r.length)s.media={media_ids:r};if(t.reply?.parent?.uri)s.reply={in_reply_to_tweet_id:t.reply.parent.uri};let n=await this.request(`${this.apiBase}/2/tweets`,{method:"POST",headers:{authorization:`Bearer ${e.accessToken}`,"content-type":"application/json"},body:JSON.stringify(s)}),o=n.data?.id;if(!o)throw new w("Twitter did not return a tweet id.",400,JSON.stringify(n));return{provider:this.provider,uri:o,cid:o,url:e.handle?`https://x.com/${e.handle}/status/${o}`:`https://x.com/i/web/status/${o}`}}async timeline(e,t={}){return{items:[]}}async listAuthoredPosts(e,t={}){if(!e.accessToken)throw Error("Twitter access token is missing for this identity.");let r=e.did;if(!r)throw Error("Twitter user id is required to list posts.");let i=new URL(`${this.apiBase}/2/users/${encodeURIComponent(r)}/tweets`);if(i.searchParams.set("max_results",String(Math.min(Math.max(t.limit||100,5),100))),i.searchParams.set("tweet.fields","created_at"),t.cursor)i.searchParams.set("pagination_token",t.cursor);let s=await this.request(i.toString(),{headers:{authorization:`Bearer ${e.accessToken}`}});return{cursor:s.meta?.next_token,posts:(s.data||[]).filter((n)=>n?.id).map((n)=>({uri:n.id,cid:n.id,text:n.text,postedAt:n.created_at,url:`https://x.com/i/web/status/${n.id}`}))}}async deletePost(e,t){if(!e.accessToken)throw Error("Twitter access token is missing for this identity.");let r=String(t.uri||"").trim();if(!r)throw Error("A tweet id is required to delete a post.");let i=await this.request(`${this.apiBase}/2/tweets/${encodeURIComponent(r)}`,{method:"DELETE",headers:{authorization:`Bearer ${e.accessToken}`}});if(i.data&&i.data.deleted===!1)throw new w(`X refused to delete tweet ${r}.`,400,JSON.stringify(i))}async tokenRequest(e,t,r){let i={"content-type":"application/x-www-form-urlencoded"};if(r)i.authorization=`Basic ${btoa(`${t}:${r}`)}`;let s=await this.request(`${this.apiBase}/2/oauth2/token`,{method:"POST",headers:i,body:e.toString()});if(!s.access_token)throw new w("Twitter did not return an access token.",400,JSON.stringify(s));return{accessToken:s.access_token,refreshToken:s.refresh_token,expiresIn:s.expires_in,scope:s.scope}}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new w(`Twitter API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}}import{config as Le}from"@stacksjs/config";var E=["clientId","clientSecret","redirectUrl"],I=Object.freeze({google:{name:"google",label:"Google",driver:J,required:E,postCallback:!1,pkce:!1,nonce:!1},github:{name:"github",label:"GitHub",driver:M,required:E,postCallback:!1,pkce:!1,nonce:!1},facebook:{name:"facebook",label:"Facebook",driver:V,required:E,postCallback:!1,pkce:!1,nonce:!1},twitter:{name:"twitter",label:"X",driver:W,required:E,postCallback:!1,pkce:!0,nonce:!1},apple:{name:"apple",label:"Apple",driver:H,required:["clientId","teamId","keyId","privateKey","redirectUrl"],postCallback:!0,pkce:!1,nonce:!0}});function pe(e){return Le?.services?.[e]}function Fe(e){return typeof e==="string"&&e in I}function he(e){if(!Fe(e))return!1;let t=pe(e);if(!t)return!1;return I[e].required.every((r)=>Boolean(t[r]))}function ir(){return Object.keys(I).filter(he).map((e)=>I[e])}function sr(e){if(!he(e))return null;let t=I[e],r=pe(e)??{};return new t.driver({clientSecret:"",...r,clientId:String(r.clientId??""),redirectUrl:String(r.redirectUrl??"")})}class Ne{accessToken;refreshToken;expiresIn;approvedScopes;constructor(e,t=null,r=null,i=[]){this.accessToken=e;this.refreshToken=t;this.expiresIn=r;this.approvedScopes=i}}function ar(e){return typeof e?.deletePost==="function"}function cr(e){return typeof e?.listAuthoredPosts==="function"}import{buildSessionHandoffCodeUrl as qe,buildSessionHandoffUrl as He}from"@stacksjs/composables";function X(e,t=[]){if(!e)return!1;if(e.startsWith("//"))return!1;if(e.startsWith("/"))return!0;try{let r=new URL(e);if(r.protocol!=="http:"&&r.protocol!=="https:")return!1;return t.includes(r.host)}catch{return!1}}function ur(e,t={}){let r=t.redirectTo??"/";if(!X(r,t.allowedHosts??[]))throw Error(`[socials] refusing to hand a session to ${r}: relative paths are always allowed; an absolute URL needs its host in allowedHosts.`);return new Response(null,{status:302,headers:{Location:He(r,e),"Cache-Control":"no-store","Referrer-Policy":"no-referrer"}})}function pr(e,t={}){let r=t.redirectTo??"/";if(!X(r,t.allowedHosts??[]))throw Error(`[socials] refusing to hand a session to ${r}: relative paths are always allowed; an absolute URL needs its host in allowedHosts.`);return new Response(null,{status:302,headers:{Location:qe(r,e),"Cache-Control":"no-store","Referrer-Policy":"no-referrer"}})}function hr(e,t={}){let r=t.redirectTo??"/login";if(!X(r,t.allowedHosts??[]))throw Error(`[socials] refusing to redirect to ${r}`);let i=r.includes("?")?"&":"?",s=`${r}${i}social_error=${encodeURIComponent(e)}`;return new Response(null,{status:302,headers:{Location:s,"Cache-Control":"no-store","Referrer-Policy":"no-referrer"}})}import{Buffer as ge}from"buffer";import{createHash as Ve,randomBytes as ze,timingSafeEqual as Me}from"crypto";function T(e){return Ve("sha256").update(e).digest("hex")}var Je=/^[a-f0-9]{64}$/,je=/^[a-f0-9]{64}$/,Ge=/^[\w.:-]{1,64}$/,Ke=3600000,fe=null;function wr(e){fe=e}function me(){return fe??Xe()}function Z(e=32){return ze(e).toString("hex")}function Qe(e,t){if(e.length!==t.length)return!1;return Me(ge.from(e,"utf8"),ge.from(t,"utf8"))}function Y(e){return`stacks_oauth_${T(e).slice(0,24)}`}async function We(){try{let{config:e}=await import("@stacksjs/config");return String(e.app?.url??"").startsWith("https:")}catch{return!1}}function we(e,t,r,i){return[`${e}=${t}`,`Path=${i.path}`,"HttpOnly",`SameSite=${i.sameSite}`,`Max-Age=${Math.max(0,Math.floor(r))}`,...i.secure?["Secure"]:[]].join("; ")}async function ye(e={}){let t=e.sameSite??"Lax",r=t==="None"?!0:e.secure??await We();return{path:e.path??"/",sameSite:t,secure:r}}async function yr(e){if(!Ge.test(e.purpose??""))throw TypeError("[socials] issueOAuthFlow needs a purpose of 1-64 word characters, dots, colons or dashes.");if(!Number.isFinite(e.ttlMs)||e.ttlMs<=0)throw TypeError("[socials] issueOAuthFlow needs a positive ttlMs.");if(e.challenge!==void 0&&!je.test(e.challenge))throw TypeError("[socials] issueOAuthFlow challenge must be the lowercase hex SHA-256 of the verifier.");if(e.userId!=null&&(!Number.isSafeInteger(e.userId)||e.userId<=0))throw TypeError("[socials] issueOAuthFlow userId must be a positive integer.");let t=Math.min(e.ttlMs,Ke),r=Z(),s=(e.challenge?!1:e.bindBrowser!==!1)?Z():null,n=e.nonce===!0?Z():typeof e.nonce==="string"&&e.nonce?e.nonce:void 0,o=new Date,a=new Date(o.getTime()+t),c=me();try{await c.prune(o)}catch{}await c.insert({stateDigest:T(r),purpose:e.purpose,browserDigest:e.challenge??(s?T(s):null),userId:e.userId??null,nonce:n??null,data:e.data??null,expiresAt:a});let u=s?we(Y(r),s,t/1000,await ye(e.cookie)):null;return{state:r,cookie:u,secret:s,...n!==void 0&&{nonce:n},expiresAt:a}}async function kr(e,t){if(typeof e!=="string"||!Je.test(e))return null;let r=me(),i=T(e),s=await r.find(i);if(!s)return null;if(s.expiresAt.getTime()<=Date.now())return await r.take(i).catch(()=>!1),null;if(s.purpose!==t.purpose)return null;if(s.browserDigest!==null){let n=t.browserSecret;if(typeof n!=="string"||n.length===0||n.length>512)return null;if(!Qe(T(n),s.browserDigest))return null}if(!await r.take(i))return null;return{purpose:s.purpose,userId:s.userId,nonce:s.nonce,data:s.data,expiresAt:s.expiresAt}}function br(e,t){if(!e||!t)return"";let r=Y(t);for(let i of e.split(";")){let s=i.trim();if(s.startsWith(`${r}=`))return s.slice(r.length+1)}return""}async function Sr(e,t={}){return we(Y(e),"",0,await ye(t))}function Pr(){let e=new Map;return{records:e,async insert(t){e.set(t.stateDigest,{...t})},async find(t){let r=e.get(t);return r?{...r}:void 0},async take(t){return e.delete(t)},async prune(t){for(let[r,i]of e)if(i.expiresAt.getTime()<=t.getTime())e.delete(r)}}}var B="oauth_flows";function Xe(){let e=()=>import("@stacksjs/database/runtime");return{async insert(t){let{db:r,sqlDateTime:i}=await e();await r.insertInto(B).values({state_digest:t.stateDigest,purpose:t.purpose,browser_digest:t.browserDigest,user_id:t.userId,nonce:t.nonce,data:t.data?JSON.stringify(t.data):null,expires_at:i(t.expiresAt),created_at:i(new Date)}).execute()},async find(t){let{db:r,parseSqlDateTime:i}=await e(),s=await r.primary.selectFrom(B).where("state_digest","=",t).selectAll().executeTakeFirst();if(!s)return;let n=null;if(typeof s.data==="string"&&s.data)try{let o=JSON.parse(s.data);n=o&&typeof o==="object"?o:null}catch{n=null}return{stateDigest:String(s.state_digest),purpose:String(s.purpose),browserDigest:typeof s.browser_digest==="string"&&s.browser_digest?s.browser_digest:null,userId:s.user_id==null?null:Number(s.user_id),nonce:typeof s.nonce==="string"&&s.nonce?s.nonce:null,data:n,expiresAt:i(s.expires_at)??new Date(0)}},async take(t){let{db:r,mutationCount:i}=await e(),s=await r.deleteFrom(B).where("state_digest","=",t).execute();return i(s)===1},async prune(t){let{db:r,sqlDateTime:i}=await e();await r.deleteFrom(B).where("expires_at","<",i(t)).execute()}}}var Ze={bluesky:["identifier","password","service"],twitter:["clientId","clientSecret","accessToken","refreshToken"],linkedin:["accessToken","memberUrn","clientId","clientSecret","apiVersion"],mastodon:["accessToken","instanceUrl"],instagram:["accessToken","accountId","clientId","clientSecret"],threads:["accessToken","userId","clientId","clientSecret"]},Ye={bluesky:["identifier","password"],twitter:["clientId","clientSecret","accessToken"],linkedin:["accessToken","memberUrn"],mastodon:["accessToken","instanceUrl"],instagram:["accessToken","accountId"],threads:["accessToken","userId"]};function et(e){return e.toUpperCase().replace(/[^A-Z0-9]/g,"")}function D(e,t,r){let i=r.replace(/[A-Z]/g,(s)=>`_${s}`).toUpperCase();return`SOCIALS_${et(e)}_${t.toUpperCase()}_${i}`}class h extends Error{constructor(e){super(e);this.name="SocialIdentityError"}}async function L(){let{config:e,overridesReady:t}=await import("@stacksjs/config");return await t,e.socials??{}}function ke(e,t){let r=Object.keys(e.identities??{});if(t){if(!r.includes(t))throw new h(`Unknown social identity '${t}'. config/socials.ts declares: ${r.length?r.join(", "):"none"}.`);return t}if(e.default){if(!r.includes(e.default))throw new h(`config/socials.ts sets default: '${e.default}', which is not one of its identities: ${r.length?r.join(", "):"none"}.`);return e.default}if(r.length===1)return r[0];if(r.length===0)throw new h("config/socials.ts declares no identities, so there is no account to post as.");throw new h(`config/socials.ts declares ${r.length} identities (${r.join(", ")}) and no default, so a call has to name one.`)}function tt(e,t,r,i=process.env){let n=(e.identities?.[r]??{}).credentials?.[t]??{},o={};for(let a of Ze[t]){let c=(n[a]??i[D(r,t,a)]??"").trim();if(c)o[a]=c}return o}function ee(e,t,r,i=process.env){let s=ke(e,r),n=tt(e,t,s,i),o=Ye[t].filter((a)=>!n[a]);if(o.length){let a=o.map((c)=>D(s,t,c)).join(", ");throw new h(`Identity '${s}' is not configured for ${t}: ${o.join(", ")} ${o.length===1?"is":"are"} missing. Set ${a}, or supply them inline in config/socials.ts.`)}return n}async function be(e){return ke(await L(),e)}async function S(e,t,r){return ee(await L(),e,t,r)}function Ir(e,t){return S("bluesky",e,t)}function Tr(e,t){return S("twitter",e,t)}function xr(e,t){return S("linkedin",e,t)}function _r(e,t){return S("mastodon",e,t)}function Cr(e,t){return S("instagram",e,t)}function Ar(e,t){return S("threads",e,t)}import{randomBytes as rt}from"crypto";function Er(e){return e==="twitter"}var it=["tweet.read","tweet.write","users.read","offline.access"],st="127.0.0.1",Se="/callback";function Br(e){if(!Number.isInteger(e)||e<1||e>65535)throw new h(`${e} is not a port a redirect can listen on.`);return`http://${st}:${e}${Se}`}function Dr(){return rt(16).toString("hex")}function Lr(e,t){let r;try{r=new URL(e)}catch{return{ok:!1,reason:"not-the-callback",message:`Not a URL: ${e}`}}if(r.pathname!==Se)return{ok:!1,reason:"not-the-callback",message:`Ignoring a request for ${r.pathname}.`};let i=r.searchParams.get("error");if(i){let o=r.searchParams.get("error_description");return{ok:!1,reason:"declined",message:o?`${i}: ${o}`:i}}let s=r.searchParams.get("state")??"";if(!t||s!==t)return{ok:!1,reason:"state-mismatch",message:"The redirect carried the wrong state, so it did not come from this run."};let n=r.searchParams.get("code")??"";if(!n)return{ok:!1,reason:"no-code",message:"The redirect carried no authorization code."};return{ok:!0,code:n}}function Fr(e,t,r){let i=[];for(let[s,n]of Object.entries(r)){if(!n)continue;i.push(`${D(e,t,s)}=${n}`)}return i}async function Nr(e){let t=new b,r=await t.exchangeCode(e);if(!r.accessToken)throw new h("X returned no access token for that code.");let i=Boolean(r.refreshToken),s=await t.getProfile(r.accessToken);return{handle:s.username,accountId:s.id,accessToken:r.accessToken,refreshToken:r.refreshToken,expiresIn:r.expiresIn,scope:r.scope,renewable:i}}async function qr(e){return new b().createAuthorization({clientId:e.clientId,redirectUrl:e.redirectUrl,scopes:[...it],state:e.state})}function Hr(e,t){let r=(i)=>i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");return`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${r(e)}</title><style>body{font:16px/1.5 system-ui,sans-serif;margin:0;display:grid;place-items:center;min-height:100vh;color:#111}main{max-width:28rem;padding:2rem;text-align:center}h1{font-size:1.25rem;margin:0 0 .5rem}p{margin:0;color:#555}@media(prefers-color-scheme:dark){body{background:#111;color:#eee}p{color:#aaa}}</style></head><body><main><h1>${r(e)}</h1><p>${r(t)}</p></main></body></html>`}var nt={bluesky:null,twitter:null,linkedin:"memberUrn",mastodon:"instanceUrl",instagram:"accountId",threads:"userId"};function ot(e,t){if(e==="bluesky")return t.service?{service:t.service}:{};if(e==="linkedin")return t.apiVersion?{apiVersion:t.apiVersion}:{};return{}}function at(e,t={}){switch(e){case"bluesky":return new C(t);case"linkedin":return new G(t);case"twitter":return new b;case"mastodon":return new K;case"instagram":return new j;case"threads":return new Q;default:throw new h(`No publishing driver for '${String(e)}'.`)}}function ct(e,t,r){let i=nt[e],s={handle:(t??"").trim()};if(r.accessToken)s.accessToken=r.accessToken;if(r.refreshToken)s.refreshToken=r.refreshToken;if(i&&r[i])s.did=r[i];return s}function lt(e,t,r){if(!r)return;if(r.includes(t))return;throw new h(`Identity '${e}' is not set up for ${t}. config/socials.ts lists: ${r.length?r.join(", "):"none"}.`)}async function dt(e,t,r){let i=await L(),s=await be(t),n=i.identities?.[s]??{};lt(s,e,n.platforms);let o=ee(i,e,s,r),a=ot(e,o);if(e==="bluesky"){let c=await new C(a).createSession({identifier:o.identifier,password:o.password});return{name:s,platform:e,identity:{handle:c.handle,did:c.did,accessToken:c.accessJwt,refreshToken:c.refreshJwt},options:a}}return{name:s,platform:e,identity:ct(e,n.handle??"",o),options:a}}async function Jr(e,t,r,i){let s=await dt(e,r,i);return at(s.platform,s.options).publish(s.identity,t)}export{st as AUTHORIZE_HOST,Se as AUTHORIZE_PATH,g as AbstractProvider,H as AppleProvider,oe as BlueskyApiError,C as BlueskyPublishingDriver,l as ConfigException,nt as DRIVER_DID_FIELD,V as FacebookProvider,M as GitHubProvider,J as GoogleProvider,v as InstagramApiError,j as InstagramPublishingDriver,Ie as InvalidStateException,f as LinkedInApiError,G as LinkedInPublishingDriver,A as META_GRAPH_VERSION,le as MastodonApiError,K as MastodonPublishingDriver,Ye as REQUIRED_SOCIAL_CREDENTIALS,Ze as SOCIAL_CREDENTIAL_FIELDS,I as SOCIAL_PROVIDERS,h as SocialIdentityError,it as TWITTER_PUBLISH_SCOPES,k as ThreadsApiError,Q as ThreadsPublishingDriver,Ne as Token,w as TwitterApiError,W as TwitterProvider,b as TwitterPublishingDriver,lt as assertPlatformDeclared,Hr as callbackPage,Br as callbackUri,Sr as clearOAuthFlowCookie,Nr as completeTwitterAuthorization,ir as configuredSocialProviders,kr as consumeOAuthFlow,Xe as databaseOAuthFlowStore,$e as detectFacetCandidates,ot as driverOptions,Fr as envLines,D as envName,et as envSegment,Oe as escapeLinkedInText,Er as isAuthorizablePlatform,X as isSafeHandoffTarget,he as isSocialProviderConfigured,Fe as isSocialProviderName,yr as issueOAuthFlow,Pr as memoryOAuthFlowStore,Dr as newState,Ee as normalizeInstance,Y as oauthFlowCookieName,T as oauthFlowDigest,Ue as parseAtUri,ee as pickCredentials,ke as pickIdentity,Jr as publishAs,at as publishingDriver,Lr as readCallback,tt as readCredentials,br as readOAuthFlowCookie,Ir as resolveBlueskyCredentials,be as resolveIdentityName,Cr as resolveInstagramCredentials,xr as resolveLinkedInCredentials,_r as resolveMastodonCredentials,S as resolveSocialCredentials,Ar as resolveThreadsCredentials,Tr as resolveTwitterCredentials,pr as socialHandoffCodeRedirect,hr as socialHandoffFailureRedirect,ur as socialHandoffRedirect,sr as socialProvider,dt as socialPublishTarget,L as socialsConfig,qr as startTwitterAuthorization,ar as supportsDeletion,cr as supportsEnumeration,ct as toDriverIdentity,wr as useOAuthFlowStore};
3
+ `),redirectUrl:this.native?"":this.redirectUrl||(y.services.apple?.redirectUrl??""),scopes:this._scopes.length>0?this._scopes:y.services.apple?.scopes??["name","email"]};return this.setScopes(e.scopes),e}async getAuthUrl(){if(this.native)throw new l("A native Apple provider redeems the app's codes; the web flow needs the Service ID");let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();this.validateConfig();let s={client_id:t,redirect_uri:r,scope:i.join(" "),state:e,response_type:"code",...this.parameters};if(i.length>0)s.response_mode="form_post";return`${this.baseUrl}/auth/authorize?${new URLSearchParams(s).toString()}`}async getAccessToken(e){let{clientId:t,redirectUrl:r}=this.getConfig();this.validateConfig();let i=await fetch(`${this.baseUrl}/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,...this.native?{}:{redirect_uri:r},client_id:t,client_secret:this.generateClientSecret()})}),s=await i.json();if(!i.ok||s.error)throw Error(`Apple OAuth error: ${s.error_description??s.error??`HTTP ${i.status}`}`);if(!s.id_token)throw Error("Apple OAuth error: token response contained no id_token");return s.id_token}async getUserByToken(e,t={}){let{clientId:r}=this.getConfig(),i;try{i=await _(e,{jwksUrl:`${this.baseUrl}/auth/keys`,issuer:this.baseUrl,audience:r,nonce:t.nonce})}catch(o){throw Error(`Apple OAuth error: ${o instanceof Error?o.message:String(o)}`)}let s=typeof i.email==="string"?i.email:null,n=null;if(i.email_verified===!0||i.email_verified==="true")n=!0;else if(i.email_verified===!1||i.email_verified==="false")n=!1;return{id:String(i.sub),nickname:null,name:"",email:s,emailVerified:n,avatar:null,token:e,raw:i}}generateClientSecret(){let{clientId:e,teamId:t,keyId:r,privateKey:i}=this.getConfig(),s=Math.floor(Date.now()/1000),n={alg:"ES256",kid:r,typ:"JWT"},o={iss:t,iat:s,exp:s+3600,aud:this.baseUrl,sub:e},a=`${this.base64urlJson(n)}.${this.base64urlJson(o)}`,c;try{c=Ce(i)}catch(p){throw new l(`Apple private key could not be parsed: ${p instanceof Error?p.message:String(p)}`)}let u=Ae("sha256",ne.from(a),{key:c,dsaEncoding:"ieee-p1363"});return`${a}.${u.toString("base64url")}`}base64urlJson(e){return ne.from(JSON.stringify(e)).toString("base64url")}validateConfig(){let{clientId:e,teamId:t,keyId:r,privateKey:i,redirectUrl:s}=this.getConfig();if(!e)throw new l(this.native?"Apple client ID (the app's bundle ID) not provided":"Apple client ID (Service ID) not provided");if(!t)throw new l("Apple team ID not provided");if(!r)throw new l("Apple key ID not provided");if(!i)throw new l("Apple private key not provided");if(!s&&!this.native)throw new l("Apple redirect URL not provided")}getTokenUrl(){return`${this.baseUrl}/auth/token`}}function Ue(e){let t=/^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(String(e||"").trim());if(!t)throw Error(`"${e}" is not a Bluesky post URI.`);return{repo:t[1],collection:t[2],rkey:t[3]}}class oe extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="BlueskyApiError"}get isAuthError(){return this.status===400||this.status===401||this.status===403}}var Re=new TextEncoder;function m(e){return Re.encode(e).length}function $e(e){let t=[],r=/https?:\/\/[^\s<>"']+/g;for(let o of e.matchAll(r)){let a=o[0].replace(/[),.;!?]+$/,"");t.push({byteStart:m(e.slice(0,o.index)),byteEnd:m(e.slice(0,o.index))+m(a),type:"link",value:a})}let i=(o,a)=>t.some((c)=>c.type==="link"&&o<c.byteEnd&&a>c.byteStart),s=/(^|\s)(#[A-Za-z0-9_]+)/g;for(let o of e.matchAll(s)){let a=o[1],c=o[2];if(a===void 0||c===void 0)continue;if(/^#\d+$/.test(c))continue;let u=(o.index??0)+a.length,p=m(e.slice(0,u)),d=p+m(c);if(i(p,d))continue;t.push({byteStart:p,byteEnd:d,type:"tag",value:c.slice(1)})}let n=/(^|\s)(@[a-z0-9][a-z0-9.-]*\.[a-z]{2,})/gi;for(let o of e.matchAll(n)){let a=o[1],c=o[2];if(a===void 0||c===void 0)continue;let u=(o.index??0)+a.length,p=m(e.slice(0,u)),d=p+m(c);if(i(p,d))continue;t.push({byteStart:p,byteEnd:d,type:"mention",value:c.slice(1).replace(/\.+$/,"")})}return t.sort((o,a)=>o.byteStart-a.byteStart)}class C{provider="bluesky";characterLimit=300;service;constructor(e={}){this.service=e.service||"https://bsky.social"}async createSession(e){let t=e.identifier.trim(),r=e.password.trim();if(!t)throw Error("Bluesky identifier is required.");if(!r)throw Error("Bluesky app password is required.");let i=await this.post("/xrpc/com.atproto.server.createSession",{identifier:t,password:r}),s=await this.getProfile({did:i.did,handle:i.handle,accessToken:i.accessJwt,refreshToken:i.refreshJwt}).catch(()=>{return});return{did:i.did,handle:i.handle,displayName:s?.displayName,accessJwt:i.accessJwt,refreshJwt:i.refreshJwt}}async refreshSession(e){if(!e)throw Error("Bluesky refresh token is required.");let t=await this.post("/xrpc/com.atproto.server.refreshSession",void 0,{authorization:`Bearer ${e}`});return{did:t.did,handle:t.handle,accessJwt:t.accessJwt,refreshJwt:t.refreshJwt}}async publish(e,t){let r=e.did||e.handle;if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(!r)throw Error("Bluesky identity DID or handle is required.");if(t.text.length>this.characterLimit)throw Error(`Bluesky posts must be ${this.characterLimit} characters or fewer.`);let i={$type:"app.bsky.feed.post",text:t.text,createdAt:t.scheduledAt||new Date().toISOString()};if(t.langs?.length)i.langs=t.langs;if(t.reply)i.reply=t.reply;let s=t.facets??await this.buildFacets(t.text);if(s.length)i.facets=s;if(t.external)i.embed={$type:"app.bsky.embed.external",external:{uri:t.external.uri,title:t.external.title,description:t.external.description||""}};let n=(t.media||[]).filter((a)=>a.bytes?.length).slice(0,4);if(n.length){let a=[];for(let c of n){let u=await this.uploadBlob(e,c.bytes,c.mimeType||"image/jpeg");a.push({image:u,alt:c.altText||""})}i.embed={$type:"app.bsky.embed.images",images:a}}let o=await this.post("/xrpc/com.atproto.repo.createRecord",{repo:r,collection:"app.bsky.feed.post",record:i},{authorization:`Bearer ${e.accessToken}`});return{provider:this.provider,uri:o.uri,cid:o.cid,url:this.toPostUrl(e.handle,o.uri)}}async postMetrics(e,t){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(t.length===0)return[];let r=new URL(`${this.service}/xrpc/app.bsky.feed.getPosts`);for(let s of t.slice(0,25))r.searchParams.append("uris",s);return((await this.request(r,{headers:{authorization:`Bearer ${e.accessToken}`}})).posts||[]).map((s)=>({uri:s.uri,likeCount:s.likeCount||0,repostCount:s.repostCount||0,replyCount:s.replyCount||0}))}async timeline(e,t={}){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");let r=new URL(`${this.service}/xrpc/app.bsky.feed.getTimeline`);if(r.searchParams.set("limit",String(Math.min(Math.max(t.limit||30,1),100))),t.cursor)r.searchParams.set("cursor",t.cursor);let i=await this.request(r,{headers:{authorization:`Bearer ${e.accessToken}`}});return{cursor:i.cursor,items:(i.feed||[]).flatMap((s)=>{let n=s.post;if(!n?.uri||!n.author?.handle)return[];return[{uri:n.uri,authorHandle:n.author.handle,authorName:n.author.displayName,authorAvatar:n.author.avatar,postUrl:this.toPostUrl(n.author.handle,n.uri),body:n.record?.text||"",postedAt:n.record?.createdAt||new Date().toISOString(),likeCount:n.likeCount||0,repostCount:n.repostCount||0,replyCount:n.replyCount||0}]})}}async getProfile(e){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");let t=e.did||e.handle;if(!t)throw Error("Bluesky identity DID or handle is required.");let r=new URL(`${this.service}/xrpc/app.bsky.actor.getProfile`);return r.searchParams.set("actor",t),await this.request(r,{headers:{authorization:`Bearer ${e.accessToken}`}})}async buildFacets(e){let t=[];for(let r of $e(e)){let i=null;if(r.type==="link")i={$type:"app.bsky.richtext.facet#link",uri:r.value};else if(r.type==="tag")i={$type:"app.bsky.richtext.facet#tag",tag:r.value};else if(r.type==="mention"){let s=await this.resolveHandle(r.value);if(s)i={$type:"app.bsky.richtext.facet#mention",did:s}}if(i)t.push({index:{byteStart:r.byteStart,byteEnd:r.byteEnd},features:[i]})}return t}async resolveHandle(e){try{let t=new URL(`${this.service}/xrpc/com.atproto.identity.resolveHandle`);return t.searchParams.set("handle",e),(await this.request(t,{})).did||null}catch{return null}}async uploadBlob(e,t,r){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(t.length>1e6)throw Error("Bluesky images must be 1MB or smaller.");return(await this.request(new URL(`${this.service}/xrpc/com.atproto.repo.uploadBlob`),{method:"POST",headers:{"content-type":r,authorization:`Bearer ${e.accessToken}`},body:new Uint8Array(t)})).blob}async listAuthoredPosts(e,t={}){let r=e.did||e.handle;if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");if(!r)throw Error("Bluesky identity DID or handle is required.");let i=new URL(`${this.service}/xrpc/com.atproto.repo.listRecords`);if(i.searchParams.set("repo",r),i.searchParams.set("collection","app.bsky.feed.post"),i.searchParams.set("limit",String(Math.min(Math.max(t.limit||100,1),100))),t.cursor)i.searchParams.set("cursor",t.cursor);let s=await this.request(i,{headers:{authorization:`Bearer ${e.accessToken}`}});return{cursor:s.cursor,posts:(s.records||[]).filter((n)=>n?.uri).map((n)=>({uri:n.uri,cid:n.cid,text:n.value?.text,postedAt:n.value?.createdAt,url:e.handle?this.toPostUrl(e.handle,n.uri):void 0}))}}async deletePost(e,t){if(!e.accessToken)throw Error("Bluesky access token is missing for this identity.");let{repo:r,collection:i,rkey:s}=Ue(t.uri);await this.post("/xrpc/com.atproto.repo.deleteRecord",{repo:r,collection:i,rkey:s},{authorization:`Bearer ${e.accessToken}`})}async post(e,t,r={}){return await this.request(new URL(`${this.service}${e}`),{method:"POST",headers:{...t===void 0?{}:{"content-type":"application/json"},...r},...t===void 0?{}:{body:JSON.stringify(t)}})}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new oe(`Bluesky API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}toPostUrl(e,t){let r=t.split("/").pop();return`https://bsky.app/profile/${e}/post/${r}`}}import{fetcher as ae}from"@stacksjs/api";import{config as P}from"@stacksjs/config";var A="v24.0";class V extends g{baseUrl="https://www.facebook.com";apiUrl="https://graph.facebook.com";get graphVersion(){return P.services.facebook?.graphVersion??A}getConfig(){let e={clientId:this.clientId||(P.services.facebook?.clientId??""),clientSecret:this.clientSecret||(P.services.facebook?.clientSecret??""),redirectUrl:this.redirectUrl||(P.services.facebook?.redirectUrl??""),scopes:P.services.facebook?.scopes??["email","public_profile"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/${this.graphVersion}/dialog/oauth?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(","),response_type:"code",...await this.pkceFields(),...this.parameters,state:e}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=await ae.get(`${this.apiUrl}/${this.graphVersion}/oauth/access_token?${new URLSearchParams({client_id:t,client_secret:r,redirect_uri:i,code:e,...this.pkceTokenFields()}).toString()}`);if(s.data.error)throw Error(`Facebook OAuth error: ${s.data.error.message}`);return s.data.access_token}async getUserByToken(e){let t=await ae.withHeaders({Authorization:`Bearer ${e}`}).get(`${this.apiUrl}/${this.graphVersion}/me?${new URLSearchParams({fields:"id,name,email,picture"}).toString()}`);return{id:t.data.id,nickname:null,name:t.data.name,email:t.data.email??null,avatar:t.data.picture?.data.url??null,token:e,raw:t.data}}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("Facebook client ID not provided");if(!t)throw new l("Facebook client secret not provided");if(!r)throw new l("Facebook redirect URL not provided")}getTokenUrl(){return`${this.apiUrl}/${this.graphVersion}/oauth/access_token`}}import{fetcher as z}from"@stacksjs/api";import{config as U}from"@stacksjs/config";class M extends g{baseUrl="https://github.com";apiUrl="https://api.github.com";getConfig(){let e={clientId:this.clientId||(U.services.github?.clientId??""),clientSecret:this.clientSecret||(U.services.github?.clientSecret??""),redirectUrl:this.redirectUrl||(U.services.github?.redirectUrl??""),scopes:this._scopes.length>0?this._scopes:U.services.github?.scopes??["read:user","user:email"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/login/oauth/authorize?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(" "),state:e,response_type:"code",...await this.pkceFields(),...this.parameters}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=await z.post(`${this.baseUrl}/login/oauth/access_token`,{client_id:t,client_secret:r,code:e,redirect_uri:i,...this.pkceTokenFields()});if(s.data.error)throw Error(`GitHub OAuth error: ${s.data.error_description}`);return s.data.access_token}async getUserByToken(e){let[t,r]=await Promise.all([z.withHeaders({Accept:"application/vnd.github.v3+json",Authorization:`token ${e}`}).get(`${this.apiUrl}/user`),z.withHeaders({Accept:"application/vnd.github.v3+json",Authorization:`token ${e}`}).get(`${this.apiUrl}/user/emails`)]),i=this.pickEmail(r.data);return{id:t.data.id.toString(),nickname:t.data.login,name:t.data.name??t.data.login,email:i?.email??t.data.email??null,emailVerified:i?i.verified:null,avatar:t.data.avatar_url,token:e,raw:t.data}}pickEmail(e){if(!Array.isArray(e)||e.length===0)return null;let t=e.find((i)=>i.primary&&i.verified),r=e.find((i)=>i.verified);return t??r??e.find((i)=>i.primary)??e[0]??null}getEmail(e){return this.pickEmail(e)?.email??null}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("GitHub client ID not provided");if(!t)throw new l("GitHub client secret not provided");if(!r)throw new l("GitHub redirect URL not provided")}getTokenUrl(){return`${this.baseUrl}/login/oauth/access_token`}}import{fetcher as ce}from"@stacksjs/api";import{config as R}from"@stacksjs/config";class J extends g{baseUrl="https://accounts.google.com";apiUrl="https://www.googleapis.com";tokenUrl="https://oauth2.googleapis.com/token";tokenInfoUrl="https://oauth2.googleapis.com/tokeninfo";certsUrl="https://www.googleapis.com/oauth2/v3/certs";issuers=["https://accounts.google.com","accounts.google.com"];getConfig(){let e={clientId:this.clientId||(R.services.google?.clientId??""),clientSecret:this.clientSecret||(R.services.google?.clientSecret??""),redirectUrl:this.redirectUrl||(R.services.google?.redirectUrl??""),scopes:this._scopes.length>0?this._scopes:R.services.google?.scopes??["openid","email"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/o/oauth2/v2/auth?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(" "),state:e,response_type:"code",access_type:"offline",prompt:"consent",...await this.pkceFields(),...this.parameters}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=await ce.post(this.tokenUrl,{client_id:t,client_secret:r,code:e,redirect_uri:i,grant_type:"authorization_code",...this.pkceTokenFields()});if(s.data.error)throw Error(`Google OAuth error: ${s.data.error_description}`);return s.data.access_token}async getUserByToken(e){await this.assertTokenWasIssuedToUs(e);let t=await ce.withHeaders({Authorization:`Bearer ${e}`}).get(`${this.apiUrl}/oauth2/v2/userinfo`);return{id:t.data.id,nickname:t.data.given_name,name:t.data.name,email:t.data.email,emailVerified:typeof t.data.verified_email==="boolean"?t.data.verified_email:null,avatar:t.data.picture,token:e,raw:t.data}}async getUserByIdToken(e,t={}){let{clientId:r}=this.getConfig(),i;try{i=await _(e,{jwksUrl:this.certsUrl,issuer:this.issuers,audience:r,nonce:t.nonce})}catch(n){throw Error(`Google OAuth error: ${n instanceof Error?n.message:String(n)}`)}let s=typeof i.email==="string"?i.email:"";return{id:String(i.sub),nickname:typeof i.given_name==="string"?i.given_name:null,name:typeof i.name==="string"?i.name:"",email:s,emailVerified:typeof i.email_verified==="boolean"?i.email_verified:i.email_verified==="true"?!0:i.email_verified==="false"?!1:null,avatar:typeof i.picture==="string"?i.picture:null,token:e,raw:i}}async assertTokenWasIssuedToUs(e){let{clientId:t}=this.getConfig(),r=await fetch(`${this.tokenInfoUrl}?access_token=${encodeURIComponent(e)}`);if(!r.ok)throw Error(`Google OAuth error: access token was not accepted by tokeninfo (HTTP ${r.status})`);let i=await r.json();if(i.error)throw Error(`Google OAuth error: ${i.error_description??i.error}`);let s=i.aud??i.audience;if(!s)throw Error("Google OAuth error: tokeninfo did not report an audience for the access token");if(s!==t)throw Error("Google OAuth error: access token was issued to a different client")}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("Google client ID not provided");if(!t)throw new l("Google client secret not provided");if(!r)throw new l("Google redirect URL not provided")}getTokenUrl(){return this.tokenUrl}}class v extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="InstagramApiError"}get isAuthError(){return this.status===401||this.status===403||this.status===190}}class j{provider="instagram";characterLimit=2200;graphVersion;authBase;graphBase;constructor(e={}){this.graphVersion=e.graphVersion||A,this.authBase=e.authBase||"https://www.facebook.com",this.graphBase=e.graphBase||"https://graph.facebook.com"}getAuthUrl(e){let t=new URLSearchParams({client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(","),state:e.state,response_type:"code"});return`${this.authBase}/${this.graphVersion}/dialog/oauth?${t.toString()}`}async exchangeCode(e){let t=await this.graph("/oauth/access_token",{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:e.clientId,client_secret:e.clientSecret,redirect_uri:e.redirectUrl,code:e.code}).toString()});if(!t.access_token)throw new v("Facebook did not return an access token.",400,JSON.stringify(t));return{accessToken:t.access_token,expiresIn:t.expires_in}}async resolveAccount(e){let t=new URLSearchParams({fields:"name,access_token,instagram_business_account{id,username}"}),r=await this.graph(`/me/accounts?${t.toString()}`,{method:"GET"},e),i=(r.data||[]).find((n)=>n.instagram_business_account?.id),s=i?.instagram_business_account;if(!s?.id||!i?.access_token)throw new v("No Instagram Business account is linked to your Facebook Pages.",400,JSON.stringify(r));return{igUserId:s.id,username:s.username,pageAccessToken:i.access_token}}async publish(e,t){if(!e.accessToken)throw Error("Instagram access token is missing for this identity.");let r=e.did;if(!r)throw Error("Instagram account id is required to publish.");let i=t.media?.[0];if(!i?.url)throw Error("Instagram requires an image to post.");if(t.text.length>this.characterLimit)throw Error(`Instagram captions must be ${this.characterLimit} characters or fewer.`);let s=await this.graph(`/${r}/media`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({image_url:i.url,caption:t.text}).toString()},e.accessToken);if(!s.id)throw new v("Instagram did not return a media container id.",400,JSON.stringify(s));let n=await this.graph(`/${r}/media_publish`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({creation_id:s.id}).toString()},e.accessToken),o=await this.graph(`/${n.id}?fields=permalink`,{method:"GET"},e.accessToken).catch(()=>{return});return{provider:this.provider,uri:n.id,url:o?.permalink}}async timeline(e,t={}){return{items:[]}}async graph(e,t,r){let i=new Headers(t.headers);if(r)i.set("authorization",`Bearer ${r}`);let s=await fetch(`${this.graphBase}/${this.graphVersion}${e}`,{...t,headers:i}),n=await s.text(),o={};try{o=n?JSON.parse(n):{}}catch{o={}}if(!s.ok||o?.error){let a=o?.error?.message||n||s.statusText;throw new v(`Instagram API failed (${s.status}): ${a}`,s.status,n)}return o}}class f extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="LinkedInApiError"}get isAuthError(){return this.status===401||this.status===403}}class G{provider="linkedin";characterLimit=3000;apiVersion;authBase;apiBase;constructor(e={}){this.apiVersion=e.apiVersion||"202405",this.authBase=e.authBase||"https://www.linkedin.com",this.apiBase=e.apiBase||"https://api.linkedin.com"}getAuthUrl(e){let t=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(" "),state:e.state});return`${this.authBase}/oauth/v2/authorization?${t.toString()}`}async exchangeCode(e){let t=new URLSearchParams({grant_type:"authorization_code",code:e.code,redirect_uri:e.redirectUrl,client_id:e.clientId,client_secret:e.clientSecret}),r=await this.request(`${this.authBase}/oauth/v2/accessToken`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:t.toString()});if(!r.access_token)throw new f("LinkedIn did not return an access token.",400,JSON.stringify(r));return{accessToken:r.access_token,expiresIn:r.expires_in,scope:r.scope}}async getProfile(e){if(!e)throw Error("LinkedIn access token is required.");let t=await this.request(`${this.apiBase}/v2/userinfo`,{headers:{authorization:`Bearer ${e}`}});if(!t.sub)throw new f("LinkedIn profile is missing a subject id.",400,JSON.stringify(t));return{sub:t.sub,name:t.name,picture:t.picture}}async publish(e,t){if(!e.accessToken)throw Error("LinkedIn access token is missing for this identity.");let r=e.did;if(!r)throw Error("LinkedIn member URN is required to publish.");if(t.text.length>this.characterLimit)throw Error(`LinkedIn posts must be ${this.characterLimit} characters or fewer.`);let i={author:r,commentary:Oe(t.text),visibility:"PUBLIC",distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"PUBLISHED",isReshareDisabledByAuthor:!1};if(t.external)i.content={article:{source:t.external.uri,title:t.external.title,description:t.external.description||""}};let s=await fetch(`${this.apiBase}/rest/posts`,{method:"POST",headers:{authorization:`Bearer ${e.accessToken}`,"content-type":"application/json","linkedin-version":this.apiVersion,"x-restli-protocol-version":"2.0.0"},body:JSON.stringify(i)}),n=await s.text();if(!s.ok)throw new f(`LinkedIn API failed (${s.status}): ${n||s.statusText}`,s.status,n);let o=s.headers.get("x-restli-id")||s.headers.get("x-linkedin-id")||"";return{provider:this.provider,uri:o,url:o?`https://www.linkedin.com/feed/update/${o}`:void 0}}async timeline(e,t={}){return{items:[]}}async listAuthoredPosts(e,t={}){if(!e.accessToken)throw Error("LinkedIn access token is missing for this identity.");let r=e.did;if(!r)throw Error("LinkedIn member URN is required to list posts.");let i=Math.min(Math.max(t.limit||50,1),100),s=Number(t.cursor||0)||0,n=new URL(`${this.apiBase}/rest/posts`);n.searchParams.set("q","author"),n.searchParams.set("author",r),n.searchParams.set("count",String(i)),n.searchParams.set("start",String(s));let o;try{o=await this.request(n.toString(),{headers:{authorization:`Bearer ${e.accessToken}`,"linkedin-version":this.apiVersion,"x-restli-protocol-version":"2.0.0"}})}catch(c){if(c instanceof f&&(c.status===401||c.status===403))throw new f("LinkedIn will not list this account's posts - the Posts author finder needs the r_member_social permission, which this app does not hold.",c.status,c.body);throw c}let a=(o.elements||[]).filter((c)=>c?.id).map((c)=>({uri:String(c.id),text:c.commentary,postedAt:c.createdAt?new Date(c.createdAt).toISOString():void 0,url:`https://www.linkedin.com/feed/update/${c.id}`}));return{cursor:a.length===i?String(s+i):void 0,posts:a}}async deletePost(e,t){if(!e.accessToken)throw Error("LinkedIn access token is missing for this identity.");let r=String(t.uri||"").trim();if(!r)throw Error("A LinkedIn post URN is required to delete a post.");let i=await fetch(`${this.apiBase}/rest/posts/${encodeURIComponent(r)}`,{method:"DELETE",headers:{authorization:`Bearer ${e.accessToken}`,"linkedin-version":this.apiVersion,"x-restli-protocol-version":"2.0.0"}});if(!i.ok&&i.status!==404){let s=await i.text().catch(()=>"");throw new f(`LinkedIn API failed (${i.status}): ${s||i.statusText}`,i.status,s)}}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new f(`LinkedIn API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}}function Oe(e){return e.replace(/[\\|{}@[\]()<>#*_~]/g,"\\$&")}class le extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="MastodonApiError"}get isAuthError(){return this.status===401||this.status===403}}function Ee(e){let t=String(e||"").trim().replace(/\/+$/,"");if(!t)throw Error("Mastodon instance URL is required.");let r=/^https?:\/\//i.test(t)?t:`https://${t}`;try{let i=new URL(r);return`${i.protocol}//${i.host}`}catch{throw Error("Mastodon instance URL is invalid.")}}class K{provider="mastodon";characterLimit=500;instanceOf(e){return Ee(e.did||"")}tokenOf(e){if(!e.accessToken)throw Error("Mastodon access token is missing for this identity.");return e.accessToken}async verifyCredentials(e){let t=await this.request(`${this.instanceOf(e)}/api/v1/accounts/verify_credentials`,{headers:{authorization:`Bearer ${this.tokenOf(e)}`}});return{accountId:t.id,username:t.username,displayName:t.display_name||void 0,url:t.url}}async uploadMedia(e,t,r,i){let s=new FormData;if(s.set("file",new Blob([new Uint8Array(t)],{type:r||"image/jpeg"}),"upload"),i)s.set("description",i);return(await this.request(`${this.instanceOf(e)}/api/v2/media`,{method:"POST",headers:{authorization:`Bearer ${this.tokenOf(e)}`},body:s})).id}async publish(e,t){let r=this.instanceOf(e),i=this.tokenOf(e);if(t.text.length>this.characterLimit)throw Error(`Mastodon posts must be ${this.characterLimit} characters or fewer.`);let s=[];for(let a of(t.media||[]).slice(0,4)){let{bytes:c,mimeType:u}=a;if(!c?.length&&a.url){let p=await fetch(a.url);if(!p.ok)continue;c=new Uint8Array(await p.arrayBuffer()),u=u||p.headers.get("content-type")||"image/jpeg"}if(c?.length)s.push(await this.uploadMedia(e,c,u||"image/jpeg",a.altText))}let n={status:t.text,visibility:"public"};if(s.length)n.media_ids=s;if(t.reply?.parent?.uri)n.in_reply_to_id=t.reply.parent.uri;let o=await this.request(`${r}/api/v1/statuses`,{method:"POST",headers:{authorization:`Bearer ${i}`,"content-type":"application/json"},body:JSON.stringify(n)});return{provider:this.provider,uri:o.id,cid:o.id,url:o.url||o.uri}}async timeline(e,t={}){return{items:[]}}async listAuthoredPosts(e,t={}){let r=this.instanceOf(e),{accountId:i}=await this.verifyCredentials(e),s=new URL(`${r}/api/v1/accounts/${encodeURIComponent(i)}/statuses`);if(s.searchParams.set("limit",String(Math.min(Math.max(t.limit||40,1),40))),s.searchParams.set("exclude_reblogs","true"),t.cursor)s.searchParams.set("max_id",t.cursor);let o=(await this.request(s.toString(),{headers:{authorization:`Bearer ${this.tokenOf(e)}`}})||[]).filter((a)=>a?.id).map((a)=>({uri:a.id,cid:a.id,text:a.content,postedAt:a.created_at,url:a.url}));return{cursor:o.length?o[o.length-1]?.uri:void 0,posts:o}}async deletePost(e,t){let r=String(t.cid||Be(t.uri)||"").trim();if(!r)throw Error("A status id is required to delete a post.");await this.request(`${this.instanceOf(e)}/api/v1/statuses/${encodeURIComponent(r)}`,{method:"DELETE",headers:{authorization:`Bearer ${this.tokenOf(e)}`}})}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new le(`Mastodon API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}}function Be(e){return String(e||"").replace(/\/+$/,"").split("/").pop()||""}class k extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="ThreadsApiError"}get isAuthError(){return this.status===401||this.status===403||this.status===190}}class Q{provider="threads";characterLimit=500;graphVersion;authBase;graphBase;constructor(e={}){this.graphVersion=e.graphVersion||"v1.0",this.authBase=e.authBase||"https://threads.net",this.graphBase=e.graphBase||"https://graph.threads.net"}getAuthUrl(e){let t=new URLSearchParams({client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(","),response_type:"code",state:e.state});return`${this.authBase}/oauth/authorize?${t.toString()}`}async exchangeCode(e){let t=await fetch(`${this.graphBase}/oauth/access_token`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({client_id:e.clientId,client_secret:e.clientSecret,grant_type:"authorization_code",redirect_uri:e.redirectUrl,code:e.code}).toString()}),r=await t.text(),i={};try{i=r?JSON.parse(r):{}}catch{i={}}if(!t.ok||i?.error||!i?.access_token){let s=i?.error_message||i?.error?.message||r||t.statusText;throw new k(`Threads token exchange failed (${t.status}): ${s}`,t.status,r)}return{accessToken:i.access_token,userId:i.user_id!=null?String(i.user_id):void 0,expiresIn:i.expires_in}}async resolveAccount(e){let t=new URLSearchParams({fields:"id,username"}),r=await this.graph(`/me?${t.toString()}`,{method:"GET"},e);if(!r.id)throw new k("Could not resolve the Threads account for this token.",400,JSON.stringify(r));return{threadsUserId:r.id,username:r.username,accessToken:e}}async publish(e,t){if(!e.accessToken)throw Error("Threads access token is missing for this identity.");let r=e.did;if(!r)throw Error("Threads account id is required to publish.");if(t.text.length>this.characterLimit)throw Error(`Threads posts must be ${this.characterLimit} characters or fewer.`);let i=t.media?.[0],s=new URLSearchParams({text:t.text});if(i?.url)s.set("media_type","IMAGE"),s.set("image_url",i.url);else s.set("media_type","TEXT");let n=await this.graph(`/${r}/threads`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:s.toString()},e.accessToken);if(!n.id)throw new k("Threads did not return a media container id.",400,JSON.stringify(n));let o=await this.graph(`/${r}/threads_publish`,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({creation_id:n.id}).toString()},e.accessToken);if(!o.id)throw new k("Threads did not return a published post id.",400,JSON.stringify(o));let a=await this.graph(`/${o.id}?fields=permalink`,{method:"GET"},e.accessToken).catch(()=>{return});return{provider:this.provider,uri:o.id,url:a?.permalink}}async timeline(e,t={}){return{items:[]}}async graph(e,t,r){let i=new Headers(t.headers);if(r)i.set("authorization",`Bearer ${r}`);let s=await fetch(`${this.graphBase}/${this.graphVersion}${e}`,{...t,headers:i}),n=await s.text(),o={};try{o=n?JSON.parse(n):{}}catch{o={}}if(!s.ok||o?.error){let a=o?.error?.message||n||s.statusText;throw new k(`Threads API failed (${s.status}): ${a}`,s.status,n)}return o}}import{Buffer as De}from"buffer";import{fetcher as de}from"@stacksjs/api";import{config as O}from"@stacksjs/config";class W extends g{baseUrl="https://twitter.com";apiUrl="https://api.twitter.com";_usesPKCE=!0;getConfig(){let e={clientId:this.clientId||(O.services.twitter?.clientId??""),clientSecret:this.clientSecret||(O.services.twitter?.clientSecret??""),redirectUrl:this.redirectUrl||(O.services.twitter?.redirectUrl??""),scopes:O.services.twitter?.scopes??["users.read","tweet.read"]};return this.setScopes(e.scopes),e}async getAuthUrl(){let e=this.resolveState(),{clientId:t,redirectUrl:r,scopes:i}=this.getConfig();return this.validateConfig(),`${this.baseUrl}/i/oauth2/authorize?${new URLSearchParams({client_id:t,redirect_uri:r,scope:i.join(" "),response_type:"code",...this.parameters,...await this.pkceFields(),state:e}).toString()}`}async getAccessToken(e){let{clientId:t,clientSecret:r,redirectUrl:i}=this.getConfig();this.validateConfig();let s=this.pkceTokenFields(),n=De.from(`${t}:${r}`).toString("base64"),o=await de.asForm().withHeaders({Authorization:`Basic ${n}`}).post(`${this.apiUrl}/2/oauth2/token`,{code:e,grant_type:"authorization_code",redirect_uri:i,...s});if(o.data.error)throw Error(`Twitter OAuth error: ${o.data.error_description}`);return o.data.access_token}async getUserByToken(e){let t=await de.withHeaders({Authorization:`Bearer ${e}`}).get(`${this.apiUrl}/2/users/me?user.fields=profile_image_url`);return{id:t.data.id,nickname:t.data.username,name:t.data.name,email:t.data.email??null,avatar:t.data.profile_image_url??null,token:e,raw:t.data}}validateConfig(){let{clientId:e,clientSecret:t,redirectUrl:r}=this.getConfig();if(!e)throw new l("Twitter client ID not provided");if(!t)throw new l("Twitter client secret not provided");if(!r)throw new l("Twitter redirect URL not provided")}getTokenUrl(){return`${this.apiUrl}/2/oauth2/token`}}class w extends Error{status;body;constructor(e,t,r){super(e);this.status=t;this.body=r;this.name="TwitterApiError"}get isAuthError(){return this.status===401||this.status===403}}function ue(e){let t="";for(let r of e)t+=String.fromCharCode(r);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}class b{provider="twitter";characterLimit=280;apiBase;authorizeBase;constructor(e={}){this.apiBase=e.apiBase||"https://api.twitter.com",this.authorizeBase=e.authorizeBase||"https://twitter.com"}async createAuthorization(e){let t=ue(crypto.getRandomValues(new Uint8Array(32))),r=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t)),i=ue(new Uint8Array(r)),s=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUrl,scope:e.scopes.join(" "),state:e.state,code_challenge:i,code_challenge_method:"S256"});return{url:`${this.authorizeBase}/i/oauth2/authorize?${s.toString()}`,codeVerifier:t}}async exchangeCode(e){return this.tokenRequest(new URLSearchParams({grant_type:"authorization_code",code:e.code,redirect_uri:e.redirectUrl,code_verifier:e.codeVerifier,client_id:e.clientId}),e.clientId,e.clientSecret)}async refreshAccessToken(e){return this.tokenRequest(new URLSearchParams({grant_type:"refresh_token",refresh_token:e.refreshToken,client_id:e.clientId}),e.clientId,e.clientSecret)}async getProfile(e){let t=await this.request(`${this.apiBase}/2/users/me?user.fields=username,name`,{headers:{authorization:`Bearer ${e}`}});if(!t.data?.id||!t.data.username)throw new w("Twitter did not return the authenticated user.",400,JSON.stringify(t));return{id:t.data.id,username:t.data.username,name:t.data.name}}async uploadMedia(e,t,r){let i=new FormData;i.set("media",new Blob([new Uint8Array(t)],{type:r||"image/jpeg"})),i.set("media_category","tweet_image");let s=await this.request(`${this.apiBase}/2/media/upload`,{method:"POST",headers:{authorization:`Bearer ${e}`},body:i}),n=s.data?.id||s.media_id_string||s.id;if(!n)throw new w("Twitter did not return a media id.",400,JSON.stringify(s));return n}async publish(e,t){if(!e.accessToken)throw Error("Twitter access token is missing for this identity.");if(t.text.length>this.characterLimit)throw Error(`Twitter posts must be ${this.characterLimit} characters or fewer.`);let r=[],i=t.media?.[0];if(i){let{bytes:a,mimeType:c}=i;if(!a?.length&&i.url){let u=await fetch(i.url);if(u.ok)a=new Uint8Array(await u.arrayBuffer()),c=c||u.headers.get("content-type")||"image/jpeg"}if(a?.length)r.push(await this.uploadMedia(e.accessToken,a,c||"image/jpeg"))}let s={text:t.text};if(r.length)s.media={media_ids:r};if(t.reply?.parent?.uri)s.reply={in_reply_to_tweet_id:t.reply.parent.uri};let n=await this.request(`${this.apiBase}/2/tweets`,{method:"POST",headers:{authorization:`Bearer ${e.accessToken}`,"content-type":"application/json"},body:JSON.stringify(s)}),o=n.data?.id;if(!o)throw new w("Twitter did not return a tweet id.",400,JSON.stringify(n));return{provider:this.provider,uri:o,cid:o,url:e.handle?`https://x.com/${e.handle}/status/${o}`:`https://x.com/i/web/status/${o}`}}async timeline(e,t={}){return{items:[]}}async listAuthoredPosts(e,t={}){if(!e.accessToken)throw Error("Twitter access token is missing for this identity.");let r=e.did;if(!r)throw Error("Twitter user id is required to list posts.");let i=new URL(`${this.apiBase}/2/users/${encodeURIComponent(r)}/tweets`);if(i.searchParams.set("max_results",String(Math.min(Math.max(t.limit||100,5),100))),i.searchParams.set("tweet.fields","created_at"),t.cursor)i.searchParams.set("pagination_token",t.cursor);let s=await this.request(i.toString(),{headers:{authorization:`Bearer ${e.accessToken}`}});return{cursor:s.meta?.next_token,posts:(s.data||[]).filter((n)=>n?.id).map((n)=>({uri:n.id,cid:n.id,text:n.text,postedAt:n.created_at,url:`https://x.com/i/web/status/${n.id}`}))}}async deletePost(e,t){if(!e.accessToken)throw Error("Twitter access token is missing for this identity.");let r=String(t.uri||"").trim();if(!r)throw Error("A tweet id is required to delete a post.");let i=await this.request(`${this.apiBase}/2/tweets/${encodeURIComponent(r)}`,{method:"DELETE",headers:{authorization:`Bearer ${e.accessToken}`}});if(i.data&&i.data.deleted===!1)throw new w(`X refused to delete tweet ${r}.`,400,JSON.stringify(i))}async tokenRequest(e,t,r){let i={"content-type":"application/x-www-form-urlencoded"};if(r)i.authorization=`Basic ${btoa(`${t}:${r}`)}`;let s=await this.request(`${this.apiBase}/2/oauth2/token`,{method:"POST",headers:i,body:e.toString()});if(!s.access_token)throw new w("Twitter did not return an access token.",400,JSON.stringify(s));return{accessToken:s.access_token,refreshToken:s.refresh_token,expiresIn:s.expires_in,scope:s.scope}}async request(e,t){let r=await fetch(e,t),i=await r.text();if(!r.ok)throw new w(`Twitter API failed (${r.status}): ${i||r.statusText}`,r.status,i);return i?JSON.parse(i):{}}}import{config as Le}from"@stacksjs/config";var E=["clientId","clientSecret","redirectUrl"],I=Object.freeze({google:{name:"google",label:"Google",driver:J,required:E,postCallback:!1,pkce:!1,nonce:!1},github:{name:"github",label:"GitHub",driver:M,required:E,postCallback:!1,pkce:!1,nonce:!1},facebook:{name:"facebook",label:"Facebook",driver:V,required:E,postCallback:!1,pkce:!1,nonce:!1},twitter:{name:"twitter",label:"X",driver:W,required:E,postCallback:!1,pkce:!0,nonce:!1},apple:{name:"apple",label:"Apple",driver:H,required:["clientId","teamId","keyId","privateKey","redirectUrl"],postCallback:!0,pkce:!1,nonce:!0}});function pe(e){return Le?.services?.[e]}function Fe(e){return typeof e==="string"&&e in I}function he(e){if(!Fe(e))return!1;let t=pe(e);if(!t)return!1;return I[e].required.every((r)=>Boolean(t[r]))}function ir(){return Object.keys(I).filter(he).map((e)=>I[e])}function sr(e){if(!he(e))return null;let t=I[e],r=pe(e)??{};return new t.driver({clientSecret:"",...r,clientId:String(r.clientId??""),redirectUrl:String(r.redirectUrl??"")})}class Ne{accessToken;refreshToken;expiresIn;approvedScopes;constructor(e,t=null,r=null,i=[]){this.accessToken=e;this.refreshToken=t;this.expiresIn=r;this.approvedScopes=i}}function ar(e){return typeof e?.deletePost==="function"}function cr(e){return typeof e?.listAuthoredPosts==="function"}import{buildSessionHandoffCodeUrl as qe,buildSessionHandoffUrl as He}from"@stacksjs/composables";function X(e,t=[]){if(!e)return!1;if(e.startsWith("//"))return!1;if(e.startsWith("/"))return!0;try{let r=new URL(e);if(r.protocol!=="http:"&&r.protocol!=="https:")return!1;return t.includes(r.host)}catch{return!1}}function ur(e,t={}){let r=t.redirectTo??"/";if(!X(r,t.allowedHosts??[]))throw Error(`[socials] refusing to hand a session to ${r}: relative paths are always allowed; an absolute URL needs its host in allowedHosts.`);return new Response(null,{status:302,headers:{Location:He(r,e),"Cache-Control":"no-store","Referrer-Policy":"no-referrer"}})}function pr(e,t={}){let r=t.redirectTo??"/";if(!X(r,t.allowedHosts??[]))throw Error(`[socials] refusing to hand a session to ${r}: relative paths are always allowed; an absolute URL needs its host in allowedHosts.`);return new Response(null,{status:302,headers:{Location:qe(r,e),"Cache-Control":"no-store","Referrer-Policy":"no-referrer"}})}function hr(e,t={}){let r=t.redirectTo??"/login";if(!X(r,t.allowedHosts??[]))throw Error(`[socials] refusing to redirect to ${r}`);let i=r.includes("?")?"&":"?",s=`${r}${i}social_error=${encodeURIComponent(e)}`;return new Response(null,{status:302,headers:{Location:s,"Cache-Control":"no-store","Referrer-Policy":"no-referrer"}})}import{Buffer as ge}from"buffer";import{createHash as Ve,randomBytes as ze,timingSafeEqual as Me}from"crypto";function T(e){return Ve("sha256").update(e).digest("hex")}var Je=/^[a-f0-9]{64}$/,je=/^[a-f0-9]{64}$/,Ge=/^[\w.:-]{1,64}$/,Ke=3600000,fe=null;function wr(e){fe=e}function me(){return fe??Xe()}function Z(e=32){return ze(e).toString("hex")}function Qe(e,t){if(e.length!==t.length)return!1;return Me(ge.from(e,"utf8"),ge.from(t,"utf8"))}function Y(e){return`stacks_oauth_${T(e).slice(0,24)}`}async function We(){try{let{config:e}=await import("@stacksjs/config");return String(e.app?.url??"").startsWith("https:")}catch{return!1}}function we(e,t,r,i){return[`${e}=${t}`,`Path=${i.path}`,"HttpOnly",`SameSite=${i.sameSite}`,`Max-Age=${Math.max(0,Math.floor(r))}`,...i.secure?["Secure"]:[]].join("; ")}async function ye(e={}){let t=e.sameSite??"Lax",r=t==="None"?!0:e.secure??await We();return{path:e.path??"/",sameSite:t,secure:r}}async function yr(e){if(!Ge.test(e.purpose??""))throw TypeError("[socials] issueOAuthFlow needs a purpose of 1-64 word characters, dots, colons or dashes.");if(!Number.isFinite(e.ttlMs)||e.ttlMs<=0)throw TypeError("[socials] issueOAuthFlow needs a positive ttlMs.");if(e.challenge!==void 0&&!je.test(e.challenge))throw TypeError("[socials] issueOAuthFlow challenge must be the lowercase hex SHA-256 of the verifier.");if(e.userId!=null&&(!Number.isSafeInteger(e.userId)||e.userId<=0))throw TypeError("[socials] issueOAuthFlow userId must be a positive integer.");let t=Math.min(e.ttlMs,Ke),r=Z(),s=(e.challenge?!1:e.bindBrowser!==!1)?Z():null,n=e.nonce===!0?Z():typeof e.nonce==="string"&&e.nonce?e.nonce:void 0,o=new Date,a=new Date(o.getTime()+t),c=me();try{await c.prune(o)}catch{}await c.insert({stateDigest:T(r),purpose:e.purpose,browserDigest:e.challenge??(s?T(s):null),userId:e.userId??null,nonce:n??null,data:e.data??null,expiresAt:a});let u=s?we(Y(r),s,t/1000,await ye(e.cookie)):null;return{state:r,cookie:u,secret:s,...n!==void 0&&{nonce:n},expiresAt:a}}async function kr(e,t){if(typeof e!=="string"||!Je.test(e))return null;let r=me(),i=T(e),s=await r.find(i);if(!s)return null;if(s.expiresAt.getTime()<=Date.now())return await r.take(i).catch(()=>!1),null;if(s.purpose!==t.purpose)return null;if(s.browserDigest!==null){let n=t.browserSecret;if(typeof n!=="string"||n.length===0||n.length>512)return null;if(!Qe(T(n),s.browserDigest))return null}if(!await r.take(i))return null;return{purpose:s.purpose,userId:s.userId,nonce:s.nonce,data:s.data,expiresAt:s.expiresAt}}function br(e,t){if(!e||!t)return"";let r=Y(t);for(let i of e.split(";")){let s=i.trim();if(s.startsWith(`${r}=`))return s.slice(r.length+1)}return""}async function Sr(e,t={}){return we(Y(e),"",0,await ye(t))}function Pr(){let e=new Map;return{records:e,async insert(t){e.set(t.stateDigest,{...t})},async find(t){let r=e.get(t);return r?{...r}:void 0},async take(t){return e.delete(t)},async prune(t){for(let[r,i]of e)if(i.expiresAt.getTime()<=t.getTime())e.delete(r)}}}var B="oauth_flows";function Xe(){let e=()=>import("@stacksjs/database/runtime");return{async insert(t){let{db:r,sqlDateTime:i}=await e();await r.insertInto(B).values({state_digest:t.stateDigest,purpose:t.purpose,browser_digest:t.browserDigest,user_id:t.userId,nonce:t.nonce,data:t.data?JSON.stringify(t.data):null,expires_at:i(t.expiresAt),created_at:i(new Date)}).execute()},async find(t){let{db:r,parseSqlDateTime:i}=await e(),s=await r.primary.selectFrom(B).where("state_digest","=",t).selectAll().executeTakeFirst();if(!s)return;let n=null;if(typeof s.data==="string"&&s.data)try{let o=JSON.parse(s.data);n=o&&typeof o==="object"?o:null}catch{n=null}return{stateDigest:String(s.state_digest),purpose:String(s.purpose),browserDigest:typeof s.browser_digest==="string"&&s.browser_digest?s.browser_digest:null,userId:s.user_id==null?null:Number(s.user_id),nonce:typeof s.nonce==="string"&&s.nonce?s.nonce:null,data:n,expiresAt:i(s.expires_at)??new Date(0)}},async take(t){let{db:r,mutationCount:i}=await e(),s=await r.deleteFrom(B).where("state_digest","=",t).execute();return i(s)===1},async prune(t){let{db:r,sqlDateTime:i}=await e();await r.deleteFrom(B).where("expires_at","<",i(t)).execute()}}}var Ze={bluesky:["identifier","password","service"],twitter:["clientId","clientSecret","accessToken","refreshToken"],linkedin:["accessToken","memberUrn","clientId","clientSecret","apiVersion"],mastodon:["accessToken","instanceUrl"],instagram:["accessToken","accountId","clientId","clientSecret"],threads:["accessToken","userId","clientId","clientSecret"]},Ye={bluesky:["identifier","password"],twitter:["clientId","clientSecret","accessToken"],linkedin:["accessToken","memberUrn"],mastodon:["accessToken","instanceUrl"],instagram:["accessToken","accountId"],threads:["accessToken","userId"]};function et(e){return e.toUpperCase().replace(/[^A-Z0-9]/g,"")}function D(e,t,r){let i=r.replace(/[A-Z]/g,(s)=>`_${s}`).toUpperCase();return`SOCIALS_${et(e)}_${t.toUpperCase()}_${i}`}class h extends Error{constructor(e){super(e);this.name="SocialIdentityError"}}async function L(){let{config:e,overridesReady:t}=await import("@stacksjs/config");return await t,e.socials??{}}function ke(e,t){let r=Object.keys(e.identities??{});if(t){if(!r.includes(t))throw new h(`Unknown social identity '${t}'. config/socials.ts declares: ${r.length?r.join(", "):"none"}.`);return t}if(e.default){if(!r.includes(e.default))throw new h(`config/socials.ts sets default: '${e.default}', which is not one of its identities: ${r.length?r.join(", "):"none"}.`);return e.default}if(r.length===1)return r[0];if(r.length===0)throw new h("config/socials.ts declares no identities, so there is no account to post as.");throw new h(`config/socials.ts declares ${r.length} identities (${r.join(", ")}) and no default, so a call has to name one.`)}function tt(e,t,r,i=process.env){let n=(e.identities?.[r]??{}).credentials?.[t]??{},o={};for(let a of Ze[t]){let c=(n[a]??i[D(r,t,a)]??"").trim();if(c)o[a]=c}return o}function ee(e,t,r,i=process.env){let s=ke(e,r),n=tt(e,t,s,i),o=Ye[t].filter((a)=>!n[a]);if(o.length){let a=o.map((c)=>D(s,t,c)).join(", ");throw new h(`Identity '${s}' is not configured for ${t}: ${o.join(", ")} ${o.length===1?"is":"are"} missing. Set ${a}, or supply them inline in config/socials.ts.`)}return n}async function be(e){return ke(await L(),e)}async function S(e,t,r){return ee(await L(),e,t,r)}function Ir(e,t){return S("bluesky",e,t)}function Tr(e,t){return S("twitter",e,t)}function xr(e,t){return S("linkedin",e,t)}function _r(e,t){return S("mastodon",e,t)}function Cr(e,t){return S("instagram",e,t)}function Ar(e,t){return S("threads",e,t)}import{randomBytes as rt}from"crypto";function Er(e){return e==="twitter"}var it=["tweet.read","tweet.write","users.read","offline.access"],st="127.0.0.1",Se="/callback";function Br(e){if(!Number.isInteger(e)||e<1||e>65535)throw new h(`${e} is not a port a redirect can listen on.`);return`http://${st}:${e}${Se}`}function Dr(){return rt(16).toString("hex")}function Lr(e,t){let r;try{r=new URL(e)}catch{return{ok:!1,reason:"not-the-callback",message:`Not a URL: ${e}`}}if(r.pathname!==Se)return{ok:!1,reason:"not-the-callback",message:`Ignoring a request for ${r.pathname}.`};let i=r.searchParams.get("error");if(i){let o=r.searchParams.get("error_description");return{ok:!1,reason:"declined",message:o?`${i}: ${o}`:i}}let s=r.searchParams.get("state")??"";if(!t||s!==t)return{ok:!1,reason:"state-mismatch",message:"The redirect carried the wrong state, so it did not come from this run."};let n=r.searchParams.get("code")??"";if(!n)return{ok:!1,reason:"no-code",message:"The redirect carried no authorization code."};return{ok:!0,code:n}}function Fr(e,t,r){let i=[];for(let[s,n]of Object.entries(r)){if(!n)continue;i.push(`${D(e,t,s)}=${n}`)}return i}async function Nr(e){let t=new b,r=await t.exchangeCode(e);if(!r.accessToken)throw new h("X returned no access token for that code.");let i=Boolean(r.refreshToken),s=await t.getProfile(r.accessToken);return{handle:s.username,accountId:s.id,accessToken:r.accessToken,refreshToken:r.refreshToken,expiresIn:r.expiresIn,scope:r.scope,renewable:i}}async function qr(e){return new b().createAuthorization({clientId:e.clientId,redirectUrl:e.redirectUrl,scopes:[...it],state:e.state})}function Hr(e,t){let r=(i)=>i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");return`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${r(e)}</title><style>body{font:16px/1.5 system-ui,sans-serif;margin:0;display:grid;place-items:center;min-height:100vh;color:#111}main{max-width:28rem;padding:2rem;text-align:center}h1{font-size:1.25rem;margin:0 0 .5rem}p{margin:0;color:#555}@media(prefers-color-scheme:dark){body{background:#111;color:#eee}p{color:#aaa}}</style></head><body><main><h1>${r(e)}</h1><p>${r(t)}</p></main></body></html>`}var nt={bluesky:null,twitter:null,linkedin:"memberUrn",mastodon:"instanceUrl",instagram:"accountId",threads:"userId"};function ot(e,t){if(e==="bluesky")return t.service?{service:t.service}:{};if(e==="linkedin")return t.apiVersion?{apiVersion:t.apiVersion}:{};return{}}function at(e,t={}){switch(e){case"bluesky":return new C(t);case"linkedin":return new G(t);case"twitter":return new b;case"mastodon":return new K;case"instagram":return new j;case"threads":return new Q;default:throw new h(`No publishing driver for '${String(e)}'.`)}}function ct(e,t,r){let i=nt[e],s={handle:(t??"").trim()};if(r.accessToken)s.accessToken=r.accessToken;if(r.refreshToken)s.refreshToken=r.refreshToken;if(i&&r[i])s.did=r[i];return s}function lt(e,t,r){if(!r)return;if(r.includes(t))return;throw new h(`Identity '${e}' is not set up for ${t}. config/socials.ts lists: ${r.length?r.join(", "):"none"}.`)}async function dt(e,t,r){let i=await L(),s=await be(t),n=i.identities?.[s]??{};lt(s,e,n.platforms);let o=ee(i,e,s,r),a=ot(e,o);if(e==="bluesky"){let c=await new C(a).createSession({identifier:o.identifier,password:o.password});return{name:s,platform:e,identity:{handle:c.handle,did:c.did,accessToken:c.accessJwt,refreshToken:c.refreshJwt},options:a}}return{name:s,platform:e,identity:ct(e,n.handle??"",o),options:a}}async function Jr(e,t,r,i){let s=await dt(e,r,i);return at(s.platform,s.options).publish(s.identity,t)}export{st as AUTHORIZE_HOST,Se as AUTHORIZE_PATH,g as AbstractProvider,H as AppleProvider,oe as BlueskyApiError,C as BlueskyPublishingDriver,l as ConfigException,nt as DRIVER_DID_FIELD,V as FacebookProvider,M as GitHubProvider,J as GoogleProvider,v as InstagramApiError,j as InstagramPublishingDriver,Ie as InvalidStateException,f as LinkedInApiError,G as LinkedInPublishingDriver,A as META_GRAPH_VERSION,le as MastodonApiError,K as MastodonPublishingDriver,Ye as REQUIRED_SOCIAL_CREDENTIALS,Ze as SOCIAL_CREDENTIAL_FIELDS,I as SOCIAL_PROVIDERS,h as SocialIdentityError,it as TWITTER_PUBLISH_SCOPES,k as ThreadsApiError,Q as ThreadsPublishingDriver,Ne as Token,w as TwitterApiError,W as TwitterProvider,b as TwitterPublishingDriver,lt as assertPlatformDeclared,Hr as callbackPage,Br as callbackUri,Sr as clearOAuthFlowCookie,Nr as completeTwitterAuthorization,ir as configuredSocialProviders,kr as consumeOAuthFlow,Xe as databaseOAuthFlowStore,$e as detectFacetCandidates,ot as driverOptions,Fr as envLines,D as envName,et as envSegment,Oe as escapeLinkedInText,Er as isAuthorizablePlatform,X as isSafeHandoffTarget,he as isSocialProviderConfigured,Fe as isSocialProviderName,yr as issueOAuthFlow,Pr as memoryOAuthFlowStore,Dr as newState,Ee as normalizeInstance,Y as oauthFlowCookieName,T as oauthFlowDigest,Ue as parseAtUri,ee as pickCredentials,ke as pickIdentity,Jr as publishAs,at as publishingDriver,Lr as readCallback,tt as readCredentials,br as readOAuthFlowCookie,Ir as resolveBlueskyCredentials,be as resolveIdentityName,Cr as resolveInstagramCredentials,xr as resolveLinkedInCredentials,_r as resolveMastodonCredentials,S as resolveSocialCredentials,Ar as resolveThreadsCredentials,Tr as resolveTwitterCredentials,pr as socialHandoffCodeRedirect,hr as socialHandoffFailureRedirect,ur as socialHandoffRedirect,sr as socialProvider,dt as socialPublishTarget,L as socialsConfig,qr as startTwitterAuthorization,ar as supportsDeletion,cr as supportsEnumeration,ct as toDriverIdentity,wr as useOAuthFlowStore};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/socials",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.432",
5
+ "version": "0.75.439",
6
6
  "description": "A simple and elegant social authentication package for Stacks.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -57,14 +57,14 @@
57
57
  "prepublishOnly": "bun run build"
58
58
  },
59
59
  "dependencies": {
60
- "@stacksjs/api": "0.75.432",
61
- "@stacksjs/composables": "0.75.432",
62
- "@stacksjs/config": "0.75.432",
63
- "@stacksjs/database": "0.75.432"
60
+ "@stacksjs/api": "0.75.439",
61
+ "@stacksjs/composables": "0.75.439",
62
+ "@stacksjs/config": "0.75.439",
63
+ "@stacksjs/database": "0.75.439"
64
64
  },
65
65
  "devDependencies": {
66
- "@stacksjs/error-handling": "0.75.432",
67
- "@stacksjs/router": "0.75.432",
66
+ "@stacksjs/error-handling": "0.75.439",
67
+ "@stacksjs/router": "0.75.439",
68
68
  "better-dx": "^0.2.24"
69
69
  }
70
70
  }