backpack-viewer 0.7.1 → 0.7.3

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.
@@ -1,357 +1,199 @@
1
- const RELAY_URL = "https://app.backpackontology.com";
2
- // OAuth metadata endpoint on the relay
3
- const OAUTH_METADATA_URL = `${RELAY_URL}/.well-known/oauth-authorization-server`;
4
- let panel = null;
5
- export function activate(viewer) {
6
- // Register the Share button in the top-right toolbar
7
- viewer.registerTaskbarIcon({
8
- label: "Share",
9
- iconText: "\u2197", // ↗
10
- position: "top-right",
11
- onClick: () => toggleSharePanel(viewer),
12
- });
1
+ const E = "https://app.backpackontology.com", x = `${E}/.well-known/oauth-authorization-server`, _ = new Uint8Array([66, 80, 65, 75]), S = 1;
2
+ let m = null;
3
+ function I(e) {
4
+ e.registerTaskbarIcon({
5
+ label: "Share",
6
+ iconText: "↗",
7
+ position: "bottom-right",
8
+ onClick: () => N(e)
9
+ });
13
10
  }
14
- function toggleSharePanel(viewer) {
15
- if (panel && panel.isVisible()) {
16
- panel.setVisible(false);
17
- return;
11
+ function N(e) {
12
+ if (m && m.isVisible()) {
13
+ m.setVisible(!1);
14
+ return;
15
+ }
16
+ const t = e.getGraphName();
17
+ if (!t) return;
18
+ const n = document.createElement("div");
19
+ n.className = "share-panel-body", m ? (m.element.replaceChildren(), m.element.appendChild(n), m.setTitle(`Share "${t}"`), m.setVisible(!0), m.bringToFront()) : (n.textContent = "Loading...", m = e.mountPanel(n, {
20
+ title: `Share "${t}"`,
21
+ defaultPosition: { left: window.innerWidth - 420, top: 80 },
22
+ showFullscreenButton: !1,
23
+ onClose: () => {
24
+ m = null;
18
25
  }
19
- const graphName = viewer.getGraphName();
20
- if (!graphName)
21
- return;
22
- const body = document.createElement("div");
23
- body.className = "share-panel-body";
24
- if (panel) {
25
- panel.element.replaceChildren();
26
- panel.element.appendChild(body);
27
- panel.setTitle(`Share "${graphName}"`);
28
- panel.setVisible(true);
29
- panel.bringToFront();
30
- }
31
- else {
32
- body.textContent = "Loading...";
33
- panel = viewer.mountPanel(body, {
34
- title: `Share "${graphName}"`,
35
- defaultPosition: { left: window.innerWidth - 420, top: 80 },
36
- showFullscreenButton: false,
37
- onClose: () => { panel = null; },
38
- });
39
- }
40
- renderShareForm(viewer, body);
26
+ })), w(e, n);
41
27
  }
42
- async function renderShareForm(viewer, container) {
43
- const token = await viewer.settings.get("relay_token");
44
- container.replaceChildren();
45
- if (!token) {
46
- renderUpsell(viewer, container);
47
- return;
48
- }
49
- renderForm(viewer, container, token);
28
+ async function w(e, t) {
29
+ const n = await e.settings.get("relay_token");
30
+ t.replaceChildren(), n ? v(e, t, n) : A(e, t);
50
31
  }
51
- function renderUpsell(viewer, container) {
52
- const wrapper = document.createElement("div");
53
- wrapper.className = "share-upsell";
54
- const heading = document.createElement("h4");
55
- heading.textContent = "Share this graph with anyone";
56
- wrapper.appendChild(heading);
57
- const desc = document.createElement("p");
58
- desc.textContent =
59
- "Encrypt your graph and get a shareable link. Recipients open it in their browser — no install needed. Your data stays encrypted on our servers.";
60
- wrapper.appendChild(desc);
61
- const cta = document.createElement("button");
62
- cta.className = "share-cta-btn";
63
- cta.textContent = "Sign in to share";
64
- cta.addEventListener("click", () => startOAuthFlow(viewer, container));
65
- wrapper.appendChild(cta);
66
- const tokenLink = document.createElement("button");
67
- tokenLink.className = "share-token-link";
68
- tokenLink.textContent = "Or paste an API token";
69
- tokenLink.addEventListener("click", () => renderTokenInput(viewer, container));
70
- wrapper.appendChild(tokenLink);
71
- const trust = document.createElement("p");
72
- trust.className = "share-trust";
73
- trust.textContent =
74
- "Free account. Your graph is encrypted before upload — we can't read it.";
75
- wrapper.appendChild(trust);
76
- container.replaceChildren(wrapper);
32
+ function A(e, t) {
33
+ const n = document.createElement("div");
34
+ n.className = "share-upsell", n.innerHTML = `
35
+ <h4>Share this graph with anyone</h4>
36
+ <p>Encrypt your graph and get a shareable link. Recipients open it in their browser — no install needed. Your data stays encrypted on our servers.</p>
37
+ `;
38
+ const a = document.createElement("button");
39
+ a.className = "share-cta-btn", a.textContent = "Sign in to share", a.addEventListener("click", () => T(e, t)), n.appendChild(a);
40
+ const o = document.createElement("button");
41
+ o.className = "share-token-link", o.textContent = "Or paste an API token", o.addEventListener("click", () => P(e, t)), n.appendChild(o);
42
+ const i = document.createElement("p");
43
+ i.className = "share-trust", i.textContent = "Free account. Your graph is encrypted before upload — we can't read it.", n.appendChild(i), t.replaceChildren(n);
77
44
  }
78
- function renderTokenInput(viewer, container) {
79
- const wrapper = document.createElement("div");
80
- wrapper.className = "share-token-input";
81
- const label = document.createElement("p");
82
- label.textContent = "Paste your API token from Backpack App settings:";
83
- wrapper.appendChild(label);
84
- const input = document.createElement("input");
85
- input.type = "password";
86
- input.placeholder = "Token";
87
- input.className = "share-input";
88
- wrapper.appendChild(input);
89
- const row = document.createElement("div");
90
- row.className = "share-btn-row";
91
- const saveBtn = document.createElement("button");
92
- saveBtn.className = "share-btn-primary";
93
- saveBtn.textContent = "Save";
94
- saveBtn.addEventListener("click", async () => {
95
- const val = input.value.trim();
96
- if (!val)
97
- return;
98
- await viewer.settings.set("relay_token", val);
99
- renderShareForm(viewer, container);
100
- });
101
- row.appendChild(saveBtn);
102
- const backBtn = document.createElement("button");
103
- backBtn.className = "share-btn-secondary";
104
- backBtn.textContent = "Back";
105
- backBtn.addEventListener("click", () => renderShareForm(viewer, container));
106
- row.appendChild(backBtn);
107
- wrapper.appendChild(row);
108
- container.replaceChildren(wrapper);
45
+ function P(e, t) {
46
+ const n = document.createElement("div");
47
+ n.className = "share-token-input";
48
+ const a = document.createElement("p");
49
+ a.textContent = "Paste your API token from Backpack App settings:", n.appendChild(a);
50
+ const o = document.createElement("input");
51
+ o.type = "password", o.placeholder = "Token", o.className = "share-input", n.appendChild(o);
52
+ const i = document.createElement("div");
53
+ i.className = "share-btn-row";
54
+ const d = document.createElement("button");
55
+ d.className = "share-btn-primary", d.textContent = "Save", d.addEventListener("click", async () => {
56
+ const c = o.value.trim();
57
+ c && (await e.settings.set("relay_token", c), w(e, t));
58
+ }), i.appendChild(d);
59
+ const l = document.createElement("button");
60
+ l.className = "share-btn-secondary", l.textContent = "Back", l.addEventListener("click", () => w(e, t)), i.appendChild(l), n.appendChild(i), t.replaceChildren(n);
109
61
  }
110
- async function startOAuthFlow(viewer, container) {
111
- try {
112
- // Fetch OAuth metadata
113
- const metaRes = await viewer.fetch(OAUTH_METADATA_URL);
114
- const meta = (await metaRes.json());
115
- // Register as a dynamic client
116
- const regRes = await viewer.fetch(meta.registration_endpoint, {
117
- method: "POST",
118
- });
119
- const client = (await regRes.json());
120
- // Generate PKCE challenge
121
- const codeVerifier = generateCodeVerifier();
122
- const codeChallenge = await generateCodeChallenge(codeVerifier);
123
- // Build authorization URL
124
- const redirectUri = window.location.origin + "/oauth/callback";
125
- const state = crypto.randomUUID();
126
- const authUrl = new URL(meta.authorization_endpoint);
127
- // authorization_endpoint may already have query params (e.g., scope=...)
128
- authUrl.searchParams.set("client_id", client.client_id);
129
- authUrl.searchParams.set("redirect_uri", redirectUri);
130
- authUrl.searchParams.set("response_type", "code");
131
- authUrl.searchParams.set("code_challenge", codeChallenge);
132
- authUrl.searchParams.set("code_challenge_method", "S256");
133
- authUrl.searchParams.set("state", state);
134
- if (!authUrl.searchParams.has("scope")) {
135
- authUrl.searchParams.set("scope", "openid email profile offline_access");
136
- }
137
- // Store state for the callback
138
- sessionStorage.setItem("share_oauth_state", state);
139
- sessionStorage.setItem("share_oauth_verifier", codeVerifier);
140
- sessionStorage.setItem("share_oauth_token_endpoint", meta.token_endpoint);
141
- sessionStorage.setItem("share_oauth_client_id", client.client_id);
142
- sessionStorage.setItem("share_oauth_redirect_uri", redirectUri);
143
- // Open popup
144
- const popup = window.open(authUrl.toString(), "backpack-share-auth", "width=500,height=700");
145
- // Listen for the callback
146
- const handler = async (event) => {
147
- if (event.data?.type !== "backpack-oauth-callback")
148
- return;
149
- window.removeEventListener("message", handler);
150
- const { code, returnedState } = event.data;
151
- if (returnedState !== state)
152
- return;
153
- // Exchange code for token
154
- const tokenRes = await viewer.fetch(meta.token_endpoint, {
155
- method: "POST",
156
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
157
- body: new URLSearchParams({
158
- grant_type: "authorization_code",
159
- code,
160
- redirect_uri: redirectUri,
161
- client_id: client.client_id,
162
- code_verifier: codeVerifier,
163
- }).toString(),
164
- });
165
- const tokenData = (await tokenRes.json());
166
- await viewer.settings.set("relay_token", tokenData.access_token);
167
- renderShareForm(viewer, container);
168
- };
169
- window.addEventListener("message", handler);
170
- // Fallback: if popup is blocked, show a link
171
- if (!popup || popup.closed) {
172
- const msg = document.createElement("p");
173
- msg.className = "share-error";
174
- msg.textContent = "Popup blocked. ";
175
- const link = document.createElement("a");
176
- link.href = authUrl.toString();
177
- link.target = "_blank";
178
- link.textContent = "Click here to sign in";
179
- msg.appendChild(link);
180
- container.appendChild(msg);
181
- }
62
+ async function T(e, t) {
63
+ try {
64
+ const a = await (await fetch(x)).json(), i = await (await fetch(a.registration_endpoint, { method: "POST" })).json(), d = B(), l = await $(d), c = window.location.origin + "/oauth/callback", r = crypto.randomUUID(), s = new URL(a.authorization_endpoint);
65
+ s.searchParams.set("client_id", i.client_id), s.searchParams.set("redirect_uri", c), s.searchParams.set("response_type", "code"), s.searchParams.set("code_challenge", l), s.searchParams.set("code_challenge_method", "S256"), s.searchParams.set("state", r), s.searchParams.has("scope") || s.searchParams.set("scope", "openid email profile offline_access"), sessionStorage.setItem("share_oauth_state", r);
66
+ const h = window.open(s.toString(), "backpack-share-auth", "width=500,height=700"), g = async (p) => {
67
+ var f;
68
+ if (((f = p.data) == null ? void 0 : f.type) !== "backpack-oauth-callback") return;
69
+ window.removeEventListener("message", g);
70
+ const { code: u, returnedState: k } = p.data;
71
+ if (k !== r) return;
72
+ const C = await (await fetch(a.token_endpoint, {
73
+ method: "POST",
74
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
75
+ body: new URLSearchParams({ grant_type: "authorization_code", code: u, redirect_uri: c, client_id: i.client_id, code_verifier: d }).toString()
76
+ })).json();
77
+ await e.settings.set("relay_token", C.access_token), w(e, t);
78
+ };
79
+ if (window.addEventListener("message", g), !h || h.closed) {
80
+ const p = document.createElement("p");
81
+ p.className = "share-error", p.textContent = "Popup blocked. ";
82
+ const u = document.createElement("a");
83
+ u.href = s.toString(), u.target = "_blank", u.textContent = "Click here to sign in", p.appendChild(u), t.appendChild(p);
182
84
  }
183
- catch (err) {
184
- const msg = document.createElement("p");
185
- msg.className = "share-error";
186
- msg.textContent = `Auth failed: ${err.message}`;
187
- container.appendChild(msg);
85
+ } catch (n) {
86
+ const a = document.createElement("p");
87
+ a.className = "share-error", a.textContent = `Auth failed: ${n.message}`, t.appendChild(a);
88
+ }
89
+ }
90
+ function v(e, t, n) {
91
+ const a = document.createElement("div");
92
+ a.className = "share-form";
93
+ const o = document.createElement("label");
94
+ o.className = "share-toggle-row";
95
+ const i = document.createElement("input");
96
+ i.type = "checkbox", i.checked = !0, o.appendChild(i);
97
+ const d = document.createElement("span");
98
+ d.textContent = "Encrypt (recommended)", o.appendChild(d), a.appendChild(o);
99
+ const l = document.createElement("div");
100
+ l.className = "share-pass-row";
101
+ const c = document.createElement("input");
102
+ c.type = "password", c.placeholder = "Passphrase (optional)", c.className = "share-input", l.appendChild(c), a.appendChild(l);
103
+ const r = document.createElement("button");
104
+ r.className = "share-btn-primary", r.textContent = "Share", r.addEventListener("click", async () => {
105
+ r.disabled = !0, r.textContent = "Encrypting...";
106
+ try {
107
+ await L(e, t, n, i.checked, c.value.trim());
108
+ } catch (g) {
109
+ r.disabled = !1, r.textContent = "Share";
110
+ const p = document.createElement("p");
111
+ p.className = "share-error", p.textContent = g.message, a.appendChild(p);
188
112
  }
113
+ }), a.appendChild(r);
114
+ const s = document.createElement("p");
115
+ s.className = "share-note", s.textContent = "Recipients open the link in their browser. No install needed.", a.appendChild(s);
116
+ const h = document.createElement("button");
117
+ h.className = "share-token-link", h.textContent = "Sign out", h.addEventListener("click", async () => {
118
+ await e.settings.remove("relay_token"), w(e, t);
119
+ }), a.appendChild(h), t.replaceChildren(a);
189
120
  }
190
- function renderForm(viewer, container, token) {
191
- const wrapper = document.createElement("div");
192
- wrapper.className = "share-form";
193
- // Encrypt toggle
194
- const encryptRow = document.createElement("label");
195
- encryptRow.className = "share-toggle-row";
196
- const encryptCheckbox = document.createElement("input");
197
- encryptCheckbox.type = "checkbox";
198
- encryptCheckbox.checked = true;
199
- encryptRow.appendChild(encryptCheckbox);
200
- const encryptLabel = document.createElement("span");
201
- encryptLabel.textContent = "Encrypt (recommended)";
202
- encryptRow.appendChild(encryptLabel);
203
- wrapper.appendChild(encryptRow);
204
- // Passphrase
205
- const passRow = document.createElement("div");
206
- passRow.className = "share-pass-row";
207
- const passInput = document.createElement("input");
208
- passInput.type = "password";
209
- passInput.placeholder = "Passphrase (optional)";
210
- passInput.className = "share-input";
211
- passRow.appendChild(passInput);
212
- wrapper.appendChild(passRow);
213
- // Share button
214
- const shareBtn = document.createElement("button");
215
- shareBtn.className = "share-btn-primary";
216
- shareBtn.textContent = "Share";
217
- shareBtn.addEventListener("click", async () => {
218
- shareBtn.disabled = true;
219
- shareBtn.textContent = "Encrypting...";
220
- try {
221
- await doShare(viewer, container, token, encryptCheckbox.checked, passInput.value.trim());
222
- }
223
- catch (err) {
224
- shareBtn.disabled = false;
225
- shareBtn.textContent = "Share";
226
- const errMsg = document.createElement("p");
227
- errMsg.className = "share-error";
228
- errMsg.textContent = err.message;
229
- wrapper.appendChild(errMsg);
230
- }
231
- });
232
- wrapper.appendChild(shareBtn);
233
- const note = document.createElement("p");
234
- note.className = "share-note";
235
- note.textContent = "Recipients open the link in their browser. No install needed.";
236
- wrapper.appendChild(note);
237
- // Logout link
238
- const logoutBtn = document.createElement("button");
239
- logoutBtn.className = "share-token-link";
240
- logoutBtn.textContent = "Sign out";
241
- logoutBtn.addEventListener("click", async () => {
242
- await viewer.settings.remove("relay_token");
243
- renderShareForm(viewer, container);
244
- });
245
- wrapper.appendChild(logoutBtn);
246
- container.replaceChildren(wrapper);
121
+ async function L(e, t, n, a, o) {
122
+ const i = e.getGraph(), d = e.getGraphName();
123
+ if (!i || !d) throw new Error("No graph loaded");
124
+ const l = new TextEncoder().encode(JSON.stringify(i));
125
+ let c, r, s = "";
126
+ if (a) {
127
+ const y = await import("../index-B-LFGJqd.js"), C = await y.generateX25519Identity(), f = await y.identityToRecipient(C), b = new y.Encrypter();
128
+ b.addRecipient(f), c = await b.encrypt(l), r = "age-v1", s = btoa(C).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
129
+ } else
130
+ c = l, r = "plaintext";
131
+ const h = await U(d, c, r), g = {
132
+ "Content-Type": "application/octet-stream",
133
+ Authorization: `Bearer ${n}`
134
+ };
135
+ o && (g["X-Passphrase"] = o);
136
+ const p = await fetch(`${E}/v1/share`, {
137
+ method: "POST",
138
+ headers: g,
139
+ body: h
140
+ });
141
+ if (!p.ok) {
142
+ const y = await p.text().catch(() => "");
143
+ throw p.status === 401 ? (await e.settings.remove("relay_token"), w(e, t), new Error("Session expired. Please sign in again.")) : new Error(`Upload failed: ${y}`);
144
+ }
145
+ const u = await p.json(), k = s ? `${u.url}#k=${s}` : u.url;
146
+ R(t, k, a, u.expires_at);
247
147
  }
248
- async function doShare(viewer, container, token, encrypted, passphrase) {
249
- const graph = viewer.getGraph();
250
- const graphName = viewer.getGraphName();
251
- if (!graph || !graphName)
252
- throw new Error("No graph loaded");
253
- const plaintext = new TextEncoder().encode(JSON.stringify(graph));
254
- let payload;
255
- let format;
256
- let fragmentKey = "";
257
- if (encrypted) {
258
- const { generateKeyPair, encrypt, encodeKeyForFragment } = await import("backpack-ontology");
259
- const keyPair = await generateKeyPair();
260
- payload = await encrypt(plaintext, keyPair.publicKey);
261
- format = "age-v1";
262
- fragmentKey = encodeKeyForFragment(keyPair.secretKey);
263
- }
264
- else {
265
- payload = plaintext;
266
- format = "plaintext";
267
- }
268
- // Build BPAK envelope
269
- const { createEnvelope } = await import("backpack-ontology");
270
- const envelope = await createEnvelope(graphName, payload, format, 1);
271
- // Upload to relay
272
- const headers = {
273
- "Content-Type": "application/octet-stream",
274
- Authorization: `Bearer ${token}`,
275
- };
276
- if (passphrase) {
277
- headers["X-Passphrase"] = passphrase;
278
- }
279
- const res = await viewer.fetch(`${RELAY_URL}/v1/share`, {
280
- method: "POST",
281
- headers,
282
- body: envelope,
283
- });
284
- if (!res.ok) {
285
- const body = await res.text().catch(() => "");
286
- if (res.status === 401) {
287
- // Token expired — clear and show upsell
288
- await viewer.settings.remove("relay_token");
289
- renderShareForm(viewer, container);
290
- throw new Error("Session expired. Please sign in again.");
291
- }
292
- throw new Error(`Upload failed: ${body}`);
293
- }
294
- const result = (await res.json());
295
- const shareLink = fragmentKey
296
- ? `${result.url}#k=${fragmentKey}`
297
- : result.url;
298
- renderSuccess(container, shareLink, encrypted, result.expires_at);
148
+ async function U(e, t, n) {
149
+ const a = new ArrayBuffer(t.byteLength);
150
+ new Uint8Array(a).set(t);
151
+ const o = await crypto.subtle.digest("SHA-256", a), i = "sha256:" + Array.from(new Uint8Array(o)).map((h) => h.toString(16).padStart(2, "0")).join(""), d = JSON.stringify({
152
+ format: n,
153
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
154
+ backpack_name: e,
155
+ graph_count: 1,
156
+ checksum: i
157
+ }), l = new TextEncoder().encode(d), c = new ArrayBuffer(4);
158
+ new DataView(c).setUint32(0, l.length, !1);
159
+ const r = new Uint8Array(9 + l.length + t.length);
160
+ let s = 0;
161
+ return r.set(_, s), s += 4, r[s] = S, s += 1, r.set(new Uint8Array(c), s), s += 4, r.set(l, s), s += l.length, r.set(t, s), r;
299
162
  }
300
- function renderSuccess(container, shareLink, encrypted, expiresAt) {
301
- const wrapper = document.createElement("div");
302
- wrapper.className = "share-success";
303
- const heading = document.createElement("h4");
304
- heading.textContent = "Shared!";
305
- wrapper.appendChild(heading);
306
- const linkRow = document.createElement("div");
307
- linkRow.className = "share-link-row";
308
- const linkInput = document.createElement("input");
309
- linkInput.type = "text";
310
- linkInput.readOnly = true;
311
- linkInput.value = shareLink;
312
- linkInput.className = "share-link-input";
313
- linkRow.appendChild(linkInput);
314
- const copyBtn = document.createElement("button");
315
- copyBtn.className = "share-btn-primary";
316
- copyBtn.textContent = "Copy";
317
- copyBtn.addEventListener("click", () => {
318
- navigator.clipboard.writeText(shareLink);
319
- copyBtn.textContent = "Copied!";
320
- setTimeout(() => { copyBtn.textContent = "Copy"; }, 2000);
321
- });
322
- linkRow.appendChild(copyBtn);
323
- wrapper.appendChild(linkRow);
324
- if (encrypted) {
325
- const note = document.createElement("p");
326
- note.className = "share-note";
327
- note.textContent =
328
- "The decryption key is in the link. Anyone with the full link can view this graph. The server cannot read your data.";
329
- wrapper.appendChild(note);
330
- }
331
- if (expiresAt) {
332
- const exp = document.createElement("p");
333
- exp.className = "share-note";
334
- exp.textContent = `Expires: ${new Date(expiresAt).toLocaleDateString()}`;
335
- wrapper.appendChild(exp);
336
- }
337
- container.replaceChildren(wrapper);
163
+ function R(e, t, n, a) {
164
+ const o = document.createElement("div");
165
+ o.className = "share-success";
166
+ const i = document.createElement("h4");
167
+ i.textContent = "Shared!", o.appendChild(i);
168
+ const d = document.createElement("div");
169
+ d.className = "share-link-row";
170
+ const l = document.createElement("input");
171
+ l.type = "text", l.readOnly = !0, l.value = t, l.className = "share-link-input", d.appendChild(l);
172
+ const c = document.createElement("button");
173
+ if (c.className = "share-btn-primary", c.textContent = "Copy", c.addEventListener("click", () => {
174
+ navigator.clipboard.writeText(t), c.textContent = "Copied!", setTimeout(() => {
175
+ c.textContent = "Copy";
176
+ }, 2e3);
177
+ }), d.appendChild(c), o.appendChild(d), n) {
178
+ const r = document.createElement("p");
179
+ r.className = "share-note", r.textContent = "The decryption key is in the link. Anyone with the full link can view. The server cannot read your data.", o.appendChild(r);
180
+ }
181
+ if (a) {
182
+ const r = document.createElement("p");
183
+ r.className = "share-note", r.textContent = `Expires: ${new Date(a).toLocaleDateString()}`, o.appendChild(r);
184
+ }
185
+ e.replaceChildren(o);
338
186
  }
339
- // --- PKCE helpers ---
340
- function generateCodeVerifier() {
341
- const arr = new Uint8Array(32);
342
- crypto.getRandomValues(arr);
343
- return btoa(String.fromCharCode(...arr))
344
- .replace(/\+/g, "-")
345
- .replace(/\//g, "_")
346
- .replace(/=+$/, "");
187
+ function B() {
188
+ const e = new Uint8Array(32);
189
+ return crypto.getRandomValues(e), btoa(String.fromCharCode(...e)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
347
190
  }
348
- async function generateCodeChallenge(verifier) {
349
- const data = new TextEncoder().encode(verifier);
350
- const buf = new ArrayBuffer(data.byteLength);
351
- new Uint8Array(buf).set(data);
352
- const digest = await crypto.subtle.digest("SHA-256", buf);
353
- return btoa(String.fromCharCode(...new Uint8Array(digest)))
354
- .replace(/\+/g, "-")
355
- .replace(/\//g, "_")
356
- .replace(/=+$/, "");
191
+ async function $(e) {
192
+ const t = new TextEncoder().encode(e), n = new ArrayBuffer(t.byteLength);
193
+ new Uint8Array(n).set(t);
194
+ const a = await crypto.subtle.digest("SHA-256", n);
195
+ return btoa(String.fromCharCode(...new Uint8Array(a))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
357
196
  }
197
+ export {
198
+ I as activate
199
+ };
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from "vite";
2
+ import { resolve } from "path";
3
+ export default defineConfig({
4
+ build: {
5
+ lib: {
6
+ entry: resolve(__dirname, "src/index.ts"),
7
+ formats: ["es"],
8
+ fileName: () => "src/index.js",
9
+ },
10
+ outDir: resolve(__dirname, "../../dist/extensions/share"),
11
+ emptyOutDir: false,
12
+ rollupOptions: {
13
+ // Don't externalize anything — bundle all deps into one file
14
+ external: [],
15
+ },
16
+ },
17
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backpack-viewer",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Web-based graph visualizer for backpack-ontology — Canvas 2D, force-directed layout, live reload",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Noah Irzinger",
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "scripts": {
16
16
  "dev": "vite",
17
- "build": "rm -rf dist && tsc && tsc -p tsconfig.extensions.json && cp src/style.css dist/ && node scripts/build-extensions.js && vite build",
17
+ "build": "rm -rf dist && tsc && tsc -p tsconfig.extensions.json && cp src/style.css dist/ && node scripts/build-extensions.js && node scripts/bundle-extensions.js && vite build",
18
18
  "preview": "vite preview",
19
19
  "serve": "node bin/serve.js",
20
20
  "prepare": "npm run build",