Skip to content

Commit 840f871

Browse files
committed
fix: improve share link generation by choosing shorter representation and add tests for payload handling
1 parent c921d73 commit 840f871

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

playground/src/pages/test.vue

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,12 @@ function decodeFromUrl(s: string) {
6767
}
6868
6969
function generateShareLink() {
70-
const data = encodeForUrl(input.value)
71-
if (!data)
70+
const encodedRaw = encodeURIComponent(input.value)
71+
const compressed = encodeForUrl(input.value)
72+
if (!compressed && !encodedRaw)
7273
return
74+
// Choose the shorter representation: compressed (URL-safe) or raw encoded
75+
const data = (compressed && compressed.length < encodedRaw.length) ? compressed : `raw:${encodedRaw}`
7376
const url = new URL(window.location.href)
7477
url.hash = `data=${data}`
7578
const full = url.toString()
@@ -166,9 +169,21 @@ function restoreFromUrl() {
166169
return
167170
const m = hash.match(/data=([^&]+)/)
168171
if (m && m[1]) {
169-
const decoded = decodeFromUrl(m[1])
170-
if (decoded)
171-
input.value = decoded
172+
const payload = m[1]
173+
// support `raw:` fallback where we stored uncompressed (encoded) content
174+
if (payload.startsWith('raw:')) {
175+
try {
176+
input.value = decodeURIComponent(payload.slice(4))
177+
}
178+
catch {
179+
// ignore
180+
}
181+
}
182+
else {
183+
const decoded = decodeFromUrl(payload)
184+
if (decoded)
185+
input.value = decoded
186+
}
172187
}
173188
}
174189
catch {

test/playground-share.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from 'lz-string'
3+
4+
function makeDataForUrl(input: string) {
5+
const encodedRaw = encodeURIComponent(input)
6+
let compressed = ''
7+
try {
8+
compressed = compressToEncodedURIComponent(input)
9+
}
10+
catch {
11+
compressed = ''
12+
}
13+
const data = (compressed && compressed.length < encodedRaw.length) ? compressed : `raw:${encodedRaw}`
14+
return data
15+
}
16+
17+
function restoreFromData(payload: string) {
18+
if (payload.startsWith('raw:')) {
19+
try {
20+
return decodeURIComponent(payload.slice(4))
21+
}
22+
catch {
23+
return ''
24+
}
25+
}
26+
try {
27+
return decompressFromEncodedURIComponent(payload) || ''
28+
}
29+
catch {
30+
return ''
31+
}
32+
}
33+
34+
describe('playground share payload', () => {
35+
it('chooses the shorter representation and can restore it (distinct links)', () => {
36+
const input = `<a href=" ">示例链接1</a > \n<a href="https://www.google.com">Google</a > \n<a href="https://github.com">GitHub</a > \n<a href="https://stackoverflow.com">Stack Overflow</a > \n<a href="https://www.wikipedia.org">维基百科</a >`
37+
const data = makeDataForUrl(input)
38+
// The implementation picks the shorter of compressed vs raw; ensure that holds
39+
const rawLen = encodeURIComponent(input).length
40+
expect(data.length).toBeLessThanOrEqual(rawLen)
41+
const restored = restoreFromData(data)
42+
expect(restored).toBe(input)
43+
})
44+
45+
it('prefers compressed when input is repetitive', () => {
46+
const input = 'hello '.repeat(1000)
47+
const data = makeDataForUrl(input)
48+
// For repetitive input compressed should be shorter
49+
expect(data.startsWith('raw:')).toBe(false)
50+
const restored = restoreFromData(data)
51+
expect(restored).toBe(input)
52+
// sanity: compression should actually reduce length vs raw
53+
const rawLen = encodeURIComponent(input).length
54+
expect(data.length).toBeLessThan(rawLen)
55+
})
56+
})

0 commit comments

Comments
 (0)