1+ import { deflateSync , inflateSync } from 'fflate'
12import { compressToEncodedURIComponent , decompressFromEncodedURIComponent } from 'lz-string'
23
34export interface FrameworkTarget {
@@ -14,19 +15,63 @@ export interface LocationLike {
1415export type TestPageViewMode = 'lab' | 'preview'
1516
1617const FALLBACK_ORIGIN = 'https://markstream.local'
18+ const RAW_PREFIX = 'raw:'
19+ const DEFLATE_PREFIX = 'z:'
20+ const BASE64_CHUNK_SIZE = 0x8000
21+
22+ function encodeBase64Url ( bytes : Uint8Array ) {
23+ let binary = ''
24+ for ( let index = 0 ; index < bytes . length ; index += BASE64_CHUNK_SIZE )
25+ binary += String . fromCharCode ( ...bytes . subarray ( index , index + BASE64_CHUNK_SIZE ) )
26+
27+ return btoa ( binary )
28+ . replace ( / \+ / g, '-' )
29+ . replace ( / \/ / g, '_' )
30+ . replace ( / = + $ / g, '' )
31+ }
32+
33+ function decodeBase64Url ( payload : string ) {
34+ const normalized = payload
35+ . replace ( / - / g, '+' )
36+ . replace ( / _ / g, '/' )
37+ . padEnd ( payload . length + ( ( 4 - payload . length % 4 ) % 4 ) , '=' )
38+
39+ const binary = atob ( normalized )
40+ const bytes = new Uint8Array ( binary . length )
41+ for ( let index = 0 ; index < binary . length ; index ++ )
42+ bytes [ index ] = binary . charCodeAt ( index )
43+
44+ return bytes
45+ }
46+
47+ function encodeDeflatedPayload ( markdown : string ) {
48+ try {
49+ const compressed = deflateSync ( new TextEncoder ( ) . encode ( markdown ) , { level : 9 } )
50+ return `${ DEFLATE_PREFIX } ${ encodeBase64Url ( compressed ) } `
51+ }
52+ catch {
53+ return ''
54+ }
55+ }
1756
1857export function isLocalHost ( hostname : string ) {
1958 return hostname === 'localhost' || hostname === '127.0.0.1'
2059}
2160
2261export function encodeMarkdownPayload ( markdown : string ) {
62+ if ( ! markdown )
63+ return ''
64+
2365 const encodedRaw = encodeURIComponent ( markdown )
24- const compressed = compressToEncodedURIComponent ( markdown )
66+ const rawPayload = `${ RAW_PREFIX } ${ encodedRaw } `
67+ const legacyCompressed = compressToEncodedURIComponent ( markdown )
68+ const deflated = encodeDeflatedPayload ( markdown )
2569
26- if ( ! compressed && ! encodedRaw )
70+ const candidates = [ rawPayload , legacyCompressed , deflated ] . filter ( Boolean )
71+ if ( ! candidates . length )
2772 return ''
2873
29- return ( compressed && compressed . length < encodedRaw . length ) ? compressed : `raw: ${ encodedRaw } `
74+ return candidates . reduce ( ( shortest , candidate ) => candidate . length < shortest . length ? candidate : shortest )
3075}
3176
3277export function createMarkdownHash ( markdown : string ) {
@@ -40,9 +85,18 @@ export function decodeMarkdownHash(hash: string) {
4085 return null
4186
4287 const payload = matched [ 1 ]
43- if ( payload . startsWith ( 'raw:' ) ) {
88+ if ( payload . startsWith ( RAW_PREFIX ) ) {
89+ try {
90+ return decodeURIComponent ( payload . slice ( RAW_PREFIX . length ) )
91+ }
92+ catch {
93+ return null
94+ }
95+ }
96+
97+ if ( payload . startsWith ( DEFLATE_PREFIX ) ) {
4498 try {
45- return decodeURIComponent ( payload . slice ( 4 ) )
99+ return new TextDecoder ( ) . decode ( inflateSync ( decodeBase64Url ( payload . slice ( DEFLATE_PREFIX . length ) ) ) )
46100 }
47101 catch {
48102 return null
0 commit comments