A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

at trunk 2.3 kB View raw
1/** 2 * Recognise which embed provider a URL belongs to (the embed content model). 3 * 4 * Dependency-free (no `@wordpress/*`) so both the reader path and the editor can 5 * import it. Hosts are matched at a dot boundary — `host === domain || 6 * host.endsWith( '.' + domain )` — so look-alikes (`notbsky.app`, 7 * `bsky.app.evil.com`) do NOT match. Mirrors the `detectProvider` pattern 8 * (Decision 0017). A URL we don't recognise returns null → it stays a plain link. 9 */ 10export type EmbedKind = 'atproto' | 'youtube' | 'vimeo'; 11 12export interface EmbedMatch { 13 kind: EmbedKind; 14 /** atproto: "<authority>/<rkey>" (authority = handle or did). video: the id. */ 15 id: string; 16} 17 18/** AppView web hosts that use the standard `/profile/<id>/post/<rkey>` scheme. */ 19const ATPROTO_HOSTS = [ 'bsky.app', 'mu.social' ]; 20 21function hostMatches( host: string, domain: string ): boolean { 22 const h = host.toLowerCase(); 23 return h === domain || h.endsWith( '.' + domain ); 24} 25 26/** 27 * Detect whether a URL points at a supported embed provider. 28 * 29 * @param url - The candidate URL (an `https://` AppView/video link or a raw `at://` URI). 30 * @returns The matched embed (`kind` + `id`), or `null` when the URL is unrelated or malformed. 31 */ 32export function detectEmbed( url: string ): EmbedMatch | null { 33 // Raw at:// post URI. 34 const at = url.match( /^at:\/\/([^/]+)\/app\.bsky\.feed\.post\/([^/?#]+)/ ); 35 if ( at ) { 36 return { kind: 'atproto', id: `${ at[ 1 ] }/${ at[ 2 ] }` }; 37 } 38 39 let parsed: URL; 40 try { 41 parsed = new URL( url ); 42 } catch { 43 return null; 44 } 45 const host = parsed.hostname; 46 47 if ( ATPROTO_HOSTS.some( ( d ) => hostMatches( host, d ) ) ) { 48 const m = parsed.pathname.match( /^\/profile\/([^/]+)\/post\/([^/?#]+)/ ); 49 return m ? { kind: 'atproto', id: `${ m[ 1 ] }/${ m[ 2 ] }` } : null; 50 } 51 52 if ( hostMatches( host, 'youtube.com' ) ) { 53 const v = parsed.searchParams.get( 'v' ); 54 return v ? { kind: 'youtube', id: v } : null; 55 } 56 if ( hostMatches( host, 'youtu.be' ) ) { 57 const id = parsed.pathname.slice( 1 ).split( '/' )[ 0 ]; 58 return id ? { kind: 'youtube', id } : null; 59 } 60 if ( hostMatches( host, 'vimeo.com' ) ) { 61 const id = parsed.pathname.split( '/' ).filter( Boolean )[ 0 ]; 62 return id && /^\d+$/.test( id ) ? { kind: 'vimeo', id } : null; 63 } 64 65 return null; 66}