@globalpayments/vega-sandbox-share 0.1.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # @globalpayments/vega-sandbox-share
2
+
3
+ Pure share-URL helper for Vega Sandbox. Composes a compact URL-hash
4
+ payload that describes an editable file snapshot (files, contents,
5
+ selected file, open tabs) and returns a full URL a sandbox host will
6
+ decode on mount.
7
+
8
+ DOM-free: relies only on `CompressionStream`, `TextEncoder`, and
9
+ `btoa` / `atob`. Safe to bundle into any browser package.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @globalpayments/vega-sandbox-share
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```ts
20
+ import {
21
+ buildShareUrl,
22
+ type EmbedOptions,
23
+ type ShareState,
24
+ } from '@globalpayments/vega-sandbox-share'
25
+
26
+ const state: ShareState = {
27
+ files: ['index.html', 'src/App.jsx'],
28
+ fileContents: {
29
+ 'index.html': '<!doctype html>…',
30
+ 'src/App.jsx': 'export default function App() {…}',
31
+ },
32
+ selectedFile: 'src/App.jsx',
33
+ openTabs: ['src/App.jsx'],
34
+ }
35
+
36
+ // Build a URL that a sandbox host will decode on mount.
37
+ const url = await buildShareUrl('/sandbox/', state, { embed: true })
38
+ window.open(url, '_blank')
39
+ ```
40
+
41
+ ## Public API
42
+
43
+ The public surface is intentionally minimal — one function plus the
44
+ input/option types it needs. The runtime encode/decode primitives
45
+ are internal implementation details of the URL protocol and are not
46
+ exposed on the npm surface so the wire format can evolve without a
47
+ breaking change.
48
+
49
+ | Export | Kind | Purpose |
50
+ |---|---|---|
51
+ | `buildShareUrl(host, state, opts?)` | function | Compose a full URL with the encoded state in the hash. |
52
+ | `ShareState` | type | Snapshot shape — files, contents, selected file, open tabs. |
53
+ | `EmbedOptions` | type | Optional flags for `buildShareUrl` (`embed` / `hideTree` / `hideTerminal` / `readOnly`). |
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Build a full share URL from a {@link ShareState}.
3
+ *
4
+ * Pure: no DOM access, no `window` reads.
5
+ *
6
+ * ```ts
7
+ * const url = await buildShareUrl('http://localhost:5173', state, { embed: true })
8
+ * window.open(url, '_blank')
9
+ * ```
10
+ */
11
+ import { type ShareState } from './shareState';
12
+ export interface EmbedOptions {
13
+ /** Hide the header bar (compact layout). */
14
+ embed?: boolean;
15
+ /** Hide the file explorer sidebar. */
16
+ hideTree?: boolean;
17
+ /** Hide the terminal panel. */
18
+ hideTerminal?: boolean;
19
+ /** Make the Monaco editor read-only. */
20
+ readOnly?: boolean;
21
+ }
22
+ /**
23
+ * @param sandboxHost - Origin plus optional base path or filename. Examples:
24
+ * - `"http://localhost:5173"` → `…:5173/#<hash>`
25
+ * - `"https://example.com/sandbox/"` → `…/sandbox/#<hash>`
26
+ * - `"https://example.com/sandbox/index.html"` → `…/sandbox/index.html#<hash>`
27
+ *
28
+ * Filename-looking last segments (containing a `.`) are left untouched;
29
+ * directory-looking segments get a trailing `/` appended.
30
+ * @param state - Snapshot to encode into the URL hash.
31
+ * @param embedOptions - Optional flags appended as query params.
32
+ */
33
+ export declare function buildShareUrl(sandboxHost: string, state: ShareState, embedOptions?: EmbedOptions): Promise<string>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pure gzip + base64url encoding helpers.
3
+ *
4
+ * DOM-free: relies only on `CompressionStream`, `TextEncoder`, and
5
+ * `btoa`/`atob`. Safe to bundle into any browser package.
6
+ */
7
+ export declare function gzipBase64UrlEncode(input: string): Promise<string>;
8
+ export declare function gzipBase64UrlDecode(encoded: string): Promise<string>;
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ async function d(e) {
2
+ const n = new CompressionStream("gzip"), r = n.writable.getWriter();
3
+ r.write(new TextEncoder().encode(e)), r.close();
4
+ const s = [], t = n.readable.getReader();
5
+ for (; ; ) {
6
+ const { done: a, value: c } = await t.read();
7
+ if (a) break;
8
+ s.push(c);
9
+ }
10
+ const o = s.reduce((a, c) => a + c.length, 0), i = new Uint8Array(o);
11
+ let l = 0;
12
+ for (const a of s)
13
+ i.set(a, l), l += a.length;
14
+ return btoa(String.fromCharCode(...i)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
15
+ }
16
+ async function h(e) {
17
+ return d(JSON.stringify(e));
18
+ }
19
+ async function f(e, n, r = {}) {
20
+ const s = await h(n), t = new URL(u(e));
21
+ return r.embed && t.searchParams.set("embed", ""), r.hideTree && t.searchParams.set("hideTree", ""), r.hideTerminal && t.searchParams.set("hideTerminal", ""), r.readOnly && t.searchParams.set("readOnly", ""), t.hash = s, t.toString();
22
+ }
23
+ function u(e) {
24
+ if (e.endsWith("/")) return e;
25
+ const n = e.split(/[?#]/)[0];
26
+ return n.slice(n.lastIndexOf("/") + 1).includes(".") ? e : e + "/";
27
+ }
28
+ export {
29
+ f as buildShareUrl
30
+ };
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/encoding.ts","../src/shareState.ts","../src/buildShareUrl.ts"],"sourcesContent":["/**\n * Pure gzip + base64url encoding helpers.\n *\n * DOM-free: relies only on `CompressionStream`, `TextEncoder`, and\n * `btoa`/`atob`. Safe to bundle into any browser package.\n */\n\nexport async function gzipBase64UrlEncode(input: string): Promise<string> {\n const stream = new CompressionStream('gzip')\n const writer = stream.writable.getWriter()\n writer.write(new TextEncoder().encode(input))\n writer.close()\n const chunks: Uint8Array[] = []\n const reader = stream.readable.getReader()\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n chunks.push(value)\n }\n const total = chunks.reduce((n, c) => n + c.length, 0)\n const merged = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n merged.set(chunk, offset)\n offset += chunk.length\n }\n return btoa(String.fromCharCode(...merged))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '')\n}\n\nexport async function gzipBase64UrlDecode(encoded: string): Promise<string> {\n const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/')\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n\n const stream = new DecompressionStream('gzip')\n const writer = stream.writable.getWriter()\n writer.write(bytes)\n writer.close()\n const chunks: Uint8Array[] = []\n const reader = stream.readable.getReader()\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n chunks.push(value)\n }\n const total = chunks.reduce((n, c) => n + c.length, 0)\n const merged = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n merged.set(chunk, offset)\n offset += chunk.length\n }\n return new TextDecoder().decode(merged)\n}\n","/**\n * Wire format for a shareable sandbox snapshot.\n *\n * A `ShareState` plus the encoding scheme in `./encoding.ts` fully\n * describes what the SPA reconstructs on load: the file list, their\n * contents, the initially selected file, and the tabs to open.\n */\n\nimport { gzipBase64UrlDecode, gzipBase64UrlEncode } from './encoding'\n\nexport interface ShareState {\n files: string[]\n fileContents: Record<string, string>\n selectedFile: string\n openTabs: string[]\n}\n\n/** Encode a {@link ShareState} into a compact URL-safe string. */\nexport async function encodeShareState(state: ShareState): Promise<string> {\n return gzipBase64UrlEncode(JSON.stringify(state))\n}\n\n/**\n * Decode a previously encoded {@link ShareState}. Returns `null` on any\n * failure (empty, corrupt, or wrong shape) so callers can fall through\n * to default state without a try/catch.\n */\nexport async function decodeShareState(hash: string): Promise<ShareState | null> {\n if (!hash) return null\n try {\n const json = await gzipBase64UrlDecode(hash)\n return JSON.parse(json) as ShareState\n } catch {\n return null\n }\n}\n","/**\n * Build a full share URL from a {@link ShareState}.\n *\n * Pure: no DOM access, no `window` reads.\n *\n * ```ts\n * const url = await buildShareUrl('http://localhost:5173', state, { embed: true })\n * window.open(url, '_blank')\n * ```\n */\n\nimport { encodeShareState, type ShareState } from './shareState'\n\nexport interface EmbedOptions {\n /** Hide the header bar (compact layout). */\n embed?: boolean\n /** Hide the file explorer sidebar. */\n hideTree?: boolean\n /** Hide the terminal panel. */\n hideTerminal?: boolean\n /** Make the Monaco editor read-only. */\n readOnly?: boolean\n}\n\n/**\n * @param sandboxHost - Origin plus optional base path or filename. Examples:\n * - `\"http://localhost:5173\"` → `…:5173/#<hash>`\n * - `\"https://example.com/sandbox/\"` → `…/sandbox/#<hash>`\n * - `\"https://example.com/sandbox/index.html\"` → `…/sandbox/index.html#<hash>`\n *\n * Filename-looking last segments (containing a `.`) are left untouched;\n * directory-looking segments get a trailing `/` appended.\n * @param state - Snapshot to encode into the URL hash.\n * @param embedOptions - Optional flags appended as query params.\n */\nexport async function buildShareUrl(\n sandboxHost: string,\n state: ShareState,\n embedOptions: EmbedOptions = {}\n): Promise<string> {\n const hash = await encodeShareState(state)\n const url = new URL(normalizeHost(sandboxHost))\n if (embedOptions.embed) url.searchParams.set('embed', '')\n if (embedOptions.hideTree) url.searchParams.set('hideTree', '')\n if (embedOptions.hideTerminal) url.searchParams.set('hideTerminal', '')\n if (embedOptions.readOnly) url.searchParams.set('readOnly', '')\n url.hash = hash\n return url.toString()\n}\n\n/** Append `/` to directory-ish paths; leave filename-ish paths untouched. */\nfunction normalizeHost(host: string): string {\n if (host.endsWith('/')) return host\n const pathOnly = host.split(/[?#]/)[0]\n const lastSegment = pathOnly.slice(pathOnly.lastIndexOf('/') + 1)\n if (lastSegment.includes('.')) return host\n return host + '/'\n}\n"],"names":["gzipBase64UrlEncode","input","stream","writer","chunks","reader","done","value","total","n","merged","offset","chunk","encodeShareState","state","buildShareUrl","sandboxHost","embedOptions","hash","url","normalizeHost","host","pathOnly"],"mappings":"AAOA,eAAsBA,EAAoBC,GAAgC;AACxE,QAAMC,IAAS,IAAI,kBAAkB,MAAM,GACrCC,IAASD,EAAO,SAAS,UAAA;AAC/B,EAAAC,EAAO,MAAM,IAAI,YAAA,EAAc,OAAOF,CAAK,CAAC,GAC5CE,EAAO,MAAA;AACP,QAAMC,IAAuB,CAAA,GACvBC,IAASH,EAAO,SAAS,UAAA;AAC/B,aAAa;AACX,UAAM,EAAE,MAAAI,GAAM,OAAAC,EAAA,IAAU,MAAMF,EAAO,KAAA;AACrC,QAAIC,EAAM;AACV,IAAAF,EAAO,KAAKG,CAAK;AAAA,EACnB;AACA,QAAMC,IAAQJ,EAAO,OAAO,CAACK,GAAG,MAAMA,IAAI,EAAE,QAAQ,CAAC,GAC/CC,IAAS,IAAI,WAAWF,CAAK;AACnC,MAAIG,IAAS;AACb,aAAWC,KAASR;AAClB,IAAAM,EAAO,IAAIE,GAAOD,CAAM,GACxBA,KAAUC,EAAM;AAElB,SAAO,KAAK,OAAO,aAAa,GAAGF,CAAM,CAAC,EACvC,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE;AACtB;ACZA,eAAsBG,EAAiBC,GAAoC;AACzE,SAAOd,EAAoB,KAAK,UAAUc,CAAK,CAAC;AAClD;ACeA,eAAsBC,EACpBC,GACAF,GACAG,IAA6B,CAAA,GACZ;AACjB,QAAMC,IAAO,MAAML,EAAiBC,CAAK,GACnCK,IAAM,IAAI,IAAIC,EAAcJ,CAAW,CAAC;AAC9C,SAAIC,EAAa,SAAOE,EAAI,aAAa,IAAI,SAAS,EAAE,GACpDF,EAAa,YAAUE,EAAI,aAAa,IAAI,YAAY,EAAE,GAC1DF,EAAa,gBAAcE,EAAI,aAAa,IAAI,gBAAgB,EAAE,GAClEF,EAAa,YAAUE,EAAI,aAAa,IAAI,YAAY,EAAE,GAC9DA,EAAI,OAAOD,GACJC,EAAI,SAAA;AACb;AAGA,SAASC,EAAcC,GAAsB;AAC3C,MAAIA,EAAK,SAAS,GAAG,EAAG,QAAOA;AAC/B,QAAMC,IAAWD,EAAK,MAAM,MAAM,EAAE,CAAC;AAErC,SADoBC,EAAS,MAAMA,EAAS,YAAY,GAAG,IAAI,CAAC,EAChD,SAAS,GAAG,IAAUD,IAC/BA,IAAO;AAChB;"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Published package entry for @globalpayments/vega-sandbox-share.
3
+ *
4
+ * Public surface — deliberately minimal:
5
+ * - `buildShareUrl(host, state, opts?)` — compose a full share URL.
6
+ * - `ShareState` — snapshot shape (input type for `buildShareUrl`).
7
+ * - `EmbedOptions` — optional flags for `buildShareUrl`.
8
+ *
9
+ * The runtime encode/decode primitives (`encodeShareState` /
10
+ * `decodeShareState`) and the raw gzip base64url helpers are
11
+ * intentionally NOT re-exported here. They are internal implementation
12
+ * details of the URL protocol shared between the URL builder and the
13
+ * sandbox SPA loader; keeping them off the public surface lets us
14
+ * evolve the wire format without a breaking change to npm consumers.
15
+ *
16
+ * Workspace-internal callers (the sandbox app) that need those
17
+ * lower-level helpers import from the internal `./index.ts` barrel
18
+ * via a Vite / Vitest alias.
19
+ */
20
+ export { buildShareUrl, type EmbedOptions } from './buildShareUrl';
21
+ export { type ShareState } from './shareState';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Wire format for a shareable sandbox snapshot.
3
+ *
4
+ * A `ShareState` plus the encoding scheme in `./encoding.ts` fully
5
+ * describes what the SPA reconstructs on load: the file list, their
6
+ * contents, the initially selected file, and the tabs to open.
7
+ */
8
+ export interface ShareState {
9
+ files: string[];
10
+ fileContents: Record<string, string>;
11
+ selectedFile: string;
12
+ openTabs: string[];
13
+ }
14
+ /** Encode a {@link ShareState} into a compact URL-safe string. */
15
+ export declare function encodeShareState(state: ShareState): Promise<string>;
16
+ /**
17
+ * Decode a previously encoded {@link ShareState}. Returns `null` on any
18
+ * failure (empty, corrupt, or wrong shape) so callers can fall through
19
+ * to default state without a try/catch.
20
+ */
21
+ export declare function decodeShareState(hash: string): Promise<ShareState | null>;
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@globalpayments/vega-sandbox-share",
3
+ "version": "0.1.1-0",
4
+ "description": "Pure share-URL helpers for Vega Sandbox: build and decode URL-hash payloads that describe an editable file snapshot.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "scripts": {
11
+ "prepublishOnly": "cd ../.. && npx nx run vega-sandbox-share:build"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/public.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/public.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/heartlandpayments/vega-sandbox.git",
30
+ "directory": "packages/share"
31
+ },
32
+ "homepage": "https://github.com/heartlandpayments/vega-sandbox/tree/main/packages/share",
33
+ "bugs": {
34
+ "url": "https://github.com/heartlandpayments/vega-sandbox/issues"
35
+ },
36
+ "engines": {
37
+ "node": ">=20"
38
+ }
39
+ }