Skip to content

Commit

Permalink
Sharing via gzip
Browse files Browse the repository at this point in the history
  • Loading branch information
simonharrer committed Jul 19, 2024
1 parent 7bd932b commit 905a4a1
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CHANGELOG

## 2024-07-19

- Better sharing support. When clicking "File -> Share", the Data Contract creates a shareable URL with the Data Contract YAML encoded in the URL. Previously, we only used base64 encoding, but it turned out that may lead to some URLs that are too long. To solve this, we gzip before encoding with base64. This allows for much large data contracts to be shared.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The [Data Contract Editor](https://editor.datacontract.com) is like VS Code, but
- ✅ Live HTML preview
- ✅ Share data contracts via URLs

Try it out at [editor.datacontract.com](https://editor.datacontract.com).
Try it out at [editor.datacontract.com](https://editor.datacontract.com). And if you like it, please star the repository.

![Data Contract Editor](images/screenshot.png)

Expand Down
82 changes: 82 additions & 0 deletions src/sharing/encoding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* encoded = encodebase64(gzip(yaml)) */
export async function encodeYamlCode(yamlCode) {
const compressed = await gzip(yamlCode);
return window.btoa(String.fromCharCode(...new Uint8Array(compressed)));
}

/* decoded = gunzip(decodebase64(queryparameter)) */
export async function decodeQueryParameter(queryParameter) {
const compressed = Uint8Array.from(window.atob(queryParameter), c => c.charCodeAt(0));
const decompressed = await gunzip(compressed);
const decoder = new TextDecoder();
return decoder.decode(decompressed)
}

async function gzip(input) {
const encoder = new TextEncoder();
const data = encoder.encode(input);

const stream = new CompressionStream('gzip');
const writer = stream.writable.getWriter();
writer.write(data);
writer.close();

const compressedStream = stream.readable;
const reader = compressedStream.getReader();

let chunks = [];
let done = false;

while (!done) {
const { value, done: doneReading } = await reader.read();
if (value) {
chunks.push(value);
}
done = doneReading;
}

let size = 0;
chunks.forEach(chunk => size += chunk.length);

let compressed = new Uint8Array(size);
let offset = 0;
chunks.forEach(chunk => {
compressed.set(chunk, offset);
offset += chunk.length;
});

return compressed;
}

async function gunzip(data) {
const stream = new DecompressionStream('gzip');
const writer = stream.writable.getWriter();
writer.write(data);
writer.close();

const decompressedStream = stream.readable;
const reader = decompressedStream.getReader();

let chunks = [];
let done = false;

while (!done) {
const { value, done: doneReading } = await reader.read();
if (value) {
chunks.push(value);
}
done = doneReading;
}

let size = 0;
chunks.forEach(chunk => size += chunk.length);

let decompressed = new Uint8Array(size);
let offset = 0;
chunks.forEach(chunk => {
decompressed.set(chunk, offset);
offset += chunk.length;
});

return decompressed;
}
8 changes: 5 additions & 3 deletions src/storage/storage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getExample } from "../templates/examples.js";
import {decodeQueryParameter} from "../sharing/encoding.js";


function loadDataContractYaml() {
Expand All @@ -18,9 +19,10 @@ export function loadInitialDocument() {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.has("dc")) {
const value = window.atob(urlParams.get("dc"));
storeDataContractYaml(value);
window.location.search = "";
decodeQueryParameter(urlParams.get("dc")).then(value => {
storeDataContractYaml(value);
window.location.search = "";
});
}
if (isFirstLoadOfDataContractEditor()) {
storeDataContractYaml(getExample());
Expand Down
14 changes: 9 additions & 5 deletions src/ui/elements/nav-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { LitElement, html } from "lit";
import * as monaco from "monaco-editor";

import { getExample, getMinimal } from "../../templates/examples.js";
import {encodeYamlCode} from "../../sharing/encoding.js";

export class NavElement extends LitElement {

Expand Down Expand Up @@ -501,11 +502,14 @@ export class NavElement extends LitElement {
}

handleShare() {
const yamlCode = this.editor.getValue();
const shareUrl = location.protocol + "//" + location.host + "/?dc=" + window.btoa(yamlCode);
navigator.clipboard.writeText(shareUrl);
this.toggleFileMenu();
this.notifyUser('Sharing prepared', 'Sharelink copied to Clipboard');


encodeYamlCode(this.editor.getValue()).then((encodedYaml) => {
const shareUrl = `${location.protocol}//${location.host}/?dc=${encodedYaml}`;
navigator.clipboard.writeText(shareUrl);
this.toggleFileMenu();
this.notifyUser('Sharing prepared', 'Sharelink copied to Clipboard');
});
}

handleClear() {
Expand Down

0 comments on commit 905a4a1

Please sign in to comment.