Skip to content

Instantly share code, notes, and snippets.

@jrcd-HR
Last active December 14, 2023 19:12
Show Gist options
  • Save jrcd-HR/92ef3407c9f5061787b7c7a095a031ea to your computer and use it in GitHub Desktop.
Save jrcd-HR/92ef3407c9f5061787b7c7a095a031ea to your computer and use it in GitHub Desktop.
Upgrade play.d2lang.org URLs generated before Dec 06
/* Upgrade old D2 playground URLs (<= Dec 06 2023) to new URLS (>= Dec 06 2023).
*
* usage: go run upd2url.go < file_of_old_urls > new_urls
*
* Timeline:
* 1. Nov 7 2023:
* d2 changed their URL encode and decode to avoid the use of a custom DEFLATE dictionary.
* https://github.com/terrastruct/d2/commit/5eead31c91fcc1f6c0ecaf28e3006758055efd8b
* 2. Dec 6 2023:
* play.d2lang.org upgraded its WASM compiled version of d2 that is run client-side.
* https://github.com/terrastruct/d2-playground/commit/ae1e9c3bcd9896a978f80a0d1d370fea302e85a0
*
* If you saved any URLs to play.d2lang.com that included D2 diagrams (compressed, base64
* encoded, and passed in the script= query parameter) these would no longer decode properly
* in the newly deployed play.d2lang.org site. If you didn't save a backup the old diagram source
* was lost.
*
* This program reads old URLs and prints out new URLs a line at a time. The old URLs are decoded
* using the old deflate dictionary and encoded to new URLs which have no custom dictionary.
*
* Justin Davis <justin_davis@baxter.com>
*/
package main
import (
"bufio"
"bytes"
"compress/flate"
"encoding/base64"
"fmt"
"io"
"net/url"
"os"
"strings"
)
// these must be pre-sorted
const keywords = `
3d animated bold border-radius class classes constraint desc direction
double-border fill fill-pattern filled font font-color font-size
grid-columns grid-gap grid-rows height horizontal-gap icon italic
label layers left link multiple near opacity scenarios shadow shape
source-arrowhead steps stroke stroke-dash stroke-width style
target-arrowhead text-transform tooltip top underline vars
vertical-gap width
`
var oldRawDict []byte
func init() {
common := strings.Fields(keywords)
oldRawDict = []byte("->" + "<-" + "--" + "<->" + strings.Join(common, ""))
}
func UpgradeURL(old *url.URL) error {
vals := old.Query()
oldscript64 := vals.Get("script")
if oldscript64 == "" {
return fmt.Errorf("URL missing script query param", old)
}
oldscript, err := base64.URLEncoding.DecodeString(oldscript64)
if err != nil {
return err
}
bin, bout := bytes.NewBuffer(oldscript), &bytes.Buffer{}
zrdr := flate.NewReaderDict(bin, oldRawDict)
zwtr, err := flate.NewWriter(bout, flate.BestCompression)
if err != nil {
return err
}
_, err = io.Copy(zwtr, zrdr)
if err != nil {
return err
}
zwtr.Close()
newscript64 := base64.URLEncoding.EncodeToString(bout.Bytes())
vals.Set("script", newscript64)
old.RawQuery = vals.Encode()
return nil
}
func main() {
scan := bufio.NewScanner(os.Stdin)
scan.Split(bufio.ScanLines)
for scan.Scan() {
var input *url.URL
var err error
if input, err = url.ParseRequestURI(scan.Text()); err != nil {
fmt.Fprintln(os.Stderr, "error: invalid URL")
continue
}
if err := UpgradeURL(input); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
continue
}
fmt.Println(input)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment