Serdes
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
}
|
||||||
@@ -103,15 +103,19 @@ describe('Site Loads', () => {
|
|||||||
'/edit#eyJjb2RlIjoiY2xhc3NEaWFncmFtXG4gICAgQW5pbWFsIDx8LS0gRHVja1xuICAgIEFuaW1hbCA8fC0tIEZpc2hcbiAgICBBbmltYWwgPHwtLSBaZWJyYVxuICAgIEFuaW1hbCA6ICtpbnQgYWdlXG4gICAgQW5pbWFsIDogK1N0cmluZyBnZW5kZXJcbiAgICBBbmltYWw6ICtpc01hbW1hbCgpXG4gICAgQW5pbWFsOiArbWF0ZSgpXG4gICAgY2xhc3MgRHVja3tcbiAgICAgICtTdHJpbmcgYmVha0NvbG9yXG4gICAgICArc3dpbSgpXG4gICAgICArcXVhY2soKVxuICAgIH1cbiAgICBjbGFzcyBGaXNoe1xuICAgICAgLWludCBzaXplSW5GZWV0XG4gICAgICAtY2FuRWF0KClcbiAgICB9XG4gICAgY2xhc3MgWmVicmF7XG4gICAgICArYm9vbCBpc193aWxkXG4gICAgICArcnVuKClcbiAgICB9XG4gICAgICAgICAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGFya1wiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
|
'/edit#eyJjb2RlIjoiY2xhc3NEaWFncmFtXG4gICAgQW5pbWFsIDx8LS0gRHVja1xuICAgIEFuaW1hbCA8fC0tIEZpc2hcbiAgICBBbmltYWwgPHwtLSBaZWJyYVxuICAgIEFuaW1hbCA6ICtpbnQgYWdlXG4gICAgQW5pbWFsIDogK1N0cmluZyBnZW5kZXJcbiAgICBBbmltYWw6ICtpc01hbW1hbCgpXG4gICAgQW5pbWFsOiArbWF0ZSgpXG4gICAgY2xhc3MgRHVja3tcbiAgICAgICtTdHJpbmcgYmVha0NvbG9yXG4gICAgICArc3dpbSgpXG4gICAgICArcXVhY2soKVxuICAgIH1cbiAgICBjbGFzcyBGaXNoe1xuICAgICAgLWludCBzaXplSW5GZWV0XG4gICAgICAtY2FuRWF0KClcbiAgICB9XG4gICAgY2xhc3MgWmVicmF7XG4gICAgICArYm9vbCBpc193aWxkXG4gICAgICArcnVuKClcbiAgICB9XG4gICAgICAgICAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGFya1wiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
|
||||||
);
|
);
|
||||||
cy.contains('Animal');
|
cy.contains('Animal');
|
||||||
|
cy.visit(
|
||||||
|
'/edit/#base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW05ldyBZZWFyXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfVxuICAgIEMgLS0-fE9uZXwgRFtMYXB0b3BdXG4gICAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXSIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9'
|
||||||
|
);
|
||||||
|
cy.contains('New Year');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load compressed URL', () => {
|
it('should load compressed URL', () => {
|
||||||
cy.visit(
|
cy.visit(
|
||||||
'/edit#eNpVkM2KwkAQhF-l6dMK5gVyEDRxvYi7sF6WjIcm0zqDzg_jBJEk725Hd2G3Tw31VVFUj23QjCWeEkUD-1p5kFs2O77BN1M6QFEshg1ncMHzfYDV2ybA1YQYrT_NXvhqgqDqtxPGkI315_ElVU__h-cB6mZLMYd4-Kvsb2GAdWM_jcT_V0xicb03RyqPVLSUoJI-OEfHyZHV0rqfDAqzYccKS3k1pbNC5Ufhuqgp81rbHBJKxuXKc6Quh6-7b7HMqeNfqLYkC7gfanwAlW1ZvQ'
|
'/edit#pako:eNpVkM2KwkAQhF-l6dMK5gVyEDRxvYi7sF6WjIcm0zqDzg_jBJEk725Hd2G3Tw31VVFUj23QjCWeEkUD-1p5kFs2O77BN1M6QFEshg1ncMHzfYDV2ybA1YQYrT_NXvhqgqDqtxPGkI315_ElVU__h-cB6mZLMYd4-Kvsb2GAdWM_jcT_V0xicb03RyqPVLSUoJI-OEfHyZHV0rqfDAqzYccKS3k1pbNC5Ufhuqgp81rbHBJKxuXKc6Quh6-7b7HMqeNfqLYkC7gfanwAlW1ZvQ'
|
||||||
);
|
);
|
||||||
cy.contains('New Year');
|
cy.contains('New Year');
|
||||||
cy.visit(
|
cy.visit(
|
||||||
'/edit#eNptkU1PwzAMhv9K5BOI9Q9EXBDbJA477YYqITcxndV8QD40weh_Jy1rGR0-OY_tV2_sEyivCSQogzGuGduAtnaixINji0bcf1WVWGfVXdMtx8M1faYm4B8sxR27JLClJd6nwK4VLTlN4bI4jMQd2pLe3C4KFhNNcLQ92jv9ADGLNoTdozc-zIV4ZDsNlud7RtVN7_5Sb_jYrFcN3iN_0pPbEqUZK3QbTP_Ojyv4NdR4bwTHlyMbPcOQ3WJ2CliBpWCRdbnLqFJDOpClGmRJNYauhtr1pS-_6bKMjebkA8hXNJFWgDn5_YdTIFPINDWdb3vu6r8BaWOZRQ'
|
'/edit#pako:eNptkU1PwzAMhv9K5BOI9Q9EXBDbJA477YYqITcxndV8QD40weh_Jy1rGR0-OY_tV2_sEyivCSQogzGuGduAtnaixINji0bcf1WVWGfVXdMtx8M1faYm4B8sxR27JLClJd6nwK4VLTlN4bI4jMQd2pLe3C4KFhNNcLQ92jv9ADGLNoTdozc-zIV4ZDsNlud7RtVN7_5Sb_jYrFcN3iN_0pPbEqUZK3QbTP_Ojyv4NdR4bwTHlyMbPcOQ3WJ2CliBpWCRdbnLqFJDOpClGmRJNYauhtr1pS-_6bKMjebkA8hXNJFWgDn5_YdTIFPINDWdb3vu6r8BaWOZRQ'
|
||||||
);
|
);
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.contains('Animal');
|
cy.contains('Animal');
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import { krokiRendererUrl, rendererUrl } from '$lib/util/env';
|
import { krokiRendererUrl, rendererUrl } from '$lib/util/env';
|
||||||
import { compressedState, codeStore, compressString } from '$lib/util/state';
|
import { pakoSerde } from '$lib/util/serde';
|
||||||
|
import { serializedState, codeStore } from '$lib/util/state';
|
||||||
import { toBase64 } from 'js-base64';
|
import { toBase64 } from 'js-base64';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
@@ -138,10 +139,10 @@
|
|||||||
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
|
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
|
||||||
isNetlify = true;
|
isNetlify = true;
|
||||||
}
|
}
|
||||||
compressedState.subscribe((encodedState: string) => {
|
serializedState.subscribe((encodedState: string) => {
|
||||||
iUrl = `${rendererUrl}/img/${encodedState}`;
|
iUrl = `${rendererUrl}/img/${encodedState}`;
|
||||||
svgUrl = `${rendererUrl}/svg/${encodedState}`;
|
svgUrl = `${rendererUrl}/svg/${encodedState}`;
|
||||||
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${compressString($codeStore.code)}`;
|
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize($codeStore.code)}`;
|
||||||
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`;
|
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { deflate, inflate } from 'pako';
|
||||||
|
import { toUint8Array, fromUint8Array, toBase64, fromBase64 } from 'js-base64';
|
||||||
|
import type { State } from '$lib/types';
|
||||||
|
|
||||||
|
interface Serde {
|
||||||
|
serialize: (state: string) => string;
|
||||||
|
deserialize: (state: string) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SerdeType {
|
||||||
|
Base64 = 'base64',
|
||||||
|
Pako = 'pako'
|
||||||
|
}
|
||||||
|
|
||||||
|
const base64Serde: Serde = {
|
||||||
|
serialize: (state: string): string => {
|
||||||
|
return toBase64(state, true);
|
||||||
|
},
|
||||||
|
deserialize: (state: string): string => {
|
||||||
|
return fromBase64(state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pakoSerde: Serde = {
|
||||||
|
serialize: (state: string): string => {
|
||||||
|
const data = new TextEncoder().encode(state);
|
||||||
|
const compressed = deflate(data, { level: 9 });
|
||||||
|
return fromUint8Array(compressed, true);
|
||||||
|
},
|
||||||
|
deserialize: (state: string): string => {
|
||||||
|
const data = toUint8Array(state);
|
||||||
|
return inflate(data, { to: 'string' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const serdes: Record<SerdeType, Serde> = {
|
||||||
|
[SerdeType.Base64]: base64Serde,
|
||||||
|
[SerdeType.Pako]: pakoSerde
|
||||||
|
};
|
||||||
|
|
||||||
|
export const serializeState = (state: State): string => {
|
||||||
|
const json = JSON.stringify(state);
|
||||||
|
const defaultSerde = SerdeType.Pako;
|
||||||
|
const serialized = serdes[defaultSerde].serialize(json);
|
||||||
|
return `${defaultSerde}:${serialized}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deserializeState = (state: string): State => {
|
||||||
|
let type: string, serialized: string;
|
||||||
|
if (state.includes(':')) {
|
||||||
|
[type, serialized] = state.split(':');
|
||||||
|
} else {
|
||||||
|
type = SerdeType.Base64;
|
||||||
|
serialized = state;
|
||||||
|
}
|
||||||
|
const json = serdes[type].deserialize(serialized);
|
||||||
|
return JSON.parse(json);
|
||||||
|
};
|
||||||
+5
-30
@@ -1,10 +1,9 @@
|
|||||||
import { writable, get, derived } from 'svelte/store';
|
import { writable, get, derived } from 'svelte/store';
|
||||||
import type { Readable } from 'svelte/store';
|
import type { Readable } from 'svelte/store';
|
||||||
import { fromBase64, toUint8Array, fromUint8Array } from 'js-base64';
|
|
||||||
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
||||||
import type { State } from '$lib/types';
|
import type { State } from '$lib/types';
|
||||||
import { saveStatistics } from './stats';
|
import { saveStatistics } from './stats';
|
||||||
import pako from 'pako';
|
import { serializeState, deserializeState } from './serde';
|
||||||
|
|
||||||
export const defaultState: State = {
|
export const defaultState: State = {
|
||||||
code: `graph TD
|
code: `graph TD
|
||||||
@@ -37,40 +36,16 @@ const urlParseFailedState = `graph TD
|
|||||||
G --> |"No :("| H(Try using the Timeline tab in History <br/>from same browser you used to create the diagram.)
|
G --> |"No :("| H(Try using the Timeline tab in History <br/>from same browser you used to create the diagram.)
|
||||||
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
|
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
|
||||||
|
|
||||||
const textEncode = (str: string) => {
|
|
||||||
return new TextEncoder().encode(str);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const compressString = (source: string): string => {
|
|
||||||
const data = textEncode(source);
|
|
||||||
const compressed = pako.deflate(data, { level: 9 });
|
|
||||||
const result = fromUint8Array(compressed, true);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const decompressString = (source: string): string => {
|
|
||||||
const data = toUint8Array(source);
|
|
||||||
const decompressed = pako.inflate(data, { to: 'string' });
|
|
||||||
return decompressed;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
||||||
export const compressedState: Readable<string> = derived([codeStore], ([code], set) => {
|
export const serializedState: Readable<string> = derived([codeStore], ([code], set) => {
|
||||||
set(compressString(JSON.stringify(code)));
|
set(serializeState(code));
|
||||||
});
|
});
|
||||||
|
|
||||||
export const loadState = (data: string): void => {
|
export const loadState = (data: string): void => {
|
||||||
let state: State;
|
let state: State;
|
||||||
console.log('Loading', data);
|
console.log('Loading', data);
|
||||||
try {
|
try {
|
||||||
let stateStr: string;
|
state = deserializeState(data);
|
||||||
if (data.startsWith('eyJ')) {
|
|
||||||
stateStr = fromBase64(data);
|
|
||||||
} else {
|
|
||||||
stateStr = decompressString(data);
|
|
||||||
}
|
|
||||||
console.log(`Trying to load state: ${stateStr}`);
|
|
||||||
state = JSON.parse(stateStr);
|
|
||||||
const mermaidConfig =
|
const mermaidConfig =
|
||||||
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
|
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
|
||||||
if (
|
if (
|
||||||
@@ -140,7 +115,7 @@ export const toggleDarkTheme = (dark: boolean): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const initURLSubscription = (): void => {
|
export const initURLSubscription = (): void => {
|
||||||
compressedState.subscribe((state: string) => {
|
serializedState.subscribe((state: string) => {
|
||||||
history.replaceState(undefined, undefined, `#${state}`);
|
history.replaceState(undefined, undefined, `#${state}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import View from '$lib/components/view.svelte';
|
import View from '$lib/components/view.svelte';
|
||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import History from '$lib/components/history/history.svelte';
|
import History from '$lib/components/history/history.svelte';
|
||||||
import { updateCode, updateConfig, codeStore, compressedState } from '$lib/util/state';
|
import { updateCode, updateConfig, codeStore, serializedState } from '$lib/util/state';
|
||||||
import { initHandler, syncDiagram } from '$lib/util/util';
|
import { initHandler, syncDiagram } from '$lib/util/util';
|
||||||
import { errorStore } from '$lib/util/error';
|
import { errorStore } from '$lib/util/error';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
import type { EditorUpdateEvent, State, Tab } from '$lib/types';
|
import type { EditorUpdateEvent, State, Tab } from '$lib/types';
|
||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
compressedState; // Weird fix for error > compressedState is not defined. Treeshaking?
|
serializedState; // Weird fix for error > serializedState is not defined. Treeshaking?
|
||||||
let selectedMode = 'code';
|
let selectedMode = 'code';
|
||||||
const languageMap = {
|
const languageMap = {
|
||||||
code: 'mermaid',
|
code: 'mermaid',
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const viewDiagram = () => {
|
const viewDiagram = () => {
|
||||||
window.open(`${base}/view#${$compressedState}`, '_blank').focus();
|
window.open(`${base}/view#${$serializedState}`, '_blank').focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user